[docker] docker 볼륨 - storage drivers

업데이트:     Updated:

카테고리:

태그:

🎯목표

  • Docker가 이미지를 빌드하는 방법
  • Docker가 이미지를 저장하는 방법
  • Docker가 이미지를 사용하는 방법

스토리지 드라이버 vs 볼륨

스토리지 드라이버
  • Docker는 스토리지 드라이버를 사용하여 이미지 레이어를 저장하고 컨테이너의 쓰기 가능한 레이어에 데이터를 저장합니다.
  • 컨테이너의 쓰기 가능 계층은 컨테이너가 삭제된 후에는 지속되지 않지만 런타임 시 생성되는 임시 데이터를 저장하는 데 적합합니다.
  • 스토리지 드라이버는 공간 효율성에 최적화 되어있어 쓰기 속도는 기본 파일 시스템 성능보다 느립니다. (특히 copy-on-write(COW) 파일 시스템을 사용하는 스토리지 드라이버의 경우)
볼륨
  • 컨테이너의 수명을 넘어 지속되어야 하는 데이터 및 컨테이너 간에 공유되어야 하는 데이터에는 볼륨을 사용해야합니다.
  • 데이터베이스 스토리지와 같은 쓰기 집약적인 애플리케이션은 기존 데이터가 읽기 전용 계층에 있는 경우 성능 오버헤드가 발생할 수 있습니다.
  • 이런 경우 해당 데이터는 볼륨에 저장되도록 해야합니다

이미지와 레이어

Docker 이미지는 일련의 레이어로 구성되며 맨 마지막 레이어를 제외한 각 레이어는 읽기 전용입니다. 다음은 Dockerfile에서 각 명령이 새로운 이미지 계층을 만드는 과정을 보여줍니다.

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
LABEL org.opencontainers.image.authors="org@example.com"
COPY . /app
RUN make /app
RUN rm -r $HOME/.cache
CMD python /app/app.py
  • FROM: ubuntu18.04 이미지 레이어를 가져와 컨테이너의 베이스 이미지로 사용합니다.
  • LABEL: 이미지의 메타데이터만 수정하고 새 계층을 생성하지 않습니다.
  • COPY: Docker 호스트의 현재 디렉터리의 내용을 컨테이너의 /app 디렉터리에 복사하고 결과를 새 계층에 기록합니다.
  • RUN: make 명령을 통해 /app 디렉터리를 빌드하고 결과를 새 계층에 기록합니다.
  • RUN: 두 번째 RUN 명령은 캐시 디렉터리를 제거하고 결과를 새 계층에 기록합니다.
  • CMD: 마지막 CMD 명령은 컨테이너 내에서 실행할 명령을 지정할 이미지의 메타데이터만 수정하고 새로운 이미지 계층을 생성하지 않습니다.

Note:

각 레이어는 이전 레이어와의 차이 집합일 뿐입니다. 파일을 추가하거나 제거하면 새 레이어가 생성됩니다. 위의 예에서 $HMOE/.cache 디렉터리는 제거되지만 이전 레이어에서 계속 사용할 수 있으며 이미지의 전체 크기에 추가됩니다.

이미지 레이어 다이어그램

  • 새 컨테이너를 만들 때 기본 레이어 위에 쓰기 가능한 새 레이어를 추가합니다. 이 계층은 종종 “컨테이너 계층”이라고 합니다.
  • 새 파일을 작성하거나 기존 파일 수정, 삭제와 같이 실행 중인 컨테이너에 대한 모든 변경 사항은 이 쓰기 가능 계층에 기록됩니다.
  • 아래 다이어그램은 ubuntu:15.04 이미지를 기반으로 하는 컨테이너를 보여줍니다.


스토리지 드라이버는 이러한 계층이 서로 상호 작용하는 방식에 대한 세부 정보를 처리합니다. 다양한 스토리지 드라이버를 사용할 수 있으며 상황에 따라 장점과 단점이 있습니다.

쓰기 가능 계층

각 컨테이너에는 쓰기 가능한 계층이 있고 모든 변경 사항은 이 컨테이너 계층에 저장되기 때문에 여러 컨테이너가 동일한 기본 이미지를 공유하면서도 고유한 데이터 상태를 가질 수 있습니다.


Docker는 스토리지 드라이버를 사용하여 기본 이미지 레이어와 쓰기 가능한 컨테이너 계층의 콘텐츠를 관리합니다. 각 스토리지 드라이버는 구현을 다르게 처리하지만 모든 드라이버는 스택 가능한 이미지 레이어와 CoW 전략을 사용합니다.

