[Docker] Volume

GreenBean·2022년 7월 29일
0
post-thumbnail
post-custom-banner

Docker: Volume

[Docker 기본]: Volume을 활용한 Data 관리

Volume을 활용한 Data 관리

  • Container의 Writable Layer에 Data를 저장했을 때 몇 가지 문제점 존재
    • Container가 삭제되면 Data도 같이 삭제됨
      • 또한, 다른 프로세스에서 Container에 저장된 Data를 사용하기 어려움
    • Container의 Writable Layer에는 Container가 실행 중인 Host Machine과 밀접하게 연결됨
      • 따라서 Data를 다른 곳으로 쉽게 옮길 수 없음
    • Container의 Writable Layer에 Data를 저장하기 위해서는 File System을 관리하는 Storage Driver가 필요
      • Storage Driver는 Linux 커널을 사용하여 공용 File System을 제공
      • 이 기능은 Host File System에 직접 쓰는 data volume보다 성능이 떨어짐
  • Docker는 Data를 안전하게 존속시킬 수 있는 방식으로 volume, bind mounts, tmpfs mount의 3가지 방식을 제공
    • 어떤 것을 사용해야할 지 모를 때는 volume를 사용 권장

올바른 Mount 유형 선택 방법

  • 어떤 유형의 Mount를 사용하든, Data는 Container 내에서 동일하게 보이며, Container File System의 폴더나 개별적인 파일들로 표시됨
    • 올바른 Mount 유형을 선택할 때 기준이 될 volume, bind mounts, tmpfs mount간의 가장 큰 차이점은, Data가 Docker Host내에서 어디에 존재하는지의 여부

Data 저장을 위한 최선의 선택

  • volume는 Docker(Linux에서는 /var/lib/docker/volume/)가 관리하는 Host File System의 일부에 Data가 저장됨
  • Non-Docker 프로세스들이 File System의 해당 부분을 수정해서는 안됨
  • Docker에서 Data를 존속시킬 수 있는 Best한 방법

Host의 File System 원하는 곳에 저장

  • bind mount는 Data가 Host System의 어디에든지 저장될 수 있음
    • 저장되는 Data는 System File이거나 Directory일 수 있음
    • Docker Host 또는 Docker Container의 Non-Docker 프로세서들이 언제든지 저장된 Data를 수정할 수 있음

Host System의 Memory에 저장

  • tmpfs mount는 Host System의 Memory에만 Data가 저장되며, 절대로 Host의 File System에는 저장되지 않음

Mount 유형

volume

  • Docker가 생성하고 관리하는 방식
    • docker volume create 명령을 사용하여 명시적으로 volumes를 생성하거나, Container나 Service 생성 중에 volume을 생성할 수 있음
  • volume이 생성되면, Data는 Docker Host의 디렉토리에 저장됨
    • 해당 volume을 Container에 Mount하면, Host의 디렉토리가 Mount가 됨
    • 이는 volume이 Docker에 의해 관리되고 Host System과 분리된다는 점을 제외한다면, bind mount와 유사하게 동작함
  • 동시에 volume을 여러 Container에 Mount할 수 있음
  • 실행 중인 Container가 volume을 사용하지 않아도, 해당 volume은 Docker에서 계속 사용할 수 있으며, 자동으로 삭제되지 않음
  • docker volume prune을 사용하여 사용하지 않는 volume을 정리할 수 있음
  • volume을 Mount할 때, 이름을 명시적으로 지정하여 사용할 수 있으며, 익명으로도 사용할 수 있음
  • 익명 volume은 처음 Mount될 때 명시적으로 이름이 부여되지 않기 때문에, Docker는 주어진 Docker Host내에서 고유한 임의의 이름을 해당 volume에 부여
  • 이름이 지정된 방식과는 상관 없이 volume은 동일한 방식으로 작동
  • 또한, volume은 원격 Host와 Cloud Provider가 Data를 저장할 수 있는 volume drivers 사용을 지원하고 있음

bind mount

  • bind mount는 Docker 초기부터 사용할 수 있었던 방식으로 volume에 비해 기능이 제한적
  • bind mount를 사용하면, Host System의 파일 또는 디렉토리가 Container에 Mount됨
  • 파일 또는 디렉토리는 Host System의 전체 경로로 참조되지만, 미리 Docker Host에 존재할 필요는 없음
    • 없을 경우, 참조된 경로로 파일 또는 디렉토리가 생성됨
  • bind mount는 매우 효과적이지만, Host Machine의 File System 디렉토리 구조에 의존적
  • Docker CLI 명령어로 bind mount를 관리할 수 없음

