[Docker in Action] Working with storage and volumes (2)

2022. 4. 23. 18:02DevOps/Docker & Kubernetes

 

 

 

 

Shared mount points and sharing files

여러 개의 컨테이너에서 동일한 File Set(set of files)을 공유할 수 있도록 하는 기능은 매우 유용합니다. 파일 공유는 이전 포스팅에서 설명한 "Bind Mounts"와 "Docker Volume"의 2가지 방법을 통해 사용할 수 있습니다.

 

Bind Mounts

다음 명령어를 순차적으로 실행해보겠습니다.

// Host Machine에 디렉터리를 생성합니다.
LOG_SRC=~/web-logs-example
mkdir ${LOG_SRC}

// plath 라는 이름의 컨테이너를 실행합니다.
// Host Machine의 path와 컨테이너의 path를 바인딩합니다.
// 로그를 기록합니다.
docker run --name plath -d \
  --mount type=bind,src=${LOG_SRC},dst=/data \
  dockerinaction/ch4_writer_a
 
// Host Machine의 path(위와 동일)와 컨테이너의 path를 바인딩합니다.
// head 명령어를 통해 로그를 읽습니다.
docker run --rm \
  --mount type=bind,src=${LOG_SRC},dst=/data \
  alpine:latest \
  head /data/logA
 
 // Host Machine의 path에 로그가 기록되었는지 확인합니다.
 cat ${LOG_SRC}/logA

 

 

실제로 위 명령어를 순차적으로 실행시켜보면 아래와 같은 결과가 나오는 것을 확인할 수 있습니다. 첫 번째 컨테이너에서 기록한 로그를 두 번째 컨테이너에서 정상적으로 읽었으며, 두 컨테이너는 모두 BindMounts를 통해 Host Machine의 특정 디렉터리(~/web-logs-example)에 바인딩 되었으므로 실제로 해당 디렉터리에 접근해서 읽었을 때도 컨테이너에서 기록한 로그들을 읽을 수 있었습니다.

 

이렇게 BindMounts를 사용하면 편하게 볼륨을 여러 컨테이너 간에 공유할 수 있지만, 이전 포스팅에서 설명한 것과 같이, 도커 프로세스가 아닌 다른 프로세스에서도 이를 접근할 수 있다는 문제점이 있고(해당 디렉터리는 도커가 관리하는 디렉터리가 아니라 호스트 머신의 어떤 디렉터리라도 가능하기 때문에), BindMounts에 관련된 모든 컨테이너들이 host file path에 대해 알고 있어야 하므로 Host machine에 의존성이 생기게 된다는 문제점도 있습니다. 따라서 여러 컨테이너 간에 데이터를 공유하기 위해 대부분의 경우 Docker Volume을 사용하게 됩니다.

 

Docker Volume

도커 볼륨을 사용한 방식은 BindMounts 방식과 거의 동일하지만, Host File Path에 대해서 의존성이 없다는 점에서는 차이가 있습니다. 다음 명령어를 순차적으로 실행해 보겠습니다.

// docker volume은 도커에서 관리하므로 명시적으로 생성해 주어야 합니다.
docker volume create \
  --driver local \
  logging-example

docker run --name plath -d \
  --mount type=volume,src=logging-example,dst=/data \
  dockerinaction/ch4_writer_a
 
docker run --rm \
  --mount type=volume,src=logging-example,dst=/data \
  alpine:latest \
  head /data/logA
  
cat "$(docker volume inspect \
  --format "{{json .Mountpoint}}" logging-example)"/logA

 

실제로 실행시켰을 때 정상적으로 첫 번째 컨테이너에서 볼륨에 Write 한 데이터를 두 번째 컨테이너에서 제대로 읽어 들이고 있는 것을 확인할 수 있습니다. 이렇게 볼륨을 사용하면 이전의 BindMounts방식과는 다르게 Host Machine의 정확한 경로를 알 필요 없이 Volume의 이름을 통해서 마운트 할 수 있으므로 Host Machine에 대한 의존성이 사라지게 됩니다.

 

 

실제로 Linux 환경이 아닌 MacOS 환경인 경우, 도커에서 "/var/lib/docker/volumes/~"를 찾을 수 없다고 나오는데, 이는 실제로 MacOS운영체제에서는 해당 Host Machine의 디렉토리에 바로 Write 하는 것이 아니라 작은 가상 머신(VM)을 띄워 리눅스 환경처럼 만든 후에 그 위에서 도커 엔진을 동작시키기 때문입니다. 따라서 MacOS에서 테스트하는 경우 Docker Desktop을 사용하거나 해당 가상 머신 안으로 접근한 뒤에 거기서 아래의 커맨드를 동작시켜야 합니다.

 

 

