[Infra] 도커(docker)(7) 도커 볼륨(docker volume)을 이용한 데이터 관리

업데이트:

도커(docker)(7) 도커 볼륨(docker volume)을 이용한 데이터 관리

참고 링크

1. 컨테이너 내부에서 생성된 데이터를 보존하려면?

도커를 사용하면 컨테이너를 활용해 서비스 배포를 쉽게 할 수 있습니다. 그리고 서비스 중에는 컨테이너 내부에 여러가지 새로운 파일들이 생성될 수 있는데요. 웹 서비스라면 로그파일, 머신러닝이라면 모델 파일 등과 같은 데이터가 발생할 것입니다. 그런데 만약 컨테이너를 삭제한다면 새롭게 생겨난 데이터들은 어떻게 될까요? 이를 위해 도커 볼륨(docker volume)을 사용합니다.

2. 도커 볼륨

도커 볼륨은 호스트 서버에 존재하며 컨테이너 속 파일들과 연동시키기 위해 사용합니다. 도커 볼륨을 사용하지 않을 경우 컨테이너를 삭제하면 컨테이너 가동 후에 발생된 파일들이 모두 사라지게 됩니다. 따라서 도커 볼륨을 사용하면 컨테이너를 삭제하더라도 변경/추가된 파일들은 모두 유지됩니다.

nginx와 flask api 서버를 하나 띄워보겠습니다. 다음과 같은 docker-compose.yml 파일을 하나 작성합니다. 우리의 목적은 model_test 컨테이너에서 발생되는 데이터를 도커 볼륨을 통해 보존하는 것입니다.

version: "3"
services:
    model_test:
        build: ./model_test
        volumes:
            - test02-tensorflow-volume:/model_test
        container_name: model-test02
        restart: always
        expose:
            - "6001"

    nginx:
        build: ./nginx
        container_name: nginx-test02
        restart: always
        ports:
            - "9001:9001"
        depends_on:
            - model_test

volumes:
    test02-tensorflow-volume:

가장 아래 파트에 있는 volumes: 는 도커 볼륨을 생성하겠다는 의미입니다. 볼륨의 이름은 test02-tensorflow-volume라고 짓겠습니다. 그리고 이렇게 생성된 도커 볼륨은 model_test 컨테이너와 연동 됩니다. model_test를 보시면 volumes: 라는 파트가 추가되었는데 이는 볼륨을 사용하겠다는 뜻입니다. 볼륨을 마운트 하는 방법은 그 다음 줄 처럼 source:destination 형식으로 쓰면 됩니다. source에 해당하는 것은 마운트 하고자 하는 볼륨 이름을 쓰면 되고 destination에 해당하는 것은 마운트 될 컨테이너의 디렉토리 명을 쓰면 됩니다. 위 문서를 보면 컨테이너 내부의 루트 경로 바로 아래의 /model_test 경로가 도커 볼륨과 연결되는 것을 알 수 있습니다. 경로를 아는 방법은 도커 파일을 보시면 알 수 있습니다.

model_test의 도커 파일은 다음과 같습니다.

FROM python:3.8.5

WORKDIR /model_test

COPY . .
RUN python -m pip install --upgrade pip
RUN pip install -r requirements.txt


# server
CMD gunicorn main:app --bind 0.0.0.0:6001 --timeout 3000

위 도커 파일을 보면 WORKDIR 경로가 컨테이너 상에서 루트 바로 아래인 /model_test라는 것을 알 수 있습니다.

따라서 이 상태로 서비스를 배포하면 됩니다.

$ docker compose -d --build
[+] Running 3/3
 ⠿ Network test02_default  Created                                                                                                                                                                            0.0s
 ⠿ Container model-test02  Started                                                                                                                                                                            0.4s
 ⠿ Container nginx-test02  Started

그리고 잘 배포 되었는지 확인해 보겠습니다.

$ docker container ls | grep test
78bd97283373   test02_nginx        "/docker-entrypoint.…"   5 seconds ago   Up 3 seconds   80/tcp, 0.0.0.0:9001->9001/tcp, :::9001->9001/tcp   nginx-test02
f922abec434d   test02_model_test   "/bin/sh -c 'gunicor…"   5 seconds ago   Up 4 seconds   6001/tcp                                            model-test02

model-test02 컨테이너를 이용해 딥러닝 모형을 학습시킵니다. 학습된 h5 파일은 컨테이너 내부에 떨어집니다. 그리고 이 파일들은 도커 볼륨으로 관리됩니다.

$ docker compose down
[+] Running 3/3
 ⠿ Container nginx-test02  Removed                                                                                                                                                                            0.4s
 ⠿ Container model-test02  Removed                                                                                                                                                                           10.3s
 ⠿ Network test02_default  Removed

그리고나서 컨테이너를 삭제했다가 다시 살려보겠습니다.

$ docker compose -d --build
[+] Running 3/3
 ⠿ Network test02_default  Created                                                                                                                                                                            0.0s
 ⠿ Container model-test02  Started                                                                                                                                                                            0.4s
 ⠿ Container nginx-test02  Started

컨테이너를 다시 살리고 컨테이너 내부를 보면 아까 학습시켰던 모형이 그대로 있는 것을 볼 수 있습니다.