tmpfx mount

  • tmpfs mount는 Docker Host 또는 Container내의 디스크에서 Data가 유지되지 않음
  • 비영구적인 상태 정보나 민감 정보들 같이 Container의 생명주기와 맞춰서 Data를 보존하고자 할 때 사용할 수 있음
  • 예를 들어, Docker Cluster인 Swarm Service는 내부적으로 tmps mount를 사용하여 Secret 정보를 Service의 Container에 Mount하여 사용
  • bind mount 및 volume는 -v 또는 --volume Flag를 사용하여 Container에 모두 Mount할 수 있지만, 각 구문은 약간 차이가 있음
    • tmpfs mount의 경우, --tmpfs Flag를 사용합
    • 그러나 Docker 17.06 이상에서는 bind mount, volume, tmpfs mount에 대해 Container와 Service 모두에 --mount Flag를 사용하는 것이 구문이 더 명확이기 때문에 권장

Mount 유형별 사용 사례

volume 사용 사례

  • volume은 Docker Container 및 Service에서 Data를 유지하는 기본적인 방법으로, 다음과 같은 경우에 volume을 사용
  • 실행 중인 여러 Container 간에 Data 공유가 필요한 경우
    • volume생성에 대해서 명시하지 않은 경우, Container에 처음 Mount될 때 생성됨
    • Container가 중지되거나 제거되더라도 생성된 volume은 그대로 유지됨
    • 여러 Container가 동일한 volume을 읽기/쓰기 또는 읽기 전용으로 동시에 Mount할 수 있음
    • volume은 명시적으로 선언하여 제거
  • Docker Host가 특정 디렉토리나 파일 구조를 가질 수 없는 경우
    • volume은 Container Runtime으로부터 Docker Host의 설정을 분리하는데 도움이 됨
  • 로컬 외의 원격 Host 및 Cloud Provider에 Container의 Data를 저장하려는 경우
    • 한 Docker Host에서 다른 Docker Host로 Data를 백업이나 복구 또는 마이그레이션을 하는 경우 volume을 사용하면 더 좋음
    • volume을 사용하여 Container를 중지한 다음 volume의 디렉토리(예시: /var/lib/docker/volume/<volume-name>)를 백업할 수 있음

bind mount 사용 사례

  • 일반적인 경우, 가능하면 bind mount보다 volume를 사용을 권장
    • 다음과 같은 경우에 bind mount를 사용
  • Host Machine에서 Container로 설정 파일을 공유해야하는 경우
    • Docker는 Host Machine의 /etc/resolv.conf를 각 Container에 bind mount하여 DNS Resolution을 제공하고 있음
  • Docker Host 개발 환경과 Container 간 소스코드 또는 빌드된 아티팩트를 공유
    • 예를 들어, Maven의 target/ 디렉토리를 Container에 Mount하고, Docker Host에서 Maven Project를 빌드할 때마다, Container에서 재 작성된 작성된 JAR/WAR에 접근할 수 있음
    • 만약 이런 방식으로 개발을 진행한다면, Production Dockerfile은 bind mount에 의존하지 않고도, Production-Ready 아티팩트를 Image에 직접 복사할 수 있음
  • Docker Host의 파일 또는 디렉토리 구조가 Container가 요구하는 bind mount와 일치하는 경우

volume 또는 bind mounts 사용 시, 유의사항

  • volume 또는 bind mount를 사용하는 경우, 다음의 사항에 대해 유의
  • File 또는 Directory 있는 Container 내의 Directory에 비어있는 volume를 Mount하면, 해당 File 또는 Directory가 volume으로 전달(복사)됨
    • 마찬가지로, 아직 존재하지 않는 특정 volume을 지정하고 Container를 시작하면, 비어있는 volume이 생성됨
    • 이는 다른 Container에 필요한 Data를 미리 갖출 수 있는 좋은 방법
  • File 또는 Directory가 있는 Container 내의 Directory에 bind mount 또는 비어있지 않은 volume를 Mount한다면, Linux Host에서 File을 /mnt에 저장하고 나서 USB 드라이브가 /mnt에 Mount되는 것처럼 기존에 존재하던 File 또는 Directory가 Mount에 의해 가려지게 됨
    • /mnt의 컨텐츠들은 USB 드라이브가 Unmount될 때까지, USB 드라이브의 컨텐츠들에 의해서 가려지게 됨
    • 가려진 파일들은 제거되거나 대체된 것은 아니지만, bind mount 또는 volume이 Mount되어 있는 동안은 접근할 수 없음

