본문 바로가기
관심 기술/container

도커 컨테이너에서도 데이터 유지하는 방법: Volume 사용법

by se-dong 2025. 4. 7.

Docker Volume 이란?

Docker volume 은 컨테이너 간에 데이터를 공유하거나, 컨테이너가 중지되고 삭제되더라도 데이터를 유지하기 위해 사용되는 도커에서 관리하는 데이터 스토리지이다.

 

Docker volume을 사용하면 컨테이너가 중지되더라도 유지할 수 있는 데이터를 안전하게 저장할 수 있으며, 컨테이너와 호스트 간 데이터 공유도 가능하다.

 

도커 볼륨을 이해하기 위해선 마운트라는 개념을 이해하자.

 

mount의 사전적인 정의는 디스크를 컴퓨터에서 사용할 수 있도록 만드는 것을 의미한다. 리눅스에서 mount는 물리적인 장치(외부 저장소, 하드디스크, usb 등)를 리눅스 파일 시스템의 특정 디렉터리를 연결하는 작업을 의미한다.

 

물리 장치를 호스트 머신의 특정한 디렉토리에 연결하여 호스트 머신의 특정 디렉터리에서 해당 물리장치를 읽을 수 있도록 하는 것이 마운트다. 컨테이너 관점에서 mount를 바라보자.

 

컨테이너는 호스트 머신의 격리된 리소스를 사용하는 프로세스이다. 컨테이너는 추상화 되어 있기 때문에 컨테이너 내부는 별개의 머신과 같다. 볼륨을 마운트 하는 것은 도커 컨테이너가 관리하는 데이터 스토리지를 컨테이너 환경의 특정 디렉터리에 연결하여, 컨테이너 내부에서 데이터 스토리지를 사용할 수 있도록 하는 것이다.

 

마운트에 대해 이해하고 도커 볼륨에 대해서 본격적으로 알아보자.


Docker Volume 의 세 종류

docker volume 은 크게 익명 볼륨, 네임드 볼륨, 바인드 마운트 세 종류로 나눌 수 있다.


익명 볼륨 (Anonymous Volume)

익명 볼륨은 도커가 관리하는 임시적인 볼륨이다.

컨테이너 구동 시 새로운 익명 볼륨이 생성되며, 컨테이너를 삭제하면 해당 볼륨도 함께 삭제되기 때문에 컨테이너와 동일한 라이프 사이클을 가진다.

익명 볼륨은 주로 단일 컨테이너에서 사용한다.

docker continaer run -v /path/in/container busybox

위 명령어에서 Docker는 자동으로 익명 볼륨을 생성하여 /path/in/container 경로를 익명 볼륨으로 사용한다.


네임드 볼륨 (Named Volume)

네임드 볼륨은 사용자가 이름을 지정하여 생성한 볼륨이다.

네임드 볼륨은 컨테이너가 삭제되더라도 볼륨이 삭제되지 않기 때문에 데이터가 유지된다. 따라서 다른 컨테이너에서도 동일한 볼륨을 참조할 수 있다.

이를 통해 컨테이너간 데이터를 공유할 수 있고 지속성 있는 데이터를 유지할 수 있다.

docker volume create my-volume
docker container run -v my-volume:/path/in/container busybox

위 명령어에서 my-volume이라는 이름의 볼륨이 생성되고, 컨테이너의 /path/in/container에 마운트된다.


바인드 마운트 (Bind Mount)

바인드 마운트는 호스트의 특정 디렉터리를 컨테이너에 마운트 한다.

호스트 경로를 명시적으로 지정해야 하며, 호스트와 컨테이너 간의 파일 시스템의 공유가 가능하다.

바인드 마운트는 주로 개발 환경에서 호스트 파일 시스템에 대한 접근이 필요한 경우 사용된다.

docker container run -v /host/path:/container/path busybox

위 명령어에서 호스트의 /host/path 디렉터리가 컨테이너의 /container/path에 마운트 된다.

 

바인드 마운트는 호스트의 파일 시스템을 컨테이너의 특정 경로에 마운트 시키는 것이기 때문에 호스트 파일 시스템에 해당 경로가 존재하지 않는 경우 도커 엔진은 해당 경로를 만들고 해당 경로를 마운트 시킨다. 호스트 파일 시스템에서 해당 경로를 삭제하지 않는 이상 데이터는 보존되며, 기존 파일 혹은 폴더가 존재하면 해당 파일 및 폴더를 사용할 수 있다.


바인드 마운트와 익명, 네임드 볼륨의 공통점과 차이점

볼륨의 한 종류인 바인드 마운트는 익명 혹은 네임드 볼륨과 유사한 점과 차이점이 존재한다.


공통점

  • 바인드 마운트, 익명 볼륨, 네임드 볼륨 모두 컨테이너와 호스트 간에 데이터 공유를 위해 특정 디렉터리를 마운트 한다.

차이점

  • 바인드 마운트
    • 사용자가 정의한 특정 디렉터리를 컨테이너에 마운트 한다.
    • 호스트 경로를 명시적으로 지정해야 한다.
    • 사용 예: docker container run -v /host/path:/container/path busybox
  • 익명, 네임드 볼륨
    • Docker 가 관리하는 특정 디렉터리를 마운트 한다.
    • Docker 가 관리하는 디렉터리이기 때문에 사용자는 해당 경로를 알 필요가 없다.
      • 사용자가 해당 경로를 알아야 하는 경우는 바이드 마운트를 사용하면 된다.
      • 경로를 알아야 하는데 익명이나 네임드 볼륨을 사용하는 것은 절절하지 않은 사용 사례이다.
    • 사용 예: docker container run -v my-volume:/container/path busybox

 

