앞선 포스트에서 UFS와 도커의 파일 시스템과 관련된 내용을 다루었다. 이번 포스트에서는 어떻게 도커에서 UFS의 기능을 구현하는지에 대해 다루도록 한다.
도커는 UFS(Union File System)을 기반으로 read-only의 이미지 레이어(하위)위에 읽고 쓰기가 가능한 얇은(thin) 컨테이너 레이어(상위)를 쌓는 방식으로 파일 시스템을 관리한다.
스토리지 드라이버는 컨테이너 레이어에서 실제로 데이터를 쓰는 작업을 수행한다. 컨테이너 레이어에서 작성된 파일은 컨테이너가 삭제되면 함께 제거된다. 뿐만 아니라, 컨테이너 레이어에서는 파일을 읽고 쓰는 작업은 호스트 머신에서 작성하는 것보다 느리다. 따라서, write-intensive한 데이터베이스 스토리지는 볼륨을 마운트 하는 방식을 사용하는 것이 좋다.
도커에는 다양한 스토리지 드라이버가 존재한다. 각각의 스토리지 드라이버는 서로 다른 구현방식(implementation)을 가지고 있지만, 공통적으로 이미지 레이어를 중첩하여 쌓고 CoW 전략을 기반으로 한다.
도커의 스토리지 드라이버에는 대표적으로 aufs
, overlay
, overlay2
등이 있다. 이러한 스토리지 드라이버에서 CoW 전략은 대략적으로 다음의 과정을 거치게 된다.
컨테이너를 실행하기 위한 기반이 되는 이미지 레이어를 찾는다. 레이어를 탐색하는 과정은 컨테이너와 가까운 상위 레이어에서 하위 레이어 순으로 이루어진다. 이 과정에서 일치하는 중간 단계 레이어를 찾으면 이를 재사용하여 이미지 빌드 과정을 최적화한다.
copy_up
과정을 수행하여 해당 레이어를 컨테이너 레이어의 파일 시스템으로 복사한다.
이 후, 컨테이너에서 이루어지는 쓰기 작업은 컨테이너 레이어에서 수행되며 이미지 레이어에는 아무런 영향을 주지 않는다.
참고로, Btrfs
, ZFS
와 같은 드라이버는 CoW 전략을 다른 방식으로 구현한다.
Note: 쓰기 작업이 빈번한 어플리케이션(ex, DB, MQ)에서는 컨테이너에 데이터를 쓰는 방식 대신
Docker Volume
을 사용하는 것이 좋다.Docker Volume
은 실행되는 컨테이너와 독립적이며 효율적인 I/O를 위해 디자인되었다. 볼륨은 컨테이너들끼리 공유가 가능하며, 컨테이너 레이어의 크기를 불필요하게 증가시키지 않는다.
스토리지 드라이버에 따라 차이를 보이지만, 대개 copy_up
연산은 상당한 오버헤드를 발생시킬 수 있다. copy_up
연산은 하위 레이어의 파일에 대한 수정이 발생할 때 최초 1번만 수행되지만,파일 시스템이 복잡하고, 용량이 클 경우 오버헤드가 상당할 수 있으므로 유의해야 한다.