OverlayFS 스토리지 드라이버
OverlayFS 스토리지 드라이버
OverlayFS는 여러 디렉터리를 하나로 합쳐서 보여주는 유니온 파일시스템이에요. Docker에서는 이 OverlayFS를 기반으로 하는 overlay2 스토리지 드라이버를 주로 사용하는데, 이 문서에서는 그 설정 방법과 동작 원리, 성능 특징, 그리고 호환성 제한 사항까지 하나씩 살펴볼게요.
참고
Docker Engine 29.0 이상에서는 기본적으로 containerd 이미지 저장소를 사용해요.
overlay2드라이버는 레거시 스토리지 드라이버로,overlayfscontainerd 스냅샷터가 그 자리를 대체하고 있어요. 자세한 내용은 스토리지 드라이버 선택 문서를 참고하세요.
참고
fuse-overlayfs드라이버에 대해서는 루트리스 모드 문서를 확인해 주세요.
사전 요구 사항
overlay2 드라이버를 사용하려면 다음 조건을 충족해야 해요.
- Linux 커널 버전 4.0 이상, 또는 RHEL/CentOS에서 커널 3.10.0-514 이상을 사용하는 경우 지원돼요.
overlay2드라이버는xfs백킹 파일시스템에서 지원되지만, 반드시d_type=true가 활성화되어 있어야 해요.xfs_info명령으로ftype옵션이1로 설정되어 있는지 확인하고,xfs파일시스템을 올바르게 포맷하려면-n ftype=1플래그를 사용하세요.- 스토리지 드라이버를 변경하면 기존 컨테이너와 이미지가 로컬 시스템에서 접근 불가능해져요. 드라이버를 변경하기 전에
docker save로 이미지를 저장하거나 Docker Hub 또는 프라이빗 레지스트리에 푸시해 두면 나중에 다시 만들 필요가 없어요.
overlay2 스토리지 드라이버로 Docker 설정하기
이 절차를 진행하기 전에 먼저 위의 사전 요구 사항을 모두 충족해야 해요. 다음 단계를 따라 overlay2 스토리지 드라이버를 구성해 볼게요.
-
Docker를 중지해요.
$ sudo systemctl stop docker -
/var/lib/docker의 내용을 임시 위치에 복사해 둘게요.$ cp -au /var/lib/docker /var/lib/docker.bk -
/var/lib/에서 사용하는 것과 별도의 백킹 파일시스템을 쓰고 싶다면, 파일시스템을 포맷해서/var/lib/docker에 마운트해요. 영구적으로 유지하려면/etc/fstab에 이 마운트를 추가하는 것도 잊지 마세요. -
/etc/docker/daemon.json파일을 편집해요. 없으면 새로 만들면 돼요. 파일이 비어 있다고 가정하고 다음 내용을 추가해 주세요.{ "storage-driver": "overlay2" }daemon.json파일에 유효하지 않은 JSON이 있으면 Docker가 시작되지 않으니 주의하세요. -
Docker를 시작해요.
$ sudo systemctl start docker -
데몬이
overlay2스토리지 드라이버를 사용하는지 확인해 볼게요.docker info명령을 실행하고Storage Driver와Backing filesystem항목을 확인해 보세요.$ docker info Containers: 0 Images: 0 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true <...>
이제 Docker는 overlay2 스토리지 드라이버를 사용하게 되고, 필요한 lowerdir, upperdir, merged, workdir 구조로 overlay 마운트를 자동으로 만들었어요.
계속해서 OverlayFS가 Docker 컨테이너 안에서 어떻게 동작하는지, 성능 조언, 그리고 다양한 백킹 파일시스템과의 호환성 제한 사항에 대해 자세히 알아볼게요.
overlay2 드라이버의 동작 방식
OverlayFS는 하나의 Linux 호스트에 있는 두 개의 디렉터리를 겹쳐서 하나의 디렉터리처럼 보여줘요. 이 디렉터리들을 '레이어'라고 부르고, 이렇게 합치는 과정을 '유니온 마운트'라고 해요. OverlayFS에서는 아래쪽 디렉터리를 lowerdir, 위쪽 디렉터리를 upperdir이라고 부르고, 합쳐진 모습은 merged라는 별도의 디렉터리로 노출돼요.
overlay2 드라이버는 기본적으로 최대 128개의 하위 OverlayFS 레이어를 지원해요. 덕분에 docker build나 docker commit 같은 레이어 관련 Docker 명령의 성능이 좋아지고, 백킹 파일시스템의 inode도 더 적게 사용해요.
이미지와 컨테이너 레이어의 디스크 구조
docker pull ubuntu 명령으로 5개 레이어로 된 이미지를 내려받고 나면, /var/lib/docker/overlay2 아래에 6개의 디렉터리가 보여요.
경고
/var/lib/docker/안의 파일이나 디렉터리를 직접 건드리지 마세요. 이들은 Docker가 관리하는 영역이에요.
$ ls -l /var/lib/docker/overlay2
total 24
drwx------ 5 root root 4096 Jun 20 07:36 223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
drwx------ 3 root root 4096 Jun 20 07:36 3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
drwx------ 5 root root 4096 Jun 20 07:36 4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
drwx------ 5 root root 4096 Jun 20 07:36 e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
drwx------ 5 root root 4096 Jun 20 07:36 eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
drwx------ 2 root root 4096 Jun 20 07:36 l
새로 생긴 l(소문자 L) 디렉터리에는 짧게 줄인 레이어 식별자가 심볼릭 링크로 들어 있어요. 이렇게 짧은 식별자를 쓰는 이유는 mount 명령의 인자에 걸리는 페이지 크기 제한을 피하기 위해서예요.
$ ls -l /var/lib/docker/overlay2/l
total 20
lrwxrwxrwx 1 root root 72 Jun 20 07:36 6Y5IM2XC7TSNIJZZFLJCS6I4I4 -> ../3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 B3WWEFKBG3PLLV737KZFIASSW7 -> ../4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 JEYMODZYFCZFYSDABYXD5MF6YO -> ../eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 NFYKDW6APBCCUCTOUSYDH4DXAT -> ../223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 UL2MW33MSE3Q5VYIKBRN4ZAGQP -> ../e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5/diff
가장 아래쪽 레이어에는 link라는 파일과 diff라는 디렉터리가 있어요. link 파일에는 짧게 줄인 식별자 이름이 들어 있고, diff 디렉터리에는 해당 레이어의 실제 내용이 들어 있어요.
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/
diff link
$ cat /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/link
6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
두 번째로 낮은 레이어부터 그 위의 모든 레이어에는 lower라는 파일과 diff 디렉터리가 있어요. lower 파일은 자신의 부모 레이어를 가리키고, diff 디렉터리에는 자신의 내용이 들어 있어요. 또한 부모 레이어와 자신을 합친 내용을 보여주는 merged 디렉터리와 OverlayFS가 내부적으로 사용하는 work 디렉터리도 있답니다.
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
diff link lower merged work
$ cat /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/lower
l/6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff/
etc sbin usr var
Docker에서 overlay 스토리지 드라이버를 사용할 때 생성되는 마운트를 확인하려면 mount 명령을 사용해요. 아래 출력은 가독성을 위해 일부를 생략했어요.
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/merged
type overlay (rw,relatime,
lowerdir=l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4,
upperdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/diff,
workdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/work)
두 번째 줄의 rw는 overlay 마운트가 읽기-쓰기 가능하다는 뜻이에요.
아래 다이어그램은 Docker 이미지와 컨테이너가 어떻게 레이어로 쌓이는지 보여줘요. 이미지 레이어가 lowerdir이고, 컨테이너 레이어가 upperdir이에요. 이미지에 레이어가 여러 개라면 lowerdir 디렉터리도 여러 개 사용돼요. 그리고 합쳐진 모습은 merged라는 디렉터리로 노출되는데, 사실상 컨테이너의 마운트 지점이 돼요.
이미지 레이어와 컨테이너 레이어에 같은 이름의 파일이 있으면, 컨테이너 레이어(upperdir)가 우선해서 이미지 레이어의 같은 파일을 가려버려요.
컨테이너를 만들 때 overlay2 드라이버는 이미지의 최상위 레이어를 나타내는 디렉터리와 컨테이너용 새 디렉터리를 결합해요. 이미지의 레이어들은 overlay에서 lowerdir 역할을 하며 읽기 전용이에요. 컨테이너용 새 디렉터리는 upperdir이 되고 쓰기가 가능해요.
이미지와 컨테이너 레이어의 디스크 구조 (레거시 overlay 드라이버)
다음 docker pull 명령은 다섯 개 레이어로 구성된 Docker 이미지를 내려받는 모습을 보여줘요.
$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
5ba4f30e5bea: Pull complete
9d7d19c9dc56: Pull complete
ac6ad7efd0f9: Pull complete
e7491a747824: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:46fb5d001b88ad904c5c732b086b596b92cfb4a4840a3abd0e35dbb6870585e4
Status: Downloaded newer image for ubuntu:latest
이미지 레이어
각 이미지 레이어는 /var/lib/docker/overlay/ 안에 자신만의 디렉터리를 갖고 있어요. 그 디렉터리에 레이어의 내용이 들어 있죠. 아래 예시를 보면 알 수 있듯이, 이미지 레이어 ID와 디렉터리 ID는 서로 일치하지 않아요.
경고
/var/lib/docker/안의 파일이나 디렉터리를 직접 건드리지 마세요. 이들은 Docker가 관리하는 영역이에요.
$ ls -l /var/lib/docker/overlay/
total 20
drwx------ 3 root root 4096 Jun 20 16:11 38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8
drwx------ 3 root root 4096 Jun 20 16:11 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
drwx------ 3 root root 4096 Jun 20 16:11 824c8a961a4f5e8fe4f4243dab57c5be798e7fd195f6d88ab06aea92ba931654
drwx------ 3 root root 4096 Jun 20 16:11 ad0fe55125ebf599da124da175174a4b8c1878afe6907bf7c78570341f308461
drwx------ 3 root root 4096 Jun 20 16:11 edab9b5e5bf73f2997524eebeac1de4cf9c8b904fa8ad3ec43b3504196aa3801
이미지 레이어 디렉터리에는 해당 레이어에만 있는 파일과, 하위 레이어와 공유하는 데이터에 대한 하드 링크가 들어 있어요. 이렇게 함으로써 디스크 공간을 효율적으로 사용할 수 있어요.
$ ls -i /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
19793696 /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
$ ls -i /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
19793696 /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
컨테이너 레이어
컨테이너도 Docker 호스트 파일시스템의 /var/lib/docker/overlay/ 아래에 디스크 상으로 존재해요. 실행 중인 컨테이너의 하위 디렉터리를 ls -l로 살펴보면 디렉터리 3개와 파일 1개가 보여요.
$ ls -l /var/lib/docker/overlay2/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root 64 Jun 20 16:39 lower-id
drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged
drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper
drwx------ 3 root root 4096 Jun 20 16:39 work
lower-id 파일에는 컨테이너가 기반으로 하는 이미지의 최상위 레이어 ID가 들어 있어요. 이게 바로 OverlayFS의 lowerdir이에요.
$ cat /var/lib/docker/overlay2/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
upper 디렉터리에는 컨테이너의 읽기-쓰기 레이어 내용이 들어 있어요. OverlayFS의 upperdir에 해당하죠.
merged 디렉터리는 lowerdir과 upperdir의 유니온 마운트로, 실행 중인 컨테이너 내부에서 보는 파일시스템의 모습이에요.
work 디렉터리는 OverlayFS 내부용이에요.
Docker에서 overlay2 스토리지 드라이버를 사용할 때 생성되는 마운트를 확인하려면 mount 명령을 사용해요. 아래 출력은 가독성을 위해 일부를 생략했어요.
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/l/ec444863a55a.../merged
type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/55f1e14c361b.../root,
upperdir=/var/lib/docker/overlay2/l/ec444863a55a.../upper,
workdir=/var/lib/docker/overlay2/l/ec444863a55a.../work)
두 번째 줄의 rw는 overlay 마운트가 읽기-쓰기 가능하다는 뜻이에요.
overlay2에서 컨테이너 읽기와 쓰기 동작 방식
파일 읽기
overlay에서 컨테이너가 파일을 읽기 위해 여는 세 가지 상황을 살펴볼게요.
컨테이너 레이어에 파일이 없는 경우
컨테이너가 파일을 읽기 위해 열었는데 그 파일이 컨테이너(upperdir)에 없다면, 이미지(lowerdir)에서 읽어와요. 이때 성능 오버헤드는 아주 작아요.
컨테이너 레이어에만 파일이 있는 경우
컨테이너가 파일을 읽기 위해 열었는데 그 파일이 컨테이너(upperdir)에는 있고 이미지(lowerdir)에는 없다면, 컨테이너에서 바로 읽어요.
컨테이너 레이어와 이미지 레이어 양쪽에 파일이 있는 경우
컨테이너가 파일을 읽기 위해 열었는데 그 파일이 이미지 레이어와 컨테이너 레이어 양쪽에 있다면, 컨테이너 레이어에 있는 버전을 읽어요. 컨테이너 레이어(upperdir)의 파일이 이미지 레이어(lowerdir)에 있는 같은 이름의 파일을 가리는 거예요.
파일이나 디렉터리 수정
컨테이너 안에서 파일을 수정하는 몇 가지 상황을 생각해 볼게요.
파일에 처음 쓰는 경우
컨테이너가 기존 파일에 처음 쓰기를 하면, 그 파일은 컨테이너(upperdir)에 아직 없어요. 이때 overlay2 드라이버는 copy_up 작업을 수행해서 이미지(lowerdir)에 있는 파일을 컨테이너(upperdir)로 복사해요. 그런 다음 컨테이너는 컨테이너 레이어에 있는 새 복사본에 변경 내용을 쓰는 거예요.
그런데 OverlayFS는 블록 레벨이 아니라 파일 레벨에서 동작해요. 즉, copy_up 작업은 파일이 아무리 크고 일부만 수정하려고 해도 파일 전체를 복사해 버려요. 그래서 컨테이너 쓰기 성능에 눈에 띄는 영향을 줄 수 있어요. 다만 두 가지를 기억해 두면 좋아요.
copy_up작업은 특정 파일에 처음 쓰는 시점에만 발생해요. 그다음부터 같은 파일에 쓰면 이미 컨테이너로 복사된 파일을 대상으로 작업해요.- OverlayFS는 여러 레이어를 사용해요. 그래서 레이어가 많은 이미지에서 파일을 찾을 때 성능 영향이 있을 수 있어요.
파일과 디렉터리 삭제
- 컨테이너 안에서 파일을 삭제하면 컨테이너(
upperdir)에 whiteout 파일이 만들어져요. 이미지 레이어(lowerdir)에 있는 원본 파일은 삭제되지 않아요.lowerdir은 읽기 전용이기 때문이죠. 하지만 whiteout 파일 덕분에 컨테이너에서는 그 파일을 더 이상 볼 수 없어요. - 컨테이너 안에서 디렉터리를 삭제하면 컨테이너(
upperdir)에 opaque 디렉터리가 만들어져요. 이는 whiteout 파일과 비슷하게 동작해서, 이미지(lowerdir)에 디렉터리가 여전히 존재해도 컨테이너에서는 접근할 수 없게 막아줘요.
디렉터리 이름 바꾸기
디렉터리에 rename(2)을 호출할 수 있는 경우는 소스와 대상 경로가 모두 최상위 레이어에 있을 때뿐이에요. 그렇지 않으면 EXDEV 오류("cross-device link not permitted")가 반환돼요. 애플리케이션은 EXDEV를 처리하고 "복사 후 삭제(copy and unlink)" 방식으로 대체할 수 있게 설계해야 해요.
OverlayFS와 Docker 성능
overlay2는 btrfs보다 성능이 더 좋을 수 있어요. 하지만 다음 세부 사항도 염두에 두어야 해요.
페이지 캐시
OverlayFS는 페이지 캐시 공유를 지원해요. 여러 컨테이너가 같은 파일에 접근하면 그 파일에 대한 페이지 캐시 항목 하나를 공유하게 돼요. 덕분에 overlay2 드라이버는 메모리를 효율적으로 사용하고, PaaS처럼 밀도가 높은 사용 사례에 좋은 선택이 될 수 있어요.
Copy-up
다른 copy-on-write 파일시스템과 마찬가지로 OverlayFS도 컨테이너가 파일에 처음 쓸 때 copy-up 작업을 수행해요. 특히 큰 파일이라면 쓰기 작업에 지연이 추가될 수 있어요. 하지만 일단 파일이 복사되고 나면, 이후의 모든 쓰기는 추가 copy-up 없이 upper 레이어에서 일어나요.
성능 모범 사례
OverlayFS에 적용할 수 있는 일반적인 성능 모범 사례는 다음과 같아요.
빠른 스토리지를 사용하세요
SSD는 회전식 디스크보다 읽기와 쓰기가 훨씬 빨라요.
쓰기 작업이 많은 워크로드에는 볼륨을 사용하세요
볼륨은 쓰기 작업이 많은 워크로드에서 가장 좋고 예측 가능한 성능을 제공해요. 스토리지 드라이버를 거치지 않고, 씬 프로비저닝이나 copy-on-write가 만들어내는 잠재적 오버헤드도 없기 때문이에요. 또한 볼륨은 여러 컨테이너 간에 데이터를 공유할 수 있고, 실행 중인 컨테이너가 없어도 데이터를 유지할 수 있는 등 다른 이점도 있어요.
OverlayFS 호환성 제한 사항
OverlayFS가 다른 파일시스템과 호환되지 않는 측면을 정리하면 다음과 같아요.
-
open(2)OverlayFS는 POSIX 표준의 일부만 구현하고 있어요. 그래서 특정 OverlayFS 작업이 POSIX 표준을 위반할 수 있어요. 대표적인 예가 copy-up 작업이에요. 애플리케이션이fd1=open("foo", O_RDONLY)을 호출한 다음fd2=open("foo", O_RDWR)을 호출한다고 가정해 볼게요. 이 경우 애플리케이션은fd1과fd2가 같은 파일을 가리킬 거라고 기대해요. 하지만 두 번째open(2)호출 이후에 발생하는 copy-up 때문에 두 디스크립터는 서로 다른 파일을 가리키게 돼요.fd1은 계속 이미지(lowerdir)의 파일을 참조하고,fd2는 컨테이너(upperdir)의 파일을 참조하죠. 이 문제를 우회하려면touch로 파일을 건드려서 copy-up을 미리 발생시켜 두면 돼요. 그러면 이후의 모든open(2)호출은 읽기 전용이든 읽기-쓰기든 컨테이너(upperdir)의 파일을 참조하게 돼요.yum은yum-plugin-ovl패키지가 설치되어 있지 않으면 이 문제의 영향을 받는 것으로 알려져 있어요. RHEL/CentOS 6.8 이전이나 7.2 이전처럼yum-plugin-ovl패키지를 사용할 수 없는 배포판이라면,yum install을 실행하기 전에touch /var/lib/rpm/*을 실행해야 할 수도 있어요. 이 패키지는 위에서 언급한touch우회 방법을yum에 적용해 주는 역할을 해요. -
rename(2)OverlayFS는rename(2)시스템 호출을 완전히 지원하지 않아요. 애플리케이션은 이 호출의 실패를 감지하고 "복사 후 삭제(copy and unlink)" 전략으로 대체해야 해요.
더 알아보기 (Learn more)
출처: 공식문서