도커 볼륨을 이해하기 위해서는 도커 레이어 아키텍처를 알고 있어야 한다.
도커 레이어 아키텍처
도커 컨테이너 이미지는 레이어 구조를 가지며, 도커 레이어는 크게 이미지 레이어와 컨테이너 레이어로 나누어져 있다.
보통 우리는 Dockerfile이라는 파일을 이용해 도커 이미지를 빌드한다. 이 Dockerfile 내에 작성되어 있는 명령어 들이 순차적으로 레이어가 쌓이듯이 저장된다. 이러한 레이어 구조의 장점은 다음과 같다. 만약에 현재 이미지에서 Layer4: Source Code 부분에 변경사항을 적용한 후에 다시 도커 이미지를 빌드한다고 가정하자. 그러면 도커는 Layer1부터 다시 레이어를 만드는 것이 아니라 변경사항이 없는 Layer1~Layer3 까지는 재활용하고 그 후에 분기를 쳐서 Layer4와 Layer5를 새롭게 만든다. 즉, 변경사항이 일어난 시점 이후에 대해서만 새로운 레이어를 만드는 것이다.
도커 컨테이너는 docker run 명령어로 생성된다. 도커 컨테이너를 생성할 때 이미지 layer를 읽기 전용으로 불러서 사용한다. 읽기 전용으로 가져오기 때문에 이미지 레이어에는 변경사항을 적용할 수 없게 된다. 컨테이너를 만들면 컨테이너 레이어가 생성되는데 이 컨테이너 레이어는 읽기,쓰기를 모두 할 수 있다. 그리고 이 컨테이너 레이어는 컨테이너가 종료되면 삭제된다. 다시말해 컨테이너 내에서 코드를 수정할 수 있고 수정된 사항은 컨테이너 레이어에 저장되며, 컨테이너를 종료하면 컨테이너 내에서 반영했던 수정사항은 폐기된다. 그래서 컨테이너 레이어는 임시 데이터 저장소라고 생각하면 된다.
도커 볼륨
앞서 말했던 것 처럼 컨테이너는 임시 데이터 저장소로, 컨테이너를 종료하면 컨테이너 내에서 수정한 내용들이 사라진다. 이를 해결하기 위해 볼륨 마운트 기능으로 우리의 컴퓨터에 도커 컨테이너를 연결하여 연동해서 쓴다. 이렇게 하면 도커 컨테이너 내부의 수정사항이 내 로컬 컴퓨터에도 반영이 되고, 컨테이너를 종료해도 수정사항은 로컬에 남아있게 된다.
컨테이너 상에서 볼륨을 영구적으로 사용하는 방법은 세가지가 있다. 하나씩 살펴보자.
1. 호스트 볼륨으로 도커 볼륨 활용하기
호스트의 디렉토리를 컨테이너의 특정 경로에 마운트한다. 가장 직관적이고 쉬운 방법이다.
docker run -v <host volume path>:<container volume path>
호스트 볼륨을 사용할 때는 -v 옵션을 사용한다.
$ mkdir html
$ cd html
$ cat > index.html
$ docker run \
-d \
-v $(pwd)/html:/usr/share/nginx/html \
-p 80:80 \
nginx
$ docker exec -it <container name> bash
먼저 현재 경로에 마운트할 폴더를 생성하고 그 안에 index.html 파일을 만들어보자.
<h1>Hello World!!</h1>
그 후 docker run 명령어로 컨테이너를 만들고 docker exec 로 컨테이너로 접속하여 확인해보면 아까 만들었던 index.html 파일이 존재하는 것을 볼 수 있다. 즉, 내 로컬에 존재하는 $(pwd)/html 경로와 컨테이너 상의 /usr/share/nginx/html 경로는 연결되어 어디서 작업을 하던지 변경사항이 두 개의 경로에 모두 적용이 된다.
2. 볼륨 컨테이너로 도커 볼륨 활용하기
호스트 볼륨이 컨테이너와 호스트 사이의 디렉터리를 공유하는 것이었다면, 볼륨 컨테이너는 컨테이너 간에 디렉터리를 공유한다. 볼륨 컨테이너는 데이터를 저장하는 것만이 목적인 컨테이너이다. 그렇기 때문에 볼륨 컨테이너 방식은 도커에서 관리하는 영역인 호스트 머신의 /var/lib/docker/volumes/ 디렉터리에만 영향을 미친다. 호스트 머신이 컨테이너에 미치는 영향을 최소한으로 억제하는 거이다. 또한 볼륨 컨테이너가 직접 볼륨을 다뤄주기 때문에 볼륨을 필요로 하는 컨테이너가 사용할 호스트 디렉터리를알 필요가 없고, 디렉터리를 제공하는 볼륨 컨테이너만 지정하면 된다.
$ docker run --volumes-from <container>
컨테이너를 생성할 때 –volumes-from 옵션을 설정하면 -v 또는 -volume 옵션을 적용한 컨테이너의 볼륨 디렉터리를 공유할 수 있다.
$ docker run \
-d \
-it \
-v $(pwd)/html:/usr/share/nginx/html \
--name web-volume \
ubuntu:focal
$ docker run \
-d \
--name my-nginx \
--volumes-from web-volume \
-p 80:80 \
nginx
$ docker run \
-d \
--name my-nginx2 \
--volumes-from web-volume \
-p 8080:80 \
nginx
docker inspect로 마운트된 경로를 확인할 수 있다.
docker exec 명령어로 my-nginx2에 들어가서 확인하면 hello 파일과 index.html 파일이 모두 존재하는 것을 확인할 수 있다.
이런식으로 사용하면 볼륨 컨테이너 기능을 하는 volumn_container 컨테이너 하나를 두고 여러개의 컨테이너와 공유하는 방식으로도 활용이 가능해진다. 그림으로 보면 다음과 같다.
3. 도커 볼륨으로 도커 볼륨 활용하기
도커 볼륨은 도커 자체에서 제공하는 볼륨 기능이다. 도커 볼륨은 도커가 제공하는 볼륨 관리 기능을 활용하여 데이터를 보존한다. 기본적으로 도커가 관리하는 특정 호스트 경로인 /var/lib/docker/volumes/${volume-name}/_data 에 데이터가 저장된다.
# db라는 이름의 도커 볼륨 생성
$ docker volume create --name db
# 도커 볼륨 목록 확인
$ docker volume ls
# 도커의 db 볼륨을 mysql의 /var/lib/mysql (mysql이 데이터를 쌓는 경로) 디렉토리로 마운트
$ docker run -d \
--name my-mysql \
-e MYSQL_DATABASE=syj \
-e MTSQL_ROOT_PASSWORD=1234 \
-v db:/var/lib/mysql \
-p 3306:3306 \
mysql:5.7
mysql 컨테이너는 MYSQL_DATABASE와 MTSQL_ROOT_PASSWORD를 설정해줘야 정상적으로 작동한다. MYSQL_DATABASE는 최초로 생성될 데이터베이스의 이름, MTSQL_ROOT_PASSWORD는 mysql root 계정의 패스워드이다.
$ docker volume inspect <volume name>
inspect로 호스트 상의 어떤 경로에 마운트 되어 있는지 (Mountpoint) 확인할 수 있다.
$ sudo ls -l /var/lib/docker/volumes/${volume-name}/_data
그 경로에 어떤 정보가 들어있는지 확인해보면 mysql 데이터가 해당 호스트 경로에 잘 쌓이고 있는 것을 확인 할 수 있다.
# 도커 볼륨 삭제
$ docker volume rm <volume name>
위와 같이 제거하려는 볼륨이 마운트되어 있는 컨테이너가 있을 때는 해당 볼륨이 제거 되지 않는다. 그럴 때는 해당 볼륨이 마운트되어 있는 모든 컨테이너를 먼저 삭제하고, 볼륨을 삭제해야 한다.
읽기 전용 볼륨 연결
볼륨 연결 설정에 :ro 옵션 (read-only)을 통해 읽기 전용 마운트 옵션을 설정할 수 있다. 보통 읽기 전용 옵션은 변경이 되서는 안되는 파일이나 디렉토리를 연결할 때 사용한다.
$ docker run \
-d \
-v $(pwd)/html:/usr/share/nginx/html:ro \
-p 80:80 \
--name ro-nginx \
nginx
# 빈 파일 만들기
$ docker exec ro-nginx touch /usr/share/nginx/html/test
위를 실행하면 read-only로 마운트 되었기 때문에 test 파일을 만들 수 없다는 에러가 뜬다.