$ docker exec -it model-test02 /bin/bash
root@f922abec434d:/model_test# ls
Dockerfile  __pycache__  data  main.ipynb  main.py  model  mymodel.ipynb  mymodel.py  requirements.txt
root@f922abec434d:/model_test# cd model/
root@f922abec434d:/model_test/model# ls
b5b2209af2129fda72a7272e802b5c90881ac303e768b0c05f368951200e0f4b.h5  
c45f80dbb7e8dd328ef26bea989b34b5c449cb7124168e3f4faab2d52da38e85.h5  
e89a7e33f7c1db0820cf74e5198b3d96c7218c327138e6d07aa7818bac2bd707.h5

볼륨에 대한 정보를 확인하고 싶다면 다음 커맨드를 입력합시다.

$ docker inspect test02_test02-tensorflow-volume
[
    {
        "CreatedAt": "2022-04-05T16:16:43+09:00",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "test02",
            "com.docker.compose.version": "2.2.2",
            "com.docker.compose.volume": "test02-tensorflow-volume"
        },
        "Mountpoint": "/var/lib/docker/volumes/test02_test02-tensorflow-volume/_data",
        "Name": "test02_test02-tensorflow-volume",
        "Options": null,
        "Scope": "local"
    }
]

위와 같이 정보를 확인하면 도커 볼륨이 저장되는 경로도 알 수 있습니다.

3. 위 방식대로 했을 때의 문제점

위 방식대로 했을 때 하나의 문제점이 있습니다. 앞서 만든 docker-compose.yml파일을 다시 보면 다음과 같습니다.

version: "3"
services:
    model_test:
        build: ./model_test
        volumes:
            - test02-tensorflow-volume:/model_test
        container_name: model-test02
        restart: always
        expose:
            - "6001"

    nginx:
        build: ./nginx
        container_name: nginx-test02
        restart: always
        ports:
            - "9001:9001"
        depends_on:
            - model_test

volumes:
    test02-tensorflow-volume:

위 파일을 보면 볼륨의 경로가 /model_test라는 것을 알수 있는데요. 이 말은 /model_test 경로 아래의 모든 파일을 볼륨으로 보존한다는 의미입니다. 즉, 다음과 같은 파일을 보존합니다.

위 그림에서 빨간 박스 내의 영역이 도커 볼륨으로 보존되는 파일들입니다. 그런데 잘보면 main.py파일이 보존되는 것을 볼 수 있는데, 이러면 추후에 로컬에서 main.py파일을 수정한 다음 다시 컨테이너로 올리면 파일이 수정된 버전으로 바뀌지 않습니다. 이를 확인해보겠습니다.

먼저 컨테이너에 접속해 main.py파일을 확인해보겠습니다.

$ docker exec -it model-test02 /bin/bash
root:/model_test# ls
Cockerfile data main.py model mymodel.py requirements.txt
root:/model_test# cat main.py
아래 그림 확인

위와 같이 파일을 확인하면 다음 그림과 같이 중간에 주석이 있는 것을 볼 수 있습니다.

컨테이너를 내리고 주석을 지운다음 다시 올려보겠습니다. 일단 컨테이너를 내리겠습니다.

$ docker compose down
[+] Running 3/3
 ⠿ Container nginx-test02  Removed 
 ⠿ Container model-test02  Removed
 ⠿ Network test02_default  Removed

로컬에서 해당 파일의 21번 줄을 다음과 같이 주석을 없애고 올려보겠습니다.

주석을 없앴습니다. 그럼 컨테이너를 다시 올려보겠습니다.

$ docker compose up -d --build
[+] Running 3/3
 ⠿ Network test02_default  Created 
 ⠿ Container model-test02  Started
 ⠿ Container nginx-test02  Started

그리고나서 다시 해당 컨테이너로 가서 파일을 확인해보면 주석이 안지워진것을 볼 수 있습니다.

그렇다면 주석을 지우려면 어떻게 해야할까요? 일단 컨테이너를 다시 내립시다.

$ docker compose down
[+] Running 3/3
 ⠿ Container nginx-test02  Removed 
 ⠿ Container model-test02  Removed
 ⠿ Network test02_default  Removed

위에서 우리가 보존하고 있는 파일들은 다음과 같다고 했습니다.

이렇게 했을때의 문제점은 main.py가 보존되어 업데이트가 적용되지 않습니다. 이 문제를 해결하려면 다음과 같이 보존 영역을 바꾸어야 합니다.

이를 위해 docker-compose.yml 파일을 수정해주겠습니다.

version: "3"
services:
    model_test:
        build: ./model_test
        #image: work_kpt_supply_chain
        volumes:
            - test02-tensorflow-volume:/model_test/model
        container_name: model-test02
        restart: always
        expose:
            - "6001"

    nginx:
        build: ./nginx
        container_name: nginx-test02
        restart: always
        ports:
            - "9001:9001"
        depends_on:
            - model_test

volumes:
    test02-tensorflow-volume:

위 파일을 보면 볼륨 경로가 /model_test/model로 바뀐것을 볼 수 있습니다. 이러고나서 다시 컨테이너를 올리고

$ docker compose up -d --build
[+] Running 3/3
 ⠿ Network test02_default  Created 
 ⠿ Container model-test02  Started
 ⠿ Container nginx-test02  Started

그리고 컨테이너 내부 파일을 확인하면

$ docker exec -it model-test02 /bin/bash
root:/model_test# ls
Cockerfile data main.py model mymodel.py requirements.txt
root:/model_test# cat main.py
아래 그림 확인

파일 수정이 적용된 것을 알 수 있습니다.