네모네모새

Docker 데이터 관리(Docker Volume vs bind mount) 본문

Docker

Docker 데이터 관리(Docker Volume vs bind mount)

네모새 2022. 3. 18. 23:46

Docker 데이터 관리

Docker 컨테이너의 데이터 관리...

Docker를 통해 애플리케이션을 컨테이너로 실행시키고 삭제하는 경우가 있는데,
이때 컨테이너의 데이터는 해당 컨테이너가 삭제되면 함께 사라지게 됩니다.

저의 경우 AWS EC2환경에 Docker 런타임을 구축하고 Jenkins를 컨테이너에 올려서 배포 및 CI/CD 테스트 등에 활용하고 있습니다.

Jenkins 내부에는 인증정보, 파이프라인 정보 등 다양한 정보가 담겨있는데,
만약 제가 실수로 컨테이너를 삭제할 경우 모든 정보들이 날아가게 됩니다..

Jenkins 이외에도 Redis, Mysql같은 DB라던가... 로그 수집을 위한 컨테이너라던가...
많은 애플리케이션들이 컨테이너 생명주기와 무관하게 데이터의 영속성을 보장해야 하므로
Docker 컨테이너의 데이터 관리는 중요한 이슈입니다.

Docker Volume과 bind mount

Docker에서는 데이터의 영속성을 관리하기 위한 Docker Volume과 bind mount라는
두 가지 방법을 제안하고 있습니다.

이 두가지 방법을 활용해 Docker 컨테이너의 데이터의 영속성을 보장하는 방법을 알아보겠습니다.

Docker 공식 문서 Volumes

Docker의 데이터 저장에 관련된 자세한 내용은 위의 API를 참고해주세요.

실습환경

  • AWS EC2 활용(공인 IP : 13.124.173.247)
  • Docker Volume : Jenkins images 활용
  • bind mount : Nginx image 활용

Docker Volume

도커 볼륨은 Filesystem의 Docker area라고 되어있는 부분을 컨테이너에 마운트하는 방식입니다.

아래 명령어들을 이용해 도커 볼륨을 생성하고 조회할 수 있습니다.

Docker Volume 생성 및 정보 확인

# 도커 볼륨 생성
$ docker volume create my-vol

# 도커 볼륨 목록 확인
$ docker volume ls

