Docker Volume 알아채기
Docker Volume을 create, backup, migrate, restore하는 일련의 예제를 작성해 봄으로써 Docker를 활용할 때 데이터를 영구적으로 저장/유지하는 방법에 대해 알아본다.
Docker가 무엇인지 먼저 간략하게 알아보기
Docker는 어플리케이션을 개발(developing), 배포(shipping), 실행(running)하는 하나의 오픈 플랫폼(Open platform)이라 할 수 있다. 또한 어플리케이션을 운영 인프라로부터 개념적으로 분리시켜 줌으로써 보다 빠르게 소프트웨어를 배포할 수 있도록 한다. Docker를 활용하면 응용 프로그램을 관리하는 것과 같은 방식으로 앱의 인프라(infrastructure)를 관리할 수 있어 개발자에게 전체 개발 프로세스를 상당히 간소화 시켜준다. 코드를 배포, 테스트, 배포를 빠르게 해주는 Docker 방법론을 활용하면 개발자는 코드 작성과 운영환경에서 실행 사이에 발생하는 일련의 과정에서 일정지연을 줄일 수 있다. 이는 전체 개발 프로세스에서 소요되는 비용 뿐만 아니라 시간도 효율적으로 활용할 수 있도록 해 준다.
요약하자면, Docker는 컨테이너(Container)를 이용하여 응용프로그램의 제작, 배포, 실행을 쉽게 할 수 있도록 만든 하나의 도구이다. 컨테이너는 어플리케이션을 외부로 배포(ship)하기 전에 필요로 하는 컴폰넌트들 즉, 라이브러리 뿐만 아니라 그 외 dependency들을 응용프로그램과 함께 하나의 패키지로 포장할 수 있도록 한다.
Docker Volume이란?
Volume은 Docker 컨테이너에 의해 생성된 데이터를 지속 가능하게 유지되도록 광범위하게 활용되고 선호되는 매커니즘이다. Docker volume은 기본적으로 호스트 시스템상의 로컬 폴더 하나와 Docker 컨테이너 내부에 있는 특정 폴더 사이에 링크(link)를 생성한다. 이와 같이 연결된 폴더들 사이에는 데이터 복제가 이루어진다 따라서, 특정 시점에 컨테이너가 재시작되거나 삭제되더라도 컨테이너가 생성한 데이터에는 지속적으로 접근할 수 있도록 해준다.
Volume Driver는 원격 호스트(remote host) 또는 cloud provider에 volume을 저장할 수 있도록 한다. 이를 활용하면 volume의 내용을 암호화가거나 그 외 다른 기능들을 추가할 수 있다. Volume Driver를 활용하여 신규 volume이 컨테이너에 의해 그 내용이 미리 채워지도록 만들 수도 있다.
또한, Docker volume은 컨테이너들 간에 데이터 공유를 가능하도록 해준다.
호스트 파일시스템과 컨테이너의 가상(virtual) 파일시스템간의 연결(link)
자, 그럼 호스트 시스템에 폴더/파일을 생성하고 이를 docker 컨테이너 내의 폴더와 연결시켜보자. 그리고 어떻게 동작하는지 시나리오를 이용하여 테스트 해 보자.
- 먼저 컨테이너 내부 폴더의 데이터가 유지되로록 로컬 시스템 상에 폴더를 생성한다. 데모를 위한 예제에서는 로컬 시스템 폴더를
$(pwd)/docker/vol
로 한다. 이제 아래의 코드를 실행해 보자.
docker run --name volnginx -v <absolute_path>/docker/vol/:/usr/share/nginx/html -p 8080:80 -d nginx
–– name: 실행할 컨테이너의 이름 지정
-v: volume을 마운트하며 호스트의 /docker/vol/ 폴더를 컨테이너 내부의 /usr/share/nginx/html 폴더로 링크한다.
-p: 컨테이너의 포트(80)를 호스트 포트(8080)로 퍼블리시(publish) 한다.
-d: docker 컨테이너를 백그라운드에서 detached mode로 실행한다.
이렇게 하면 두 파일 시스템을 연결된다. 컨테이너가 nginx 컨테이너이므로 static 페이지 2개를 호스트 파일 시스템에 생성해 보자.
docker/vol/index.html(호스트 파일시스템)
Hello this is index.html
docker/vol/hello.html (호스트 파일시스템):
Hello this is hello.html
컨테이너로 접속한 후 공유된 파일/폴더의 내용을 확인해 보자. 아래의 스샷과 같이 2개의 파일이 생성되 있음을 확인할 수 있다.
docker exec -it volnginx /bin/bash
cd /usr/share/nginx/html
ls -al