Anonymous volumes and the volumes-from flag

이렇게 도커 볼륨을 사용하면 Host Machine의 파일 시스템에 대해 알지 못해도 볼륨의 이름을 가지고 컨테이너에 마운트를 쉽게 할 수 있습니다. 하지만 이런 방식은 한계도 존재하는데, 중복된 이름에 대해서 "name-conflicts"가 발생할 수 있다는 것입니다. 그리고 이러한 중복된 볼륨 이름에 대한 문제는 Anonymous Volumes를 사용하면 어느 정도 해결할 수 있습니다.

 

볼륨을 이름없이 바로 생성하면 익명의 볼륨, 즉 Anonymous Volumes가 생성됩니다. 

이 익명의 볼륨은 별도의 이름이 없이 유일한 긴 스트링의 식별자를 갖습니다. 실제로 사용하는 입장에서 이 식별자를 사용하는 것은 어렵기 때문에 도커에서는 volumes-from이라는 flag를 제공합니다.

 

 

volumes-from 플래그는 하나 이상의 컨테이너에서 사용되는 마운트 정의를 그대로 복사해서 새로운 컨테이너에 주입해줍니다. 즉, A 컨테이너에서 익명의 볼륨을 특정한 dst에 마운트 한 뒤에 B 컨테이너를 "--volumes-from A" 커맨드를 사용해 실행한다면 A 컨테이너의 마운트 포인트와 A 컨테이너에서 생성된 볼륨이 그대로 B 컨테이너에도 마운트 되는 것입니다.(It copies all mount-point definitions present on a referenced container into the new container.) 다음의 예제를 통해 이를 확인해보겠습니다.

docker run --name fowler \
    --mount type=volume,dst=/library/PoEAA \
    --mount type=bind,src=/tmp,dst=/library/DSL \
    alpine:latest \
    echo "Fowler collection created."

docker run --name knuth \
    --mount type=volume,dst=/library/TAoCP.vol1 \
    --mount type=volume,dst=/library/TAoCP.vol2 \
    --mount type=volume,dst=/library/TAoCP.vol3 \
    --mount type=volume,dst=/library/TAoCP.vol4.a \
    alpine:latest \
    echo "Knuth collection created"
    
docker run --name reader \
    --volumes-from fowler \
    --volumes-from knuth \
    alpine:latest ls -l /library/
    
docker inspect --format "{{json .Mounts}}" reader

 

fowler라는 컨테이너에서 마운트한 익명의 볼륨과 knuth라는 컨테이너에서 마운트 한 익명의 볼륨이 모두 reader라는 볼륨에 마운트 된 것을 확인할 수 있습니다. 각각의 볼륨이 마운트 된 위치도 fowler, knuth 컨테이너에서 마운트 한 위치와 동일하다는 것을 알 수 있습니다.

 

이렇게 --volumes-from을 사용하면 다른 컨테이너의 볼륨 마운트를 쉽게 가져올 수 있다는 장점이 있습니다. 다만 이는 다른 컨테이너의 설정을 "그대로 덮어쓰는" 방식으로 동작하기 때문에 다음 3가지 경우에 대해 주의해야 합니다.

 

  1. Source Container와 다른 마운트 포인트에 마운트를 할 수 없습니다. 즉, Source Container에서 볼륨이 '/tmp' path에 마운트되었다면, --volumes-from을 사용해서 Target Container에 마운트 할 때 '/subdirectory/tmp'와 같은 다른 path로 마운트 할 수는 없습니다.

  2. 여러 개의 Source Container를 마운트 하는 경우, 겹치는 마운트 포인트들이 있다면 가장 마지막에 마운트 된 볼륨의 정보로만 덮어씌워 지게 됩니다. 

  3. --volumes-from은 full volume definition을 복사하는 것이기 때문에 특정 path에 대한 권한을 수정할 수 없습니다.

 

Cleaning up volumes

터미널에 다음과 명령어를 입력하면 현재 존재하는 도커 볼륨의 리스트를 확인할 수 있습니다.

docker volume list

 

각각의 볼륨들은 remove 명령어를 통해 지울 수 있으며 익명 볼륨의 경우 docker run --rm 플래그를 통해 컨테이너가 지워질때 같이 지워지도록 설정할 수 있고, docker volume remove ${VOLUME_NAME} 커맨드를 통해 수동으로 제거할 수도 있습니다. 단 이름이 존재하는 볼륨의 경우 반드시 수동으로만 제거해야 합니다.

 

 

 

반응형