# 볼륨 정보 확인
$ docker volume inspect my-vol
[
    {
        "CreatedAt": "2022-03-20T06:22:33Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

inspect 명령어를 수행하면 위와 같은 결과를 확인할 수 있는데,
Mountpoint에서 확인할 수 있는 "/var/lib/docker/volumes"가 개념도의 Filesystem 내부의 "Docker area"입니다.

해당 경로 안에 {볼륨 이름}/_data에 컨테이너의 특정 경로를 마운트 할 수 있습니다.

Jenkins 컨테이너 구동

이제 생성한 my-vol을 이용해 Jenkins 컨테이너를 구동시켜 보겠습니다.

DockerHub Jenkins Repository

이미지는 Docker Hub에 올라와있는 Jenkins 이미지를 사용하겠습니다.

Jenkins 이미지의 경우 Docker 컨테이너를 통해 최초 실행시 컨테이너 내부의
[/var/jenkins_home/secrets/initialAdminPassword]값이 필요합니다.

로그로 확인하거나, docker exec 명령어를 통해 컨테이너 쉘에 직접 접근하여 확인할 수도 있지만,
위에서 생성한 my-vol에 마운트를 해서 해당 값을 찾아보겠습니다.
명령어는 아래와 같습니다.

# 컨테이너 생성
$ docker run -itd -v my-vol:/var/jenkins_home --name jenkins -p 8080:8080 jenkins/jenkins:jdk11

# 실행 결과
Unable to find image 'jenkins/jenkins:jdk11' locally
jdk11: Pulling from jenkins/jenkins
e4d61adff207: Pull complete
eacef06daf30: Pull complete
ca581b0141a3: Pull complete
d872c65909bb: Pull complete
bcce550e05a9: Pull complete
3461f061a833: Pull complete
3b6f8a58a68d: Pull complete
6d47f55855ba: Pull complete
baa80a92c8e4: Pull complete
39889d888af7: Pull complete
18b5e0e36b4c: Pull complete
a53e22d026ad: Pull complete
a281963da5b5: Pull complete
2366689c95a7: Pull complete
27cbe8a0f233: Pull complete
fffccee1c284: Pull complete
a6afec98241f: Pull complete
Digest: sha256:a5215b81a7f6e111ed6625b342521145e24c232891615be29ce3a251a631feac
Status: Downloaded newer image for jenkins/jenkins:jdk11
b188f3fd411d3ac75c7795b7fdd8371b79ae8612ed57669f958458439da1a8c6

# 컨테이너 확인
$ docker ps

# 결과
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
b188f3fd411d   jenkins/jenkins:jdk11   "/sbin/tini -- /usr/…"   49 seconds ago   Up 48 seconds   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp   jenkins

-v 옵션을 통해 앞서 생성한 my-vol 볼륨을 컨테이너의 /var/jenkins_home 경로에 마운트 합니다.
jenkins/jenkins:jdk11 이미지가 없으니 해당 이미지를 Pull해오고 구동이 완료된것을 확인할 수 있습니다.

-p 옵션을 통해 8080번 포트를 개방했으니 EC2에 정상적으로 Jenkins 애플리케이션이 배포되었는지 확인하겠습니다.

Jenkins 구동에 성공했습니다!
그런데 Jenkins 구동을 위해 Administrator password를 입력해달라고 합니다.

만약 Volume을 마운트하지 않은 상태라면 로그를 확인하거나 docker exec 명령어를 이용해 직접 해당 파일에 접근해야 합니다.

하지만 이번에는 /var/jenkins_home 경로를 my-vol로 마운트했기 때문에 로컬 호스트의 my-vol이 저장된 위치에서 해당 데이터를 확인할 수 있습니다!

# 컨테이너 정보 확인
$ docker inspect {jenkins continer id}

# 결과
... 중간생략
"Mounts": [
            {
                "Type": "volume",
                "Name": "my-vol",
                "Source": "/var/lib/docker/volumes/my-vol/_data",
                "Destination": "/var/jenkins_home",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
]
... 이하생략

컨테이너 정보 확인을 위해 inspect 명령어를 사용하겠습니다.
잡다한 내용은 생략하고 Mounts 부분을 보면

Name이 my-vol인 볼륨이 마운트 되어 있으며,
로컬 호스트의 /var/lib/docker/volumes/my-vol/_data를 Source로 하고
컨테이너의 /var/jenkins_home을 Destination으로 하고 있다는 것을 확인할 수 있습니다.

# 로컬 호스트의 Source 경로로 이동
$ cd /var/lib/docker/volumes/my-vol/_data

$ ls -al
total 84
drwxr-xr-x 12 ubuntu ubuntu 4096 Mar 20 06:26 .
drwx-----x  3 root   root   4096 Mar 20 06:22 ..
drwxr-xr-x  4 ubuntu ubuntu 4096 Mar 20 06:25 .cache
drwxr-xr-x  3 ubuntu ubuntu 4096 Mar 20 06:25 .java
-rw-r--r--  1 ubuntu ubuntu    0 Mar 20 06:26 .lastStarted
-rw-r--r--  1 ubuntu ubuntu 1661 Mar 20 06:26 config.xml
-rw-r--r--  1 ubuntu ubuntu   50 Mar 20 06:25 copy_reference_file.log
-rw-r--r--  1 ubuntu ubuntu  156 Mar 20 06:25 hudson.model.UpdateCenter.xml
-rw-------  1 ubuntu ubuntu 1712 Mar 20 06:25 identity.key.enc
-rw-r--r--  1 ubuntu ubuntu  171 Mar 20 06:25 jenkins.telemetry.Correlator.xml
drwxr-xr-x  2 ubuntu ubuntu 4096 Mar 20 06:25 jobs
-rw-r--r--  1 ubuntu ubuntu  907 Mar 20 06:25 nodeMonitors.xml
drwxr-xr-x  2 ubuntu ubuntu 4096 Mar 20 06:25 nodes
drwxr-xr-x  2 ubuntu ubuntu 4096 Mar 20 06:25 plugins
-rw-r--r--  1 ubuntu ubuntu   64 Mar 20 06:25 secret.key
-rw-r--r--  1 ubuntu ubuntu    0 Mar 20 06:25 secret.key.not-so-secret
drwx------  2 ubuntu ubuntu 4096 Mar 20 06:25 secrets
-rw-rw-r--  1 ubuntu root   7152 Mar 15 14:55 tini_pub.gpg
drwxr-xr-x  2 ubuntu ubuntu 4096 Mar 20 06:26 updates
drwxr-xr-x  2 ubuntu ubuntu 4096 Mar 20 06:25 userContent
drwxr-xr-x  3 ubuntu ubuntu 4096 Mar 20 06:25 users
drwxr-xr-x 11 ubuntu ubuntu 4096 Mar 20 06:25 war

# Destination 경로/secrets/initialAdminPassword
$ cat secrets/initialAdminPassword

# 결과 확인
80f24341f4bf449b8d3ad95d61061511

그렇다면 로컬 호스트에서 cd 명령어를 이용해 Source 경로의 데이터를 조회해 보겠습니다.

어떤 파일들이 들어있고 AdministratorPassword값을 찾기 위해 필요한 secrets라는 폴더도 보입니다!
cat 명령어를 이용해 Password 값을 조회하고 결과값을 Jenkins 웹 콘솔에 입력해보겠습니다.

인증에 성공했습니다!

컨테이너 삭제 후 데이터 확인

그럼 이제 Jenkins 컨테이너를 삭제하고 데이터를 확인해 보겠습니다.

# Jenkins 컨테이너 정지
$ docker stop {jenkins container id}

# Jenkins 컨테이너 삭제
$ docker rm {jenkins container id}


docker rm 명령어를 이용해 컨테이너를 삭제했지만,
컨테이너의 Source 경로엿던 /var/lib/docker/volumes/my-vol과 내부 파일이 그대로 남아있는 것을 확인할 수 있습니다!

bind mount

bind mount는 로컬 호스트의 특정 디렉토리를 컨테이너에 마운트 하는 방식입니다.

Docekr Volume과의 차이점은 Docker Volume의 경우 Docker가 해당 마운트 포인트를 지속적으로 관리해 주지만,
bind mount의 경우 Docker가 생성한것이 아닌 로컬 호스트의 특정 경로이기 때문에 Docker가 관리를 해주지 않습니다.

mount 대상 디렉토리 생성

# 마운트 대상 폴더 및 테스트 파일 생성
$ mkdir /app
$ cat <<EOF > index.html
> hello world
> EOF

먼저 루트 경로에 app이라는 폴더를 생성하고 내부에 index.html 파일을 생성하였습니다.

Nginx 컨테이너 구동

bind mount는 간단하게 nginx 이미지를 통해 테스트 해보겠습니다.

Nginx 이미지의 루트 디렉토리는 /usr/share/nginx/html입니다.
DockerHub Nginx Repository

# nginx 컨테이너 생성
docker run -itd -v /app:/usr/share/nginx/html --name nginx -p 80:80 nginx

docker run 명령어로 nginx 컨테이너를 생성하였습니다.
포트는 80번이고 앞에서 생성한 /app 폴더를 Nginx의 루트 디렉토리에 마운트 하였습니다.
정상적으로 마운트가 되었다면 배포한 컨테이너의 80번 포트로 접속했을 때,
hello world가 나타나야 합니다.

정상적으로 hello world가 출력된 것을 확인할 수 있습니다!

결론

Docker Volume vs bind mount

지금까지 Docker Volume과 bind mount를 이용해 도커 컨테이너 내부의 데이터를 영속적으로 관리하는 방법을 실습해 보았습니다.

그렇다면 어떤게 더 좋은 방법일까요?

먼저 Docker 설명서에 따르면 Docker Volume을 사용하는 것이 도커 컨테이너에서 데이터를 유지하는 가장 쉬운 방법입니다. 또한, 보안적인 측면에서도 Docker Volume이 더 안전하다고 소개합니다.

Docker Volume은 도커가 관리해주기 때문에 사용자가 신경쓸 것이 비교적 적지만,

로컬에서 개발을 하는 경우와 같이 특정 경로에 git reposiroty를 clone하고 빈번히 수정하는 등 몇몇 상황에서는 bind mount를 사용하는 것이 더 적합할 수 있습니다.

참고

  1. Docker 공식 문서 Volumes
  2. DockerHub Jenkins Repository
  3. DockerHub Nginx Repository

'Docker' 카테고리의 다른 글

Docker와 인증서(1/2)  (0) 2022.04.05
Comments