title: Learn Docker In A Month Of Lunches - 06. Using Docker Volumes For Persistent Storage
date: 2023-04-01
tags:
- Docker
Introduction
GDSC에서 기존에 진행하던 HTTP 스터디 종료 이후, Docker 스터디를 이어서 진행하게 되었다.
실습 내용을 위주로 작성한다. 서적에는 각 내용에 대한 더욱 자세한 설명이 포함되어 있으니 포스팅에 적지 않은 자세한 내용은 서적을 보거나 검색 등을 추천한다.
Purpose
Docker에서 영구적인 Storage 사용 방법에 대해 알아본다.
Data In Container Is Not Permanent
지금까지 사용한 Container의 경우, Container가 삭제되면 데이터가 전부 사라진다.
이때 Container가 삭제된다는 의미는 docker container ls -a
로 조회되는 Exited Status를 의미하는 것이 아니다.docker container rm
명령어를 통해 완전히 삭제된 Container를 의미하는 것이다.
각 Container는 독자적인 파일시스템을 가지며, 이 공간은 Container의 LifeCycle과 동일하다.
아래 예제를 통해 이를 쉽게 확인할 수 있다.
docker container run --name rn1 diamol/ch06-random-number
docker container run --name rn2 diamol/ch06-random-number
docker container cp rn1:/random/number.txt number1.txt
docker container cp rn2:/random/number.txt number2.txt
cat number1.txt number2.txt
본 Image는 난수를 생성하여 number.txt
파일에 작성한다.
Container는 종료되었으나 삭제되지 않았기에 각 Container의 데이터가 남아있다.
Container 실행을 위해 가져온 Image의 각 Layer는 Read Only로 되어 있다.
각 Container는 별도의 Writeable Layer를 구성하며, 이로 인하여 위의 두 Container는 서로 다른 파일을 생성한 것이다.
Read-Only Layer로 구성된 파일이나 디렉토리를 수정한다면, Copy-On-Write를 이용한다.
해당 파일이나 디렉토리를 Writeable Layer에 복사한 후, 이를 수정한다.
이러한 구조 덕분에 다양한 Image를 기반으로 새로운 Image를 생성하며, 각 Container가 독립적인 파일시스템을 가지게 되는 것이다.
docker container run --name f1 diamol/ch06-file-display
echo "FOOBAR" > something.txt
docker container cp something.txt f1:/input.txt
docker container start -a f1
위 예제는 Container의 파일을 바꾸어 다시 실행한다.
Image에는 input.txt
가 존재하기에 해당 내용을 출력한다.
해당 파일을 FOOBAR
라는 내용을 가진 파일로 바꾸어 FOOBAR
가 출력이 되는 것을 확인할 수 있다.f1
은 실행 후 종료되어 Exited
Status이나, 고유의 Storage는 여전히 남는다.
Container에 Read Only Layer만 존재하였다면 이러한 방식으로 파일을 대체할 수 없으나, Writeable Layer가 존재하기에 가능한 것이다.
docker container run --name f2 diamol/ch06-file-display
docker container rm -f f1
docker container cp f1:/input.txt .
동일한 Image의 새로운 Container 실행 후, 기존 Container f1
을 삭제하였다.
삭제된 Container는 더 이상 고유의 Storage를 유지하지 않는다.
그렇기에 마지막 명령어를 실행할 경우 f1
이라는 Container를 찾지 못하는 것이다.
Docker Volume
이전에는 Container 종료 시 Container 내부의 파일이 모두 사라지는 것을 확인하였다.
하지만 Docker를 이용하여 Container를 띄우는 경우, Container가 죽더라도 데이터가 유지되어야 한다.
이를 해결하기 위한 여러 방법 중, 우선 Volume에 대해 살펴본다.
docker container run --name todo1 -d -p 8010:80 diamol/ch06-todo-list
docker container inspect -f '{{.Mounts}}' todo1
docker volume ls
Volume을 구성하는 Dockerfile을 이용한 Image를 실행 후, 이를 확인하는 예제이다.
해당 Volume은 호스트(Linux Ubuntu)의 /var/lib/docker/volumes/...
에 저장이 되며, Container 내부의 /data
에 Mount된다.
영구적인 Storage를 이용하는 것이므로, Container와 관계없이 해당 공간은 어딘가에 할당되어야 한다.
Linux에서는 저 경로에 할당이 되며, Container와 Mount되어 이용이 가능한 것이다.
호스트나 Container에서 Volume 내부의 데이터를 변경한다면 이는 반대쪽에도 반영이 된다.
http://localhost:8010
에서 데이터를 추가한 후, 아래 예제를 실행한다.
docker container run --name todo2 -d diamol/ch06-todo-list
docker container run --name todo3 --volumes-from todo1 -d diamol/ch06-todo-list
docker container exec todo2 ls /data
docker container exec todo3 ls /data
todo2
는 Volume 명시 없이 실행하였으며, todo3
는 todo1
의 Volume을 이용하라는 --volumes-from
옵션을 이용하였다.todo2
는 새로운 Volume을 생성하여 어떠한 데이터도 존재하지 않은 상황이다.todo3
는 todo1
의 Volume을 그대로 이용하였기에 todo1
의 데이터가 조회되는 것을 확인할 수 있다.
또한 해당 Volume은 복사가 아닌 Mount가 이루어졌기에 todo1
혹은 todo3
에서 데이터를 수정하면 서로에게 영향을 준다.
지금까지는 Volume이 자체적으로 구성이 되었기에 Volume명이 16진수의 Hash값으로 조회가 된다.
Volume이 많아지면 서로 구별하기가 어렵기에 Volume을 생성할 때 Volume명을 명시하는 것이 좋다.
docker volume create todo-list
docker container run -d -p 8011:80 -v todo-list:/data --name todo-v1 diamol/ch06-todo-list
curl http://localhost:8011
docker container rm -f todo-v1
docker container run -d -p 8011:80 -v todo-list:/data --name todo-v2 diamol/ch06-todo-list:v2
docker container exec todo-v2 ls /data
todo-list
라는 Volume을 생성한 후, 이를 Mount하였다.curl
명령을 이용하여 todo-list.db
파일 생성을 유도하였으며, http://localhost:8011
에서 직접 데이터를 추가하여 확인하여도 된다.todo-v1
Container를 삭제한 후, todo-list
Volume을 명시하여 todo-v2
Container를 생성하였다.todo-v1
Container에서 변경된 데이터는 호스트에도 반영이 되므로, todo-v2
는 처음 실행하였지만 데이터가 존재하는 것을 확인할 수 있다.
이전에 본 Dockerfile에서는 VOLUME Directive를 이용하여 Volume 생성을 명시하였다.
Container 실행 시 Volume을 명시해준다면 문제가 되지 않으나, 명시하지 않을 경우 이전에 보았듯이 Hash값의 이름을 가진 Volume이 생성된다.
VOLUME Directive를 사용하여 옵션 사용을 까먹는 경우를 대비하여도 되지만, default에 의존하기보다는 Named Volume 사용이 더 낫다.
FileSystem Mount
Volume을 이용하면 호스트가 남아있는 한 영구적인 Storage 사용이 가능하다.
Linux 호스트에서는 /var/lib/docker/volumes/...
에 데이터를 저장하게 되며, 호스트에서 직접 접근하지 않는다면 안전하다.
하지만 Container를 거치지 않고 호스트에서 직접 데이터를 변경하고 싶을 경우, 권한이 제한될 수 있다.
이러한 경우 사용하기 좋은 것이 Bind Mount이다.
Mount는 지정한 파일이나 디렉토리를 Container의 특정 무언가와 연결하는 것이다.
Volume과 매우 유사하다고 볼 수 있다.
mkdir ~/databases
docker container run --mount type=bind,source=$(pwd)/databases,target=/data -d -p 8012:80 diamol/ch06-todo-list
curl http://localhost:8012
ls ./databases
홈 디렉토리에서 수행하였으며, ~/databases
와 Container의 /data
를 Mount하였다.
curl을 통해 todo-list.db
생성을 유도하였으며, ~/databases
에도 반영이 된 것을 확인할 수 있다.
일반적으로 Container는 호스트에 대한 공격을 방지하기 위해 최소한의 권한을 가진다.
호스트의 파일시스템에 직접 접근해야 하기에 권한 부여가 필요하다.
해당 Image의 Dockerfile에서는 Linux의 경우 root 권한을 부여하였다.
Write 작업이 필요하지 않다면 Read Only로 설정할 수도 있다.
docker container run --name todo-configured --mount type=bind,source=$(pwd)/diamol/ch06/exercises/todo-list/config,target=/app/config -d -p 8013:80 diamol/ch06-todo-list
curl http://localhost:8013
docker container logs todo-configured
위 예제는 설정 파일을 Mount하였다.
기본 Logging Level은 Info였으나, Debug Level로 명시된 설정 파일을 Mount하였다.
Limitation Of Mount
Mount는 만능이 아니다. 예제를 통해 Mount의 문제를 확인한다.
docker container run diamol/ch06-bind-mount
docker container run --mount type=bind,source=$(pwd)/diamol/ch06/exercises/bind-mount/new,target=/init diamol/ch06-bind-mount
Mount하려는 Container에 데이터가 존재할 경우, 해당 데이터를 전부 Overwrite한다.
따라서 기존 데이터에 추가적인 데이터를 넣고 싶을 경우, 다른 경로에 Mount를 하거나 Mount가 아닌 다른 방법을 찾아야 한다.
docker container run --mount type=bind,source=$(pwd)/diamol/ch06/exercises/bind-mount/new/123.txt,target=/init/123.txt diamol/ch06-bind-mount
이번에는 디렉토리가 아닌 파일을 Mount하였다.
이 경우, 기존 디렉토리는 유지가 되며 새로운 파일만 추가가 된다.
마지막 케이스는 재현하기 복잡하기에 글로만 설명한다. 서적에서도 동일한 이유로 글과 그림으로만 설명하였다.
분산 파일시스템에서 Mount할 경우, 언제나 같은 결과가 나오지 않는다.
Postgres DB와 Azure Files를 동시에 사용할 경우, 일부 기능은 Azure Files에서 지원하지 않는다.
이때 프로그램이 비정상적으로 동작하거나 아예 죽을 수도 있다.
물론 다른 환경에서는 잘 될 수도 있으나, 해당 예시와 같이 문제가 발생할 여지가 존재한다.
따라서 배포 전 철저한 테스트 및 QA를 통해 문제가 없음을 확인해야 한다.
Container FileSystem
각 Container는 독자적인 Writeable Layer를 가진다고 하였다.
또한 기반 Image의 Layer, Volume, Mount 등 또한 가능하다.
이러한 것들에 접근하는 방식이 모두 다르다면 관리가 어려울 터, 따라서 Virtual Disk를 통해 접근한다. 이를 Union FileSystem이라 한다.
덕분에 Container 내부에는 단일 Disk로 보이기에 편하게 사용할 수 있다.
- Writeable Layer - 각 Container에 독립적으로 존재하는 Layer이다. Container가 삭제되면 해당 공간도 함께 삭제된다.
- Local Bind Mount - 호스트와 Container간의 데이터 공유를 위해 사용한다. HTML이나 JavaScript 파일 등을 공유한다면 무중단 배포도 가능하다.
- Distributed Bind Mount - 클라우드 Storage와 Container간의 데이터 공유를 위해 사용한다. 다만 클라우드 환경이기에 Local보다 성능이 좋지 않으며 모든 기능을 사용하지 못할 가능성이 있다.
- Volume - Container의 데이터를 영구적으로 저장하기 위해 사용한다. Volume을 통해 데이터가 관리되기에 다른 프로그램이나 버전 업데이트 등의 경우에도 별도의 백업이 필요하지 않다.
- Image Layer - Container의 초기 FileSystem을 구성한다. 각 Layer는 쌓이게 되며 가장 최근의 Layer가 다른 Layer를 Override한다. Read-Only이기에 다른 Image에서도 사용 가능하다.
Lab
이번 Chapter에서 이용한 todo-list
와 동일하지만 기존 데이터가 존재하는 Image를 이용한다.
Container 실행 시 기존 데이터를 이용하지 않도록 Storage를 설정하여야 한다.
다양한 해결책이 존재한다.
우선 기존의 Container 및 Volume을 모두 삭제한다.
docker container rm -f $(docker container ls -a -q)
docker volume rm $(docker volume ls -q)
우선 Container의 데이터의 경로를 파악한다.
docker container run --name todo-lab-origin -d diamol/ch06-lab
docker container exec todo-lab-origin ls
docker container exec todo-lab-origin cat appsettings.json
Container의 /init-data
에 데이터가 저장이 되는 상황이다.
기존의 데이터를 삭제할 수 있으나, 이는 Lab에서 의도한 방식이 아닐 것이다.
새로운 Directory에 데이터가 저장되도록 설정하면 될 것이다.
echo '{
"ConnectionStrings": {
"ToDoDb": "Filename=/lab-db/todo-list.db"
}
}' > appsettings.json
docker volume create todo-lab
docker container run --name todo-lab -v todo-lab:/lab-db --mount type=bind,source=$(pwd)/appsettings.json,target=/app/appsettings.json -d -p 8010:80 diamol/ch06-lab
appsettings.json
을 수정하여 /init-data
대신 /lab-db
에 저장하도록 한다.
또한 새로운 Volume인 todo-lab
을 만들어 /lab-db
에 연결하였다.http://localhost:8010
에서 확인하면 기존의 데이터가 조회되지 않으며, 새로운 데이터는 /lab-db
에 저장이 되는 것을 확인할 수 있다.
Conclusion
Docker에서 영구적인 Storage 사용을 위한 Volume, Mount에 대해 알아보았다.
Volume과 Mount 두 방식 모두 알아보았으며, 목적에 맞는 적절한 방식을 선택하여야 한다.
AWS, GCP, Azure와 같은 클라우드 Storage를 사용하려면 별도의 플러그인이 필요하기에 관련 내용을 더 찾아보아야 한다.