이제 브라우저를 이용하여 localhost:8080 와 localhost:8080/hello.html을 접속해 보자. 아래 스샷과 같이 호스트 파일시스템에 생성한 파일의 내용을 컨테이너에서 동작하는 nginx로 확인할 수 있다.


호스트 시스템에서 hello.html파일을 삭제하고 localhost:8080/hello.html로 다시 접속하면 페이지가 없다고 나올 것이다.
이제 docker 컨테이너 내부의 공유된 폴더에서 파일 하나를 생성해보자. 그러면 동일한 파일이 호스트 파일 시스템의 공유 폴더에 생성/유지됨을 확인할 수 있다.


컨터이너에게 공유된 폴더의 권한을 read-only 권한(permission)으로 주려면 아래의 명령과 같이 한다.
docker run --name volnginx -v <absolute_path>/docker/vol/:/usr/share/nginx/html:ro -p 8080:80 -d nginx
지금까지는 MountType: bind에 해당하는 예제로 호스트의 파일시스템을 docker 컨테이너로 마운트 한다. 다음과 같은 명령을 통해 그 내용을 확인할 수 있다.
docker inspect volnginx
출력되는 내용 중 Mounts 객체 항목에서 volume 설정을 확인할 수 있다.
...
"Mounts": [
{
"Type": "bind",
"Source": "<directory>/docker/vol/",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
...
Named Docker Volume 생성
디렉터리를 생성하고 싶지 않거나 이미 사용하던 디렉터리가 있다면 다음과 같이 간단하게 volume을 생성하여 컨테이너에 attach하면 된다.
docker volume create hello
docker run --name mynginx1 -v hello:/<directory inside the container>:ro -p 8080:80 -d nginx
여기서 hello라는 volume 하나를 만들고 nginx 이미지 인스턴스를 실행할 때 attach 시켰다.
컨테이너 이미지의 디렉터리는 호스트 파일시스템에서 아래 위치에 생성되지만 여기에 접근하는 것을 추천하지 않는다.
/var/lib/docker/volumes/<vol_name>/_data
참고: 만약 Docker Desktop에서 작업을 한다면 이 디렉터리에는 접근이 불가능하다. 왜냐하면 Docker Desktop은 VM 내부에서 동작하고 있기 때문이다. 호스트 시스템에서 Docker의 volume 디렉터리에 접근하는 것이 추천하지 않는다고 했다. 왜냐하면 Docker가 volume storage를 보이지 않는 디렉터리(hidden directory, /var/lib/docker/volumes
on Linux)로 관리하며 만약 직접 접근을 허용할 경우 데이터에 손상이 발생할 수 있기 때문이다.(sudo 옵션을 활용하여 접근) 따라서, Docker volume에 접근하려면 컨테이너를 실행할 때 -v 옵션으로 volume을 마운트하고 컨테이너 내부에 접속한 후 접근하는 것을 추천한다.
이와 같은 예제는 MountType: volume 타입의 예제이다. 아래와 같이 명령을 통해 그 내용을 확인할 수 있다.
docker inspect mynginx1
출력되는 내용 중 Mounts 객체 항목에서 확인할 수 있다.
...
"Mounts": [
{
"Type": "volume",
"Name": "hello",
"Source": "/var/lib/docker/volumes/hello/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "ro",
"RW": false,
"Propagation": ""
}
]
...
Named Docker Volume의 기본 위치 변경하기
Docker Volume을 docker volume create hello 와 같이 기본적인 방법으로 생성하면 hello라는 volume에 link된 디렉터리는 Default로 /var/lib/docker/volumes/hello/_data 로 지정된다. 앞에서 설명했듯이 이 디렉터리로는 접근을 허용하지 않는다.
Volume을 생성할 때 Default로 생성되는 디렉터리와 다른 폴더로 직접 link하고 싶다면 아래와 같이 옵션(-o)를 주어 생성한다.
docker volume create --driver local -o o=bind -o type=none -o device="/home/skanto/docker/vol2" skantovol
생성된 Named Volume의 속성을 확인하려면 docker volume inspect skantovol 명령어를 통해 아래와 같이 확인할 수 있다.
docker volume inspect skantovol
[
{
"CreatedAt": "2024-11-30T09:43:34Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/skantovol/_data",
"Name": "skantovol",
"Options": {
"device": "/home/skanto/docker/vol2",
"o": "bind",
"type": "none"
},
"Scope": "local"
}
]
이렇게 생성한 Named Volume을 이용하여 앞에서의 예제와 같은 방법으로 nginx 컨테이너 인스턴스를 생성한다.
docker run --name mynginx3 -v skantovol:/usr/share/nginx/html -p 8090:80 -d nginx
이제 skantovol 로 link된 호스트 시스템의 디렉터리(/home/skanto/docker/vol2)로 이동하여 내용을 확인하면 아래와 같이 파일들이 생성되어 있음을 확인할 수 있다.

여기서 index.html파일의 내용을 수정하면(The contents are updated through the file on host file system!!!) 아래 스샷과 같이 컨테이너의 nginx 페이지에 바로 반영됨을 확인할 수 있다.

컨테이너들 간에 Volume 공유하기
하나의 volume을 여러 개의 컨테이너로 attach시키기만 하면 컨테이너들 간에 데이터를 비교적 쉽게공유할 수 있다.
docker run --name mynginx1 -v hello:/<directory inside the container> -p 8080:80 -d nginx
docker run --name mynginx2 -v hello:/<directory inside the container> -p 8081:80 -d nginx
실행된 각각의 컨테이너들은 동일한 volume을 사용한다.
특정 컨테이너에 여러 개의 volume이 attach되어 있을 경우 이 컨테이너의 모든 volume을 새로 생성한 컨테이너로 attach되도록 해야 한다면 아래의 코드와 같이 간단하게 이를 처리할 수 있다.
docker run --name mynginx3 --volumes-from mynginx1 -p 8082:80 -d nginx
이와 같은 방법으로 컨테이너들 간에 volume을 공유하게 되면 특정 container 내부에서 volume으로 지정된 디렉터리의 내용을 수정할 경우 수정된 내용이 다른 컨테이너들에도 자동 반영된다.
Docker Volume의 백업, 복구, 마이그레이션
백업(backup)
- 백업 디렉터리를 생성한다.
- 기존의 volume을 앞에서 생성한 백업 디렉터리와 함께 새로 만들 컨테이너로 공유되도록 설정한다.
- 기존 공유 volume의 내용을 압축하고 그 압축파일이 백업 컨테이너에 링크(link)된 디렉터리로 생성한다.
- 이렇게 하면 호스트 시스템에 압축파일이 생성되는 것과 동일한 효과가 발생한다.
- 마지막으로, 작업이 완료되면 새로 생성한 컨테이너를 제거한다.(–rm 옵션 활용)
mkdir ~/backup
docker run --rm --volumes-from mynginx1 -v ~/docker/backup:/backup ubuntu bash -c "cd /usr/share/nginx/html && tar cvf /backup/container.tar ."
~/backup 디렉터리에 생성될 파일은 container.tar 가 된다.

복구(restore)
그럼 백업받았던 내용을 새로운 Docker Volume으로 복구해 보자.
- 앞에서 사용했던 명령어와 비슷한 방법으로 진행한다. 여기서는 복구 확인을 위해 restore.html파일을 임의로 만들었다.
docker run --rm -v hello:/recover -v ~/docker/backup:/backup ubuntu bash -c "cd /recover && tar xvf /backup/container.tar"

- 정상적으로 복구되었는지 확인하려면 volume을 서로 공유하는 컨테이너들 중 하나에 접속해서 volume으로 link된 디렉터리(/usr/share/nginx/html)의 내용을 확인한다. 아래와 스샷처럼 컨테이너의 volume으로 link된 디렉터리에서 restore.html파일이 복구되었음을 확인할 수 있다.

마이그레이션(Migration)
백업을 위한 방법으로, 폴더에 생성된 압축파일을 scp, rsync 등과 같이 활용 가능한 파일전송 툴을 이용하여 다른 호스트로 전송하고 해당 호스트에서 압축파일을 복구하면 된다.
Docker Compose에서 Volume 사용하기
Docker Compose는 다중 컨테이너로 구성된 Docker 응용프로그램을 정의하고 실행하는데 활용하는 툴이다. Compose는 YAML파일을 이용하여 어플리케이션의 서비스들을 설정하고 이렇게 설정된 정보를 이용하여 명령어 하나로 모든 서비스들을 생성하고 실행할 수 있도록 한다.
다음과 같이 아주 기본적인 방법으로 compose.yaml 파일에 Volume을 정의할 수 있다.
version: '3.5'
services:
corporate:
image: corporate.skanto:latest ## base image
volumes:
- <host_path>:<conatiner_path> ## for host-mounted volumes
- <named_volume>:<container path> ## for named volumes
Docker Volume의 장점
- 데이터를 유지를 위한 훌륭한 방법을 제공한다.
- Volume관리는 Docker CLI 또는 Docker API로 할 수 있다.
- 백업, 복구, 마이그레이션이 용이다.
- 컨테이너들간의 데이터 공유를 쉽게 한다.
내용이 비교적 간단하지만 Docker를 활용하는 인프라 환경에서 Docker Volume을 설정하고 활용하는데 이 글이 도움이 되길 바래본다.
2024.11.30