BTRFS 스토리지 드라이버

BTRFS 스토리지 드라이버

Btrfs는 리눅스 커널에 포함된 쓰기 시 복사(copy-on-write) 방식의 파일시스템이에요. Docker의 btrfs 스토리지 드라이버는 Btrfs의 블록 단위 연산, 씬 프로비저닝, CoW 스냅샷 같은 고급 기능을 활용해서 이미지와 컨테이너를 효율적으로 관리해 줘요. 여러 개의 물리 블록 디바이스를 하나의 Btrfs 파일시스템으로 묶는 것도 가능하답니다. 이 페이지에서는 Docker의 Btrfs 스토리지 드라이버를 btrfs라고 부르고, 파일시스템 자체는 Btrfs라고 구분해서 설명할게요.

출처: 공식문서

본문

중요 사항

대부분의 경우에는 overlay2 스토리지 드라이버를 사용하는 걸 권장해요. 시스템의 루트 파일시스템이 Btrfs라고 해서 꼭 btrfs 스토리지 드라이버를 써야 하는 건 아니에요.

btrfs 드라이버에는 알려진 문제가 있으니, 자세한 내용은 Moby issue #27653을 참고하세요.

참고

btrfs 스토리지 드라이버는 Docker Engine CE를 SLES, Ubuntu, Debian 시스템에서 사용할 때만 지원돼요.

사전 요구사항

btrfs를 사용하려면 다음 조건을 모두 충족해야 해요.

  • btrfs는 Ubuntu나 Debian 시스템의 Docker Engine에서만 권장돼요.
  • 스토리지 드라이버를 변경하면 기존에 만들어 둔 컨테이너를 로컬 시스템에서 접근할 수 없게 돼요. docker save로 컨테이너를 저장해 두고, 기존 이미지는 Docker Hub나 프라이빗 저장소에 푸시해 두면 나중에 다시 만들 필요가 없어요.
  • btrfs는 물리 디스크 같은 전용 블록 스토리지 디바이스가 필요해요. 이 블록 디바이스를 Btrfs로 포맷한 다음 /var/lib/docker/에 마운트해야 해요. 아래 설정 절차에서 자세히 안내할게요. 참고로 SLES는 기본적으로 / 파일시스템이 Btrfs로 포맷되어 있어서 별도 블록 디바이스가 필요 없지만, 성능을 위해 별도로 구성할 수도 있어요.
  • 커널에 btrfs 지원이 포함되어 있어야 해요. 확인하려면 다음 명령을 실행해 보세요.
$ grep btrfs /proc/filesystems

btrfs
  • 운영체제 수준에서 Btrfs 파일시스템을 관리하려면 btrfs 명령이 필요해요. 명령이 없다면 SLES는 btrfsprogs 패키지, Ubuntu는 btrfs-tools 패키지를 설치하면 돼요.

Docker가 btrfs 스토리지 드라이버를 사용하도록 설정하기

이 절차는 SLES와 Ubuntu에서 본질적으로 동일해요.

  1. Docker를 중지하세요.

  2. /var/lib/docker/의 내용을 백업 위치로 복사한 다음, /var/lib/docker/의 내용을 비우세요.

