도커 시작하기 3에서 컨테이너의 변경 내역을 유지하기 위해 --mount 옵션을 사용해서 호스트 파일 시스템에 컨테이너의 경로를 마운트하는 방법을 살펴봤는데 이 장에서는 스토리지에 대한 추가적인 내용을 살펴보겠다.

컨테이너와 로컬 파일 시스템

컨테이너의 파일은 호스트의 파일 시스템에 마운트되어 있다. 컨테이너는 이미지로부터 만들어지므로 크게 다음의 호스트 파일 경로에 마운트된다.

  • 이미지의 파일 내용을 저장하는 호스트 파일 경로(이미지는 실제로 레이어로 구성되므로 여러 경로 사용)
  • 컨테이너 구동 이후 변경 사항을 저장하기 위한 호스트 파일 경로

이 외에 --mount 옵션을 이용해서 호스트 파일 시스템이나 도커 볼륨에 마운트 할 수 있다.

docker inspect 명령어를 사용하면 호스트 파일 시스템에 마운트된 경로를 확인할 수 있다.

"Data": {
    "LowerDir": "/var/lib/docker/overlay2/8bec........생략",
    "MergedDir": "/var/lib/docker/overlay2/8bec.../merged",
    "UpperDir": "/var/lib/docker/overlay2/8bec.../diff",
    "WorkDir": "/var/lib/docker/overlay2/8bec.../work"
},

docker inspect 명령어의 출력 결과에서 Data 속성은 마운트된 위치를 표시한다. 여기서 UppderDir 속성은 컨테이너의 변경 내역을 저장한다. 콘테이너에 새로운 파일을 생성하고 UpperDir 속성의 디렉토리로 이동하면 컨테이너에 변경한 내역이 존재하는 것을 확인할 수 있다.

읽기 전용 컨테이너

--read-only 옵션을 사용하면 컨테이너를 읽기 전용으로 생성할 수 있다. 

vagrant@ubuntu-bionic:~$ docker run -d --name web --read-only -p 8080:80 nginx:latest
6077fb45aea3b5b71f4161211949b4e6ed4c9eb38c89d49b07daf1887608570d

읽기 전용으로 컨테이너를 구동하면 컨테이너의 파일을 새로 생성하거나 기존 파일을 변경할 수 없다. nginx의 경우 서브를 구동할 때 파일을 새로 생성해야 하는데 파일을 생성할 수 없으므로 위 명령어로 생성한 컨테이너는 정상적으로 시작하지 못하고 종료된다. docker ps 명령어로 web 컨테이너의 상태를 보면 다음과 같이 STATUS가 Exited로 표시된다.

vagrant@ubuntu-bionic:~$ docker ps -a
CONTAINER ID        IMAGE        ...  STATUS                     ... NAMES
6077fb45aea3        nginx:latest ...  Exited (1) 3 minutes ago   ... web

docker logs 명령어로 컨테이너의 로그를 보면 다음고 같이 파일 생성에 실패한 것을 확인할 수 있다.

vagrant@ubuntu-bionic:~$ docker logs web
2019/09/28 11:19:30 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
vagrant@ubuntu-bionic:~$

호스트 파일 시스템으로 마운트하기

--mount 옵션을 사용하면 컨테이너의 파일 시스템을 로컬 파일 시스템으로 마운트할 수 있다. 다음은 사용 예이다. 호스트 파일 경로에 마운트할 때에는 type 속성을 bind로 설정하고 source에는 호스트의 절대 경로를 지정하고, target에는 호스트 파일 경로에 마운트할 컨테이너의 경로를 지정한다.

$ docker run -d --name web --rm \
   --mount type=bind,source=/home/vagrant/nginx/html,target=/usr/share/nginx/html \
   --mount type=bind,source=/home/vagrant/nginx/run,target=/run \
   --mount type=bind,source=/home/vagrant/nginx/cache,target=/var/cache/nginx \
   -p 8080:80 \
   nginx:latest

위 명령을 실행한 뒤 /home/vagrant/nginx/run 디렉토리를 보면 nginx가 서버를 구동할 때 생성하는 nxinx.pid 파일이 생성된 것을 확인할 수 있다. 비슷하게 /home/vagrant/nginx/cache 디렉토리에는 nginx가 cache 목적으로 생성한 디렉토리가 생성된 것도 확인할 수 있다.

호스트 파일 경로에 마운트할 때 읽기 전용으로 마운트할 수도 있다. --mount 옵션을 지정할 때 readonly 옵션을 추가하면 된다.

docker run -d --name web --rm \
   --mount type=bind,source=/home/vagrant/nginx/html,target=/usr/share/nginx/html,readonly \
   --mount type=bind,source=/home/vagrant/nginx/run,target=/run \
   --mount type=bind,source=/home/vagrant/nginx/cache,target=/var/cache/nginx \
   -p 8080:80 \
   nginx:latest