특징 바인드 마운트  익명, 네임드 볼륨
호스트 디렉토리 지정 필요 필요 없음
볼륨 경로 관리 주체 사용자 Docker
사용 용도 호스트 파일 시스템 접근, 개발 환경 컨테이너 간 데이터 공유, 백업 및 복원

 

여기서 컨테이너와 호스트 간에 데이터를 공유한다는 것은 어떤 의미인지 알아보자.


컨테이너와 호스트 간 데이터 공유

익명 볼륨이든 네임드 볼륨이든 그 경로에 대한 관리 주체는 docker이다.

docker container run -v named-volume:/etc/named -v /etc/anonymous busybox

 

네임드 볼륨과 익명 볼륨을 마운트 설정하여 컨테이너를 실행한다.

컨테이너를 실행하면 docker 엔진은 자동으로 named-volume을 생성하고 익명의 볼륨을 하나 생성한다.

생성된 볼륨들을 확인하기 위해선 다음과 같은 명령어를 사용한다.

 

docker volume ls

 

각각의 볼륨이 어떻게 정의되어 있는지 확인하기 위해 inspect 명령어로 확인해 보자.

docker volume inspect named-volume <anonymous-volume-name>

 

네임드 볼륨과 익명 볼륨 둘 다 바인드마운트와 같이 호스트의 특정 경로를 컨테이너에 마운트 한다.

다만 네임드 볼륨과 익명 볼륨은 특정 경로에 대한 선택을 도커 엔진이 하며, 바인드 마운트는 사용자가 하는 것이다.


볼륨을 사용해 보자

고로 작성된 간단한 웹 애플리케이션이 있다.

sample-web.tar
0.01MB

 

 

 

 

해당 파일을 다운로드하고 압축을 풀자.

tar -xvf sample-web.tar

 

해당 애플리케이션은 사용자에게 파일 이름을 받아서 해당 파일 이름을 특정 경로에서 찾은 후 해당 파일의 내용을 읽어서 사용자에게 보여주는 기능을 하는 /files? path=xxx 엔드포인트를 포함하고 있다.

해당 프로젝트 경로로 가서 docker container를 실행해 보자.

 

cd sample-web
docker image build --tag sample-web .

 

도커 파일을 통해서 sample-web을 실행 가능한 docker 이미지를 빌드했다.

이제 해당 도커 이미지를 바인드 마운트 설정 없이 컨테이너로 실행해 보자.

docker container run -p 8080:8080 -d --rm sample-web

 

http://localhost:8080/files? path=test.txt 해당 경로에 요청을 보내면 적절한 응답이 오지 않고 에러가 발생한다.

func fileHandler(w http.ResponseWriter, r *http.Request) {
	// Get the 'path' query parameter
	queryPath := r.URL.Query().Get("path")
	if *queryPath* == "" {
		http.Error(w, "Path query parameter is missing", http.StatusBadRequest)
		return
	}

	p := "/app/data"
	filePath := path.Join(p, queryPath)

	file, err := os.ReadFile(filePath)

	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to read file: %v", err), http.StatusInternalServerError)
		return
	}
	fmt.Fprintln(w, string(file))

}

 

/app/data 경로를 기반으로 file을 찾아오는데 해당 경로에 test.txt로 요청한 파일이 존재하지 않아서 발생하는 에러다.

바인드 마운트를 사용하면 이를 간단하게 해결할 수 있다.

실행 중인 컨테이너를 중지하고 이번엔 바인드 마운트 설정을 추가한 후 컨테이너를 실행해 보자.

 

docker container run -p 8080:8080 -d --rm -v ./data:/app/data:ro sample-web

 

바인드 마운트 설정 없이 실행한 컨테이너의 애플리케이션은 에러가 발생했지만 바인드 마운트를 설정한 컨테이너 애플리케이션에 동일한 경로로 요청을 보내면 파일의 내용을 잘 읽어온다.

이번엔 프로젝트 루트의 data 폴더에 원하는 내용의 파일을 추가해 보자.

echo "hello my name sehyeong" >> ./data/sehyeong.txt

 

해당 파일을 추가한 후 http://localhost:8080/files? path=sehyeong.txt로 요청을 보내면 다음과 같은 응답이 출력된다.

 


Docker CLI를 사용한 볼륨 관리

  • 볼륨 생성:
docker volume create my-volume  # 네임드 볼륨 생성
  • 볼륨 목록 보기:
docker volume ls  # 네임드 볼륨, 익명 볼륨을 볼 수 있다.
  • 볼륨 삭제:
docker volume rm my-volume
  • 볼륨을 사용한 컨테이너 실행:
# 익명 볼륨
docker container run -d --name my-container -v /data busybox
# 네임드 볼륨
docker container run -d --name my-container -v name:/data busybox
# 바인드 마운트
docker container run -d --name my-container -v ./path:/data busybox

Docker Compose를 사용한 볼륨 선언

앞서 수행한 바인드 마운트를 사용하여 웹 애플리케이션을 실행하는 docker command를 docker compose에 설정해 보자.

version: '3.9'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    volumes:
      - ./data:/app/data:ro

 

만약 네임드 볼륨을 사용하려면 volume 이름에 대해서도 선언해줘야 한다.

version: '3.9'
services:
  web:
    image: nginx
    volumes:
      - my-volume:/var/lib/nginx
  db:
    image: postgres
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  my-volume:
  db-data:

 

다음 명령어를 docker-compose.yaml 파일이 있는 경로에서 실행하자.

  • 애플리케이션 시작:
docker compose up -d  # detach mode 에서 compose 를 실행한다.
  • 애플리케이션 중지 및 볼륨 제거:
docker compose down -v  # compose 중지 시 docker volume 도 삭제한다