$ sudo cp -au /var/lib/docker /var/lib/docker.bk
$ sudo rm -rf /var/lib/docker/*
  1. 전용 블록 디바이스를 Btrfs 파일시스템으로 포맷하세요. 이 예시에서는 /dev/xvdf/dev/xvdg 두 개의 블록 디바이스를 사용한다고 가정할게요. 이 작업은 데이터를 모두 지우는 파괴적인 작업이므로 블록 디바이스 이름을 다시 한번 꼭 확인하세요.
$ sudo mkfs.btrfs -f /dev/xvdf /dev/xvdg

Btrfs에는 스트라이핑, RAID 등 더 많은 옵션이 있어요. 자세한 내용은 Btrfs 문서를 참고하세요.

  1. 새 Btrfs 파일시스템을 /var/lib/docker/ 마운트 지점에 마운트하세요. Btrfs 파일시스템을 만들 때 사용한 블록 디바이스 중 아무거나 지정하면 돼요.
$ sudo mount -t btrfs /dev/xvdf /var/lib/docker

참고

재부팅 후에도 마운트가 유지되도록 /etc/fstab에 항목을 추가해 두세요.

  1. /var/lib/docker.bk의 내용을 /var/lib/docker/로 복사하세요.
$ sudo cp -au /var/lib/docker.bk/* /var/lib/docker/
  1. Docker가 btrfs 스토리지 드라이버를 사용하도록 설정하세요. /var/lib/docker/가 이미 Btrfs 파일시스템을 사용 중이더라도 이 설정은 반드시 필요해요. /etc/docker/daemon.json 파일을 편집하거나 새로 만드세요. 새 파일이라면 다음 내용을 추가하면 되고, 기존 파일이라면 키와 값만 추가하되, 끝나는 중괄호(}) 앞의 마지막 줄이 아니라면 줄 끝에 쉼표를 붙이는 걸 잊지 마세요.
{
 "storage-driver": "btrfs"
}

각 스토리지 드라이버의 모든 스토리지 옵션은 daemon 참조 문서에서 확인할 수 있어요.

  1. Docker를 시작하세요. 실행된 후 btrfs가 스토리지 드라이버로 사용되고 있는지 확인해 보세요.
$ docker info

Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ce
Storage Driver: btrfs
 Build Version: Btrfs v4.4
 Library Version: 101
<...>
  1. 모든 게 정상적으로 동작하면 /var/lib/docker.bk 디렉토리를 삭제하세요.

Btrfs 볼륨 관리하기

Btrfs의 장점 중 하나는 파일시스템을 마운트 해제하거나 Docker를 재시작하지 않아도 쉽게 관리할 수 있다는 거예요.

공간이 부족해지면 Btrfs는 약 1GB 단위로 볼륨을 자동으로 확장해요.

Btrfs 볼륨에 블록 디바이스를 추가하려면 btrfs device addbtrfs filesystem balance 명령을 사용하면 돼요.

$ sudo btrfs device add /dev/svdh /var/lib/docker

$ sudo btrfs filesystem balance /var/lib/docker

참고

Docker가 실행 중인 상태에서도 이 작업을 할 수는 있지만 성능이 저하될 수 있어요. Btrfs 파일시스템 밸런싱은 유지보수 시간을 따로 정해서 진행하는 게 좋아요.

btrfs 스토리지 드라이버의 동작 방식

btrfs 스토리지 드라이버는 다른 스토리지 드라이버와 달리 /var/lib/docker/ 전체 디렉토리가 Btrfs 볼륨에 저장된다는 점에서 차이가 있어요.

디스크 상의 이미지 및 컨테이너 레이어

이미지 레이어와 쓰기 가능한 컨테이너 레이어에 대한 정보는 /var/lib/docker/btrfs/subvolumes/에 저장돼요. 이 하위 디렉토리에는 이미지 또는 컨테이너 레이어마다 하나의 디렉토리가 있고, 해당 레이어와 모든 상위 레이어로 구성된 통합 파일시스템이 들어 있어요. 서브볼륨은 기본적으로 쓰기 시 복사 방식이며, 기본 스토리지 풀에서 필요에 따라 공간을 할당받아요. 또한 서브볼륨은 중첩하거나 스냅샷을 만들 수도 있어요. 아래 다이어그램은 4개의 서브볼륨을 보여주는데, 'Subvolume 2'와 'Subvolume 3'은 중첩되어 있고 'Subvolume 4'는 자체 내부 디렉토리 트리를 보여줘요.

이미지의 베이스 레이어만 실제 서브볼륨으로 저장돼요. 나머지 모든 레이어는 스냅샷으로 저장되는데, 해당 레이어에서 도입된 변경 사항만 포함해요. 아래 다이어그램처럼 스냅샷의 스냅샷도 만들 수 있어요.

디스크 상에서 스냅샷은 서브볼륨처럼 보이고 동작하지만, 실제로는 훨씬 작고 공간 효율적이에요. 쓰기 시 복사를 사용해서 저장 효율을 극대화하고 레이어 크기를 최소화하며, 컨테이너의 쓰기 가능 레이어에 대한 쓰기는 블록 단위로 관리돼요. 아래 이미지는 서브볼륨과 그 스냅샷이 데이터를 공유하는 모습을 보여줘요.

최대 효율을 위해 컨테이너에 더 많은 공간이 필요하면 약 1GB 단위로 공간이 할당돼요.

Docker의 btrfs 스토리지 드라이버는 모든 이미지 레이어와 컨테이너를 각각의 Btrfs 서브볼륨 또는 스냅샷으로 저장해요. 이미지의 베이스 레이어는 서브볼륨으로 저장되고, 하위 이미지 레이어와 컨테이너는 스냅샷으로 저장돼요. 아래 다이어그램에서 확인할 수 있어요.

btrfs 드라이버를 사용하는 Docker 호스트에서 이미지와 컨테이너를 생성하는 전체적인 프로세스는 다음과 같아요.

  1. 이미지의 베이스 레이어는 /var/lib/docker/btrfs/subvolumes 아래의 Btrfs 서브볼륨에 저장돼요.
  2. 이후 이미지 레이어는 상위 레이어의 서브볼륨 또는 스냅샷을 기반으로 한 Btrfs 스냅샷으로 저장되며, 이 레이어에서 도입된 변경 사항만 포함해요. 이러한 차이점은 블록 단위로 저장돼요.
  3. 컨테이너의 쓰기 가능 레이어는 최종 이미지 레이어의 Btrfs 스냅샷이며, 실행 중인 컨테이너가 만든 변경 사항을 포함해요. 이러한 차이점 역시 블록 단위로 저장돼요.

btrfs에서 컨테이너 읽기/쓰기 동작 방식

파일 읽기

컨테이너는 이미지의 공간 효율적인 스냅샷이에요. 스냅샷의 메타데이터는 스토리지 풀의 실제 데이터 블록을 가리켜요. 이는 서브볼륨과 동일한 방식이에요. 따라서 스냅샷에 대한 읽기는 서브볼륨에 대한 읽기와 본질적으로 동일하게 동작해요.

파일 쓰기

한 가지 주의할 점은, Btrfs에서 많은 수의 작은 파일을 쓰거나 업데이트하면 성능이 느려질 수 있다는 거예요.

Btrfs에서 컨테이너가 파일을 쓰기 위해 여는 세 가지 시나리오를 살펴볼게요.

새 파일 쓰기

컨테이너에 새 파일을 쓰면 할당 온 디맨드(allocate-on-demand) 작업이 호출되어 컨테이너의 스냅샷에 새 데이터 블록을 할당해요. 그런 다음 파일이 이 새 공간에 기록돼요. 할당 온 디맨드 작업은 Btrfs의 모든 쓰기에 기본적으로 적용되며, 서브볼륨에 새 데이터를 쓰는 것과 동일해요. 결과적으로 컨테이너의 스냅샷에 새 파일을 쓰는 것은 기본 Btrfs 속도로 동작해요.

기존 파일 수정

컨테이너에서 기존 파일을 업데이트하는 것은 쓰기 시 복사 작업이에요. Btrfs 용어로는 redirect-on-write라고 해요. 파일이 현재 존재하는 레이어에서 원본 데이터를 읽고, 수정된 블록만 컨테이너의 쓰기 가능 레이어에 기록돼요. 그런 다음 Btrfs 드라이버는 스냅샷의 파일시스템 메타데이터를 업데이트해서 이 새 데이터를 가리키게 해요. 이 동작은 약간의 오버헤드를 발생시켜요.

파일 또는 디렉토리 삭제

컨테이너가 하위 레이어에 존재하는 파일이나 디렉토리를 삭제하면, Btrfs는 하위 레이어에서 해당 파일이나 디렉토리의 존재를 마스킹해요. 컨테이너가 파일을 생성한 다음 삭제하는 경우에는 이 작업이 Btrfs 파일시스템 자체에서 처리되고 공간은 회수돼요.

Btrfs와 Docker 성능

btrfs 스토리지 드라이버에서 Docker 성능에 영향을 미치는 요소는 여러 가지가 있어요.

참고

이러한 요소 중 상당수는 쓰기 집약적인 워크로드에서 컨테이너의 쓰기 가능 레이어에 데이터를 저장하는 대신 Docker 볼륨을 사용하면 완화될 수 있어요. 하지만 Btrfs의 경우 /var/lib/docker/volumes/가 Btrfs로 백업되지 않는 한 Docker 볼륨도 이러한 단점의 영향을 그대로 받아요.

페이지 캐싱

Btrfs는 페이지 캐시 공유를 지원하지 않아요. 즉, 동일한 파일에 접근하는 각 프로세스가 파일을 Docker 호스트 메모리에 개별적으로 복사해요. 결과적으로 btrfs 드라이버는 PaaS와 같은 고밀도 사용 사례에는 최선의 선택이 아닐 수 있어요.

작은 쓰기

많은 작은 쓰기를 수행하는 컨테이너는 Btrfs 청크 사용 효율이 떨어질 수 있어요. 이 패턴은 짧은 시간에 많은 컨테이너를 시작하고 중지할 때도 자주 발생해요. 이로 인해 Btrfs 파일시스템이 조기에 가득 차서 Docker 호스트에서 공간 부족 상태가 발생할 수 있어요. btrfs filesys show 명령으로 Btrfs 디바이스의 여유 공간을 면밀히 모니터링하세요.

순차 쓰기

Btrfs는 디스크에 쓸 때 저널링 기법을 사용해요. 이는 순차 쓰기 성능에 영향을 미쳐 최대 50%까지 성능을 저하시킬 수 있어요.

단편화

단편화는 Btrfs와 같은 쓰기 시 복사 파일시스템에서 자연스럽게 발생하는 현상이에요. 많은 작은 무작위 쓰기가 이 문제를 더 악화시킬 수 있어요. 단편화는 SSD 사용 시 CPU 스파이크, 회전 디스크 사용 시 헤드 스래싱으로 나타날 수 있고, 두 경우 모두 성능에 해로워요.

Linux 커널 버전이 3.9 이상이라면 Btrfs 볼륨을 마운트할 때 autodefrag 기능을 활성화할 수 있어요. 일부 테스트에서 성능에 부정적인 영향이 있는 것으로 나타났으니, 프로덕션에 배포하기 전에 자신의 워크로드로 먼저 테스트해 보세요.

SSD 성능

Btrfs에는 SSD 미디어를 위한 기본 최적화가 포함되어 있어요. 이 기능을 활성화하려면 -o ssd 마운트 옵션으로 Btrfs 파일시스템을 마운트하면 돼요. 이 최적화는 솔리드 스테이트 미디어에 적용되지 않는 시크 최적화 같은 것을 피함으로써 SSD 쓰기 성능을 향상시켜 줘요.

Btrfs 파일시스템을 자주 밸런싱하세요

cron 작업 같은 운영체제 유틸리티를 사용해서 비수기 시간에 Btrfs 파일시스템을 정기적으로 밸런싱하세요. 이렇게 하면 할당되지 않은 블록을 회수하고 파일시스템이 불필요하게 가득 차는 것을 방지할 수 있어요. 완전히 가득 찬 Btrfs 파일시스템은 추가 물리 블록 디바이스를 추가하지 않는 한 리밸런싱할 수 없어요.

자세한 내용은 Btrfs Wiki를 참고하세요.

빠른 스토리지 사용

솔리드 스테이트 드라이브(SSD)는 회전 디스크보다 훨씬 빠른 읽기와 쓰기 속도를 제공해요.

쓰기 집약적인 워크로드에는 볼륨 사용

볼륨은 쓰기 집약적인 워크로드에서 가장 좋고 예측 가능한 성능을 제공해요. 스토리지 드라이버를 우회하기 때문에 씬 프로비저닝과 쓰기 시 복사로 인한 잠재적 오버헤드가 없기 때문이에요. 또한 볼륨은 컨테이너 간 데이터 공유, 실행 중인 컨테이너가 없어도 데이터 유지 같은 다른 이점도 있어요.

더 알아보기