컨테이너 크기

실행중인 컨테이너의 대략적인 크기를 보려면 docker ps -s 명령을 사용할 수 있습니다. -s 옵션을 덧붙이면 두 개의 새로운 열이 출력됩니다.

  • size: 각 컨테이너의 쓰기 가능한 계층에 사용되는 데이터의 양
  • virtual size: 읽기 전용 이미지 계층의 데이터와 쓰기 가능한 계층에서 사용되는 데이터를 합친 크기입니다. 동일한 이미지에서 시작된 여러 개의 컨테이너는 같은 읽기 전용 계층을 공유하고 실제 디스크에서 사용 중인 크기는 하나의 이미지 크기입니다.
  • 컨테이너와 관련된 sizevirtual size 크기 계산에 포함되지 않는 목록도 있습니다.
    • 로깅 드라이버에 의해 저장되는 로그 파일에 사용되는 디스코 공간
    • 컨테이너에서 사용하는 호스트 볼륨 및 도커 볼륨

이미지 레이어 공유


  1. 두 개의 Dockerfile을 이용하여 두 개의 이미지를 만듭니다.
     # acme/my-base-image:1.0
     FROM alpine
     RUN apk add --no-cache bash
    
     # acme/my-base-final:1.0
     FROM acme/my-base-image:1.0
     COPY . /app
     RUN chmod +x /app/hello.sh
     CMD /app/hello.sh
    
  2. 첫 번째 이미지를 빌드합니다.
     $ docker build -t acme/my-base-image:1.0 -f Dockerfile.base .
    
  3. 두 번째 이미지를 빌드합니다.
     $ docker build -t acme/my-final-iamge:1.0 -f Dockerfile .
    
  4. 이미지의 크기를 확인합니다.
     $ docker images --format "table \t\t"
     IMAGE ID       REPOSITORY            SIZE
     7e378a742edd   acme/my-final-image   9.29MB
     be79c158a01c   acme/my-base-image    9.29MB
    
  5. 각 이미지의 기록을 확인합니다.
     $ docker image history acme/my-base-image:1.0
     IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
     be79c158a01c   3 minutes ago   /bin/sh -c apk add --no-cache bash              2.24MB    
     49176f190c7e   6 weeks ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
     <missing>      6 weeks ago     /bin/sh -c #(nop) ADD file:587cae71969871d3c…   7.05MB
    
     $ docker image history acme/my-final-image:1.0
     IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
     7e378a742edd   3 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/app…   0B        
     a824b61197f6   3 minutes ago   /bin/sh -c chmod +x /app/hello.sh               39B       
     0579d758da9e   3 minutes ago   /bin/sh -c #(nop) COPY dir:7d0c2712efd6bd685…   222B      
     be79c158a01c   4 minutes ago   /bin/sh -c apk add --no-cache bash              2.24MB    
     49176f190c7e   6 weeks ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
     <missing>      6 weeks ago     /bin/sh -c #(nop) ADD file:587cae71969871d3c…   7.05MB
    

Note:

  1. 일부 단계는 크기(0B)가 없으며 메타데이터 전용 변경 사항으로, 이미지 레이어를 생성하지 않고 메타데이터 자체외에 다른 크기를 차지하지 않습니다.
  2. 최종 이미지에는 첫 번째 이미지의 2개 레이어와 두 번째 이미지에 추가된 2개 레이어 모두 포함됩니다.
  3. 처음 두 레이어는 두 이미지에서 동일합니다. 공유 이미지 레이어는 한 번만 저장됩니다. 이미지를 이미지 레지스트리로 pull 하거나 push 하는 경우에도 공유됩니다
  4. 따라서 공유 이미지 레이어는 네트워크 대역폭과 스토리지를 줄일 수 있습니다.