tmpfs mount 사용 사례

  • tmpfs mount는 Host System이나 Container 내에서 Data를 유지하지 않아야할 때 가장 적합
    • 이는 보안상의 이유가 될 수도 있고, Application이 많은 양의 비영구적인 상태의 Data를 작성해야 할 때, Container의 성능을 보호하기 위한 것일 수도 있음

Tip! 추가 내용

volume container란?

  • 일반적으로 도커는 컨테이너 내부에 데이터를 관리하므로, 컨테이너가 파기되면 데이터가 모두 날라가게 됨
    • 이는 MySQL 같은 데이터 스토리지를 사용할 경우 위험하게 되는데, 이를 방지하기 위해 따로 볼륨을 설정해서 데이터를 저장해줘야 함
  • 호스트OS 디렉토리를 마운트시켜서 데이터를 관리할 수도 있지만, 호스트쪽 디렉토리에 의존이 생기고 만약 이 디렉토리의 데이터를 잘못 손대면 애플리케이션에 부정적 영향을 미칠 수 있기 때문에 이 방식은 사용하지 않는 것이 좋음
    • 이에 대한 대안으로 추천되는것이 볼륨 컨테이너
  • 볼륨 컨테이너는 말 그대로 데이터를 저장하는 것이 목적인 컨테이너
    • Dockerfile 작성 시 아래와 같이 볼륨을 설정 가능
FROM mysql

VOLUME /var/lib/mysql

# ...
  • 위처럼 작성하게 되면 컨테이너 내의 /var/lib/mysql 디렉터리가 호스트 PC의 /var/lib/docker/volumes/${volume_name}/_data에 마운트 되며 이때 볼륨 이름은 임의의 해쉬값으로 생성
  • 참고로 mac이나 windows의 경우 /var/lib/docker/volumes 디렉토리가 없는데,
    이는 mac이나 windows의 경우 도커를 바로 실행할 수 없으므로 VM을 하나 띄운 뒤, docker를 실행하기 때문
    • 즉, /var/lib/docker/volumes 디렉토리는 mac과 도커 사이에 띄워진 VM 내에 감춰져있음
  • volume container는 볼륨의 이러한 특징을 사용한 것
    • 컨테이너 자체를 볼륨을 관리하도록 만들어서 캡슐화하고, 이를 다른 컨테이너의 볼륨에 매핑해서 결합을 느슨하게 함
FROM busybox # 최소한의 운영체제 기능만 제공

VOLUME /var/lib/mysql
VOLUME /var/log
  • 위와 같은 볼륨 컨테이너를 작성하면 /var/lib/mysql, /var/log 디렉토리에 대한 볼륨 2개가 생성되어 각각 /var/lib/docker/volumes/${volume_name}/_data 에 마운트 됨
  • 볼륨 컨테이너를 띄우면 바로 종료되는데 이렇게 종료된 컨테이너를 사용해도 상관없음
  • 빌드하고 컨테이너로 띄운 뒤 --volumes-from 옵션으로 다른 컨테이너에 연결하면 아래와 같은 형태가 됨
$ docker image build -t volume_container:latest .
$ docker container run -d volume_container:latest