읽기전용으로 지정한 마운트 경로는 컨테이너 내부에서는 수정이 안 된다.

vagrant@ubuntu-bionic:~/nginx$ docker exec web touch /usr/share/nginx/html/a.txt
touch: cannot touch '/usr/share/nginx/html/a.txt': Read-only file system

--mount 옵션에서 source 속성 대신에 src 속성을, target 속성 대신에 dst 속성을 사용해도 된다.

메모리 파일 시스템에 마운트하기

메모리 파일 시스템에 마운트할 수도 있다. --mount 옵션에서 type 값을 tmpfs로 지정하면 된다.

docker run -d --name web --rm \
  --mount type=bind,source=/home/vagrant/nginx/html,target=/usr/share/nginx/html,readonly \
  --mount type=bind,source=/home/vagrant/nginx/run,target=/run \
  --mount type=tmpfs,target=/var/cache/nginx \
  -p 8080:80 \
  nginx:latest

메모리에 파일 내용을 유지하므로 컨테이너를 종료하면 저장한 파일도 함께 삭제된다.

tmpfs는 메모리를 사용하므로 호스트 파일 시스템의 경로를 지정할 필요가 없다.

도커 볼륨

도커 볼륨은 도커가 관리하는 파일 시스템이다. 컨테이너의 파일 시스템도 도커 볼륨을 이용해 관리한다.

필요하면 도커 볼륨을 직접 생성하고 컨테이너에 마운트할 수 있다. 아래 명령어는 이름이 myvol인 볼륨을 생성한다.

vagrant@ubuntu-bionic:~$ docker volume create --driver local myvol
myvol

--driver 옵션은 볼륨을 생성할 때 사용할 스토리지 드라이버를 지정한다. 위 명령어는 local 드라이버를 사용해서 볼륨을 생성했다. local 드라이버는 호스트의 파일 시스템에 생성한 볼륨이 위치한다.

볼륨을 생성하면 --mount 옵션을 사용해서 컨테이너 경로를 볼륨에 마운트할 수 있다.

docker run -d --name web --rm \
  --mount type=volume,src=myvol,dst=/usr/share/nginx/html \
  -p 8080:80 \
  nginx:latest

--mount 옵션에서 type 속성은 volume을 지정하고 src에는 볼륨 이름을 지정한다.

도커 볼륨은 여러 컨테이너에서 공유할 수 있다. 다음과 같이 새로운 컨테이너에서 같은 볼륨을 사용하는 컨테이너를 만들고 해당 위치에 파일을 생성하자.

vagrant@ubuntu-bionic:~/nginx/html$ docker run -it --name gen --rm \
> --mount type=volume,src=myvol,dst=/website \
> centos:7 bash
[root@f27afdbfac6d /]# echo "hello world" > /website/hello.txt

위 코드는 myvol 볼륨을 /website 경로에 마운트한 컨테이너를 생성하고 이 컨테이너 안에서 /website/hello.txt 파일을 생성한다. 앞서 web 컨테이너는 myvol 볼륨을 /usr/share/nginx/html 경로에 마운트했는데 두 경로는 myvol 볼륨을 사용하므로 web 컨테이너의 /usr/share/nginx/html 경로에 hello.txt 파일이 생성된다. (http://호스트:8080/hello.txt 주소로 연결하면 gen 컨테이너에서 생성한 파일 내용이 표시된다.)

docker volume rm 명령어는 볼륨을 삭제한다. 단 볼륨을 사용중인 컨테이너가 존재하면 볼륨을 삭제할 수 없다.

vagrant@ubuntu-bionic:~/nginx/html$ docker volume rm myvol
Error response from daemon: remove myvol: volume is in use - [dd963...]

 

사용중인 컨테이너가 없는 볼륨을 모두 삭제하고 싶다면 docker volume prune 명령어를 사용하면 된다. 이 명령어를 사용하면 일일이 볼륨 이름을 지정할 필요가 없어 불필요한 볼륨을 삭제할 때 편리하다.

도커가 제공하는 local 스토리지 드라이버는 로컬 파일 시스템에 대한 볼륨을 지원한다. 여러 호스트에서 공유할 수 있는 볼륨을 생성하려면 별도 플러그인을 사용해야 한다. local 스토리지 드라이버는 NFS를 지원하므로 각 호스트에서 NFS로 연결한 볼륨을 같은 이름으로 생성해도 동일한 결과를 얻을 수 있다.

REX-Ray 같은 플러그인을 사용하면 클라우드 환경에서 볼륨을 생성하고 공유할 수 있다.

관련 글

+ Recent posts