이미지 레이어 크기


  • 컨테이너를 시작하면 쓰기 가능 계층이 이미지 레이어 위에 추가됩니다. 컨테이너에서 이루어지는 대부분의 쓰기 작업은 이 쓰기 가능 계층의 새로운 공간에 쓰여집니다.
  • 컨테이너가 변경하지 않는 파일은 이 쓰기 가능한 계층에 복사되지 않습니다. 이것은 쓰기 가능한 레이어가 가능한 작다는 것을 의미합니다.
  • 컨테이너의 기존 파일이 수정되면 스토리지 드라이버는 CoW 작업을 수행합니다. 관련된 특정 단계는 특정 스토리지 드라이버에 따라 다릅니다. overlay2, overlayaufs 드라이버의 경우 CoW 작업은 대략적으로 다음의 순서를 따릅니다.
    • 업데이트할 파일의 이미지 레이어를 검색합니다. 검색은 최신 레이어에서 시작하여 한 번에 한 레이어씩 이미지 레이어로 내려갑니다.
    • 처음으로 찾은 파일의 복사본을 컨테이너의 쓰기 가능 계층에 복사합니다.
    • 이 파일 사본에 수정이 이루어지며 컨테이너는 하위 계층에 존재하는 파일의 읽기 전용 사본을 볼 수 없습니다.
  • CoW가 작동하는 방식을 이해하기 위해 다음 절차를 참고하세요.
  1. acme/my-final-image:1.0 이미지를 기반으로 하는 5개의 컨테이너를 가동합니다.
     $ docker run -dit --name my_container_1 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_2 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_3 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_4 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_5 acme/my-final-image:1.0 bash
    
  2. 각 컨테이너의 크기를 확인합니다. 이를 통해 각 컨테이너는 이미지 레이어를 공유하고 쓰기 가능 계층에는 아무런 데이터가 추가되지 않았음을 알 수 있습니다.
     $ docker ps --size --format "table \t\t\t"
     CONTAINER ID   IMAGE                     NAMES            SIZE
     0015627b8341   acme/my-final-image:1.0   my_container_5   0B (virtual 9.29MB)
     4af1078b876e   acme/my-final-image:1.0   my_container_4   0B (virtual 9.29MB)
     17c41d4edc4a   acme/my-final-image:1.0   my_container_3   0B (virtual 9.29MB)
     18200fc60af5   acme/my-final-image:1.0   my_container_2   0B (virtual 9.29MB)
     73ebdfc9d5e4   acme/my-final-image:1.0   my_container_1   0B (virtual 9.29MB)
    
  3. “hello” 라는 단어를 my_container_1, my_container_2, my_container_3의 쓰기 가능 계층에 저장합니다.
     $ for in in {1..3}; do docke exec my_container_$i sh -c 'printf hello > /out.txt'; done
    
  4. 다시 각 컨테이너의 크기를 확인합니다. 이를 통해 이미지 레이어는 영향을 받지 않으면 여전히 모든 컨테이너에서 공유되지만 1,2,3 컨테이너에 쓰인 데이터는 고유하며 공유되지 않음을 알 수 있습니다.
     $ docker ps --size --format "table \t\t\t"
     CONTAINER ID   IMAGE                     NAMES            SIZE
     0015627b8341   acme/my-final-image:1.0   my_container_5   0B (virtual 9.29MB)
     4af1078b876e   acme/my-final-image:1.0   my_container_4   0B (virtual 9.29MB)
     17c41d4edc4a   acme/my-final-image:1.0   my_container_3   5B (virtual 9.29MB)
     18200fc60af5   acme/my-final-image:1.0   my_container_2   5B (virtual 9.29MB)
     73ebdfc9d5e4   acme/my-final-image:1.0   my_container_1   5B (virtual 9.29MB)
    

Note:

  1. CoW는 공간을 절약할 뿐만 아니라 컨테이너 시작 시간도 줄여줍니다. 컨테이너를 생성할 때 동일한 이미지 레이어가 이미 저장되어 있다면 Docker는 쓰기 가능 계층만 생성하면 됩니다.
  2. Docker가 새 컨테이너를 생성할 때마다 기본 이미지 스택의 전체 복사본을 만들어야 한다면 컨테이너 생성 시간과 사용되는 디스크 공간이 크게 증가할 것입니다.
  3. 쓰기 작업이 많은 애플리케이션의 경우 컨테이너에 데이터를 저장하면 안됩니다. 데이터베이스와 같은 애플리케이션은 특히 기존 데이터가 읽기 전용 계층에 존재할 때 문제가 될 수있습니다. 이는 CoW 작업을 생각해보면 금방 이해할 수 있습니다.
  4. 컨테이너 계층에 쓰는 대신 Docker 볼륨을 사용하십시오. 볼륨은 컨테이너 간에 공유가 용이할 뿐만아니라 컨테이너의 쓰기 가능 계층 크기를 늘리지 않습니다.

📌출처

Docker Docs


👍 개인 공부 기록용 블로그입니다. 오류나 조언이 있으시면 언제든지 댓글 혹은 메일로 남겨주시면 감사하겠습니다! 😄

맨 위로 이동하기

docker 카테고리 내 다른 글 보러가기

댓글남기기