$ docker container run --volumes-from volume_container mysql:5.7
mysql container -> volume_container -> /var/lib/docker/volumes/${/var/lib/mysql's volume_name}/_data
                                    -> /var/lib/docker/volumes/${/var/log's volume_name}/_data

docker-compose.yml 에 volume container 추가

  • 위의 방식처럼 컨테이너를 직접 만들고 다른 컨테이너 실행 시 --volumes-from 속성으로 연결해주는 방법도 있지만, docker-compose를 사용 시 좀 더 간단한 방법을 제공
version: "3"
services:
    test_database:
        image: mysql:5.7
        environment:
          MYSQL_DATABASE: test_db
          MYSQL_ROOT_PASSWORD: root
          MYSQL_ROOT_HOST: '%'
        ports:
          - 3306:3306
        volumes:
          - test_volume:/var/lib/mysql

    test_application:
        build: .
        expose:
          - 8080
        depends_on:
          - test_database

volumes:
    test_volume:
  • 보다시피 test_volume 이라는 볼륨을 생성하고, 사용하는 쪽에서 ${volume_name}:${mount를 원하는 디렉토리} 의 형태로 지정해주면 딤
    • docker-compose 설정 파일 v2의 형태로 보면 좀 더 직관적으로 이해 쉬움
version: "2"
services:
    test_database:
        image: mysql:5.7
        environment:
          MYSQL_DATABASE: test_db
          MYSQL_ROOT_PASSWORD: root
          MYSQL_ROOT_HOST: '%'
        ports:
          - 3306:3306
        volumes:
          - test_volume

    test_application:
        build: .
        expose:
          - 8080
        depends_on:
          - test_database

    test_volume:
        image: busybox
        volumes:
            - /var/lib/mysql
            - /var/log
  • v3의 경우 컨테이너를 따로 생성하지 않아도 된다는 장점이 있음
    • 볼륨 컨테이너는 충분히 좋은 기능이지만, 그래도 범위가 같은 도커 호스트 안이라는 사실은 변하지 않음

Tip! 추가 내용

서로 다른 container가 같은 volume을 공유하는 방법

  • Docker compose를 사용하고 있다면 이미 여러 컨테이너를 사용하고 있다는 뜻
    • 이 여러 컨테이너들은 DB일 수도 있고 웹 애플리케이션일 수도 있음
    • 만약 서로 다른 컨테이너들이 하나의 volume을 공유해야한다면 어떻게 해야할까?
      • docker-compose.yml 설정 파일에서 volumes란 설정을 사용 가능
  • 예시: app이라는 컨테이너와 db라는 컨테이너가 있다고 가정했을 때
# in docker-compose.yml

services:
  app:
    build:
      context: .
    volumes:
      - .:/usr/src/app
    ports:
      - “80:80”
  db:
    build:
      context: ./db
    volumes:
      - .:/var/lib/mysql
    ports:
      - “6630:3306”
  • 위의 예시를 보시면 app과 db 컨테이너 둘 다 volumes 라는 항목이 명시되어 있음
    • 하지만 현재는 같은 volume을 사용하는 것이 아니면 별도의 volume으로 되어있어서 서로 다른 파일 시스템으로 이루어진 상태
  • 이 두 컨테이너가 같은 volume을 사용해야 한다면 먼저 설정 파일 맨 아래에 공유할 volume의 이름을 추가
volumes:
  shared-data:
  • shared-data는 임의로 지정한 이름이며 원하는 이름으로 변경 가능
  • 공유할 volume을 추가하고 app과 db 컨테이너에서 shared-data 볼륨을 사용하도록 지정해주면 됨
services:
  app:
    build:
      context: .
    volumes:
      - shared-data:/usr/src/app
    ports:
      - “80:80”
  db:
    build:
      context: ./db
    volumes:
      - shared-data:/usr/src/mysql
    ports:
      - “6630:3306”
volumes:
  shared-data:
  • services 부분을 보시면 app, db의 volumes 설정에 shared-data를 사용한다고 추가한 것을 확인할 수 있음
    • 그리고 db 컨테이너의 volumes 경로가 이전엔 /var/lib/mysql 이였는데 /usr/src/mysql 로 변경
  • volumes을 공유해서 사용할시 주의할 점은 공유할 볼륨의 이름을 컨테이너에 명시하는 것 뿐만 아니라 경로도 같아야 한다는 점
  • app 컨테이너의 볼륨의 경로가 만약 shared-data:/test/app 처럼 변경된다면 db 컨테이너의 볼륨의 경로도 shared-data:/test/mysql 처럼 바꿔줘야 같은 볼륨을 두 컨테이너에서 동시에 사용할 수 있음
  • docker compose up 으로 두 컨테이너를 실행한 후 docker exec -it container-id bash 명령어로 접근해서 해당 경로로 이동하면 app이라는 이름의 디렉토리와 mysql이라는 이름의 디렉토리가 /test 경로 밑에 생기는 것을 확인할 수 있음
profile
🌱 Backend-Dev | hwaya2828@gmail.com
post-custom-banner

0개의 댓글