[Docker] Overlay에 대해서 알아보자. (feat. OverlayFS)
devicemapper 유형의 스토리지 관리방식이 25 버전부터는 deprecate됨에 따라 overlay2 방식적용에 대하여 검토가 필요해졌다. 이에 대하여 Overlay2 스토리지 드라이버에 대해 이해하는 것을 목적으로 둔다.
Background
(1) 운영체제는 효율적인 데이터 관리를 위해 데이터를 조직적으로 저장하고 관리한다. 이를 파일시스템이라 부른다.
(예. ext4, NTFS, XFS)
(2) 여기서 유니온 파일 시스템은 여러 파일 시스템 레이어를 하나로 결합하여 단일 파일 시스템처럼 보여주는 기술이다. 이 기술은 여러 해 동안 존재했지만, 널리 사용되지는 않았다. 그러나 컨테이너 기술에서 유니온 파일 시스템이 채택됨에 따라 AUFS, OverlayFS 등의 유니온 파일 시스템 구현체가 주요 역할을 자리매김하게 되었다.
<그림 1. 유니온 파일시스템>
<그림 2. 유니온 파일시스템 종류>
(3) 여기서 devicemapper는 도커 초기버전에서 많이 사용된 드라이버 유형이었으며, 블록 레벨에서 작동하고 Thin-provisioning 기능을 지원하는 드라이버였다. devicemapper는 주로 최신 스토리지 드라이버를 지원하지 않았던 구형 Linux 커널(3.x)에서 사용되어져 왔으나 커널 4.x의 발전과 overlay2의 광범위한 채택으로 인해 devicemapper를 유지할 필요가 없어지게 되었으며 25버전부터는 더이상 지원되지 않게 되었다.
Device mapper가 Deprecated 되어진 사유는 아래와 같다.
- (성능 문제) 특정 상황에서는 성능이 우수했지만, devicemapper는 블록 레벨 작업으로 인해 쓰기 집약적인 워크로드에서 성능 저하를 겪게된다. 얇은 풀(thin pool)에서 블록을 할당하는 과정이 overlay2에 비해 상당히 느릴 수 있다.
- (높은 메모리 사용량) devicemapper는 수정된 파일의 여러 복사본을 메모리에 로드하는 방식으로 작동한다. 이러한 동작방식은 더 큰 메모리 소비로 야기되어지며, 이는 메모리 효율성이 중요한 고밀도 환경에서 문제가 되었다.
- (복잡한 구성) devicemapper를 설정하려면 논리 볼륨 관리자(LVM)의 물리 장치, 볼륨 그룹, 논리 볼륨 및 씬풀 등을 구성해야 하는 복잡한 과정이 요구되어진다.
Block Level
컴퓨터 저장장치에서 데이터를 처리하는 방식을 설명하는데 이용된다. 저장되어지는 데이터를 block 단위라는 고정된 크기로 나누어 저장하고 처리한다. 도커 관점에서 컨테이너의 이미지가 생성되어지거나 수정되어질 때, 해당 파일의 블록만 생성 및 수정되어지며 효율적인 저장 공간 관리의 장점을 제공해준다.
Thin-provisioning
저장 장치의 용량을 효율적으로 관리하는 기술이다. 실제로 사용되지 않는 저장 공간을 미리 할당하지 않고, 각 컨테이너 Layer에 필요한 공간을 동적할당 방식으로 관리함으로써 저장 장치를 효율적으로 사용할 수 있게 해준다.
실제로 사용된 데이터만큼의 블록을 할당하여 관리하므로 저장 공간을 절약하고 더 많은 컨테이너를 관리할 수 있다는 장점이 있다.
(4) OverlayFS 방식은 간단한 설계 덕분에 성능과 유지 관리 측면에서 유리한 장점을 지녔으며 리눅스 커널 3.18 버전부터 메인라인에 포함되어 졌고 현재는 Docker와 같은 컨테이너화 기술에서 많이 사용되어지고 있다.
OverlayFS
앞서 정의하였듯이 유니온 파일 시스템은 여러 파일 시스템 레이어를 하나로 결합하여 단일 파일 시스템처럼 보여주는 기술이다.
alice_k106 님의 블로그(링크)글에서 직관적으로 이해하기 쉽도록 셀로판지를 비유하여 기술하였다.
유니온 마운트란, 하나의 디렉터리 지점에 여러 개의 디렉터리를 마운트함으로써, 마치 하나의 통합된 디렉터리처럼 보이게 하는 것을 의미한다.
(그림출처: alice_k106 블로그)
여기서 OverlayFS에서는 단일 리눅스 호스트에서 이미지 레이어와 컨테이너 레이어를 하나의 통합 뷰로 제공한다.
이미지 레이어 (Image Layer)
- Docker 이미지의 구성 요소로, 애플리케이션 및 그 의존성을 포함한 정적인 파일 시스템 스냅샷이다.
- 읽기 전용(read-only)이며, 여러 레이어가 쌓여서 하나의 Docker 이미지를 구성한다.
컨테이너 레이어(Container Layer)
- Container Layer는 Docker 컨테이너가 실행될 때 생성되는 쓰기 가능한(writeable) Layer이다.
- Image Layer 위에 위치하며, 컨테이너 실행 중 발생하는 모든 파일 시스템 변경 사항(파일 생성, 삭제, 수정 등)이 기록된다.
이미지 레이어는 영구적으로 존재하며 여러 컨테이너에서 재사용될 수 있는 반면, 컨테이너 레이어는 컨테이너의 수명 동안만 존재하며, 컨테이너가 삭제되면 함께 삭제되어진다.
아래 그림은 Docker 이미지와 컨테이너가 어떻게 레이어링 되는 것을 보여준다. 여기서 이미지 레이어는 "lowerdir"이고, 컨테이너 레이어는 "upperdir"이다. 통합된 뷰는 "merged"라는 디렉토리를 통해 노출되며, 이는 컨테이너의 마운트 포인트이다.
- Image Layer와 Container Layer에서 동일한 파일이 존재하는 경우 Container Layer가 우선순위를 가진다.
여기서, 우리가 새로운 도커 이미지를 생성하는 경우 컨테이너 레이어(upperdir)를 스냅샷으로 만든 뒤 새로운 이미지 레이어로서 추가하는 방식이다. 각 이미지 레이어는 /var/lib/docker/overlay 디렉토리 아래에 자체 디렉토리로 구현된다.
또한, Docker가 이미지의 레이어들 간에 데이터를 효율적으로 공유하기 위해 하드 링크를 사용한다. 다시말해, 동일한 파일이 여러 레이어에 걸쳐 존재하더라도, 실제로는 하나의 파일 데이터만 저장되며, 각 레이어는 이 파일을 참조하는 하드 링크만을 가지게 된다.
Overlay 예시
아래 예시는 ubuntu 이미지를 docker pull 명령어를 통해 다운로드 받는 상황이다. 여기서 다운받은 도커 이미지는 5개의 레이어를 가지고 있음을 시사한다.
$ sudo 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 디렉토리 아래에 자체 디렉토리로 구현된 결과를 확인할 수 있다. (이미지 layer ID와 디렉토리 이름이 일치하지 않는것은 Docker 1.10이후에서는 정상적인 상황이다.)
$ 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
또한, 앞서 언급하였듯이 Docker가 이미지의 레이어들 간에 데이터를 효율적으로 공유하기 위해 하드 링크를 사용하며 동일한 파일을 가리키는 여러 하드 링크는 동일한 inode 번호를 가지게 되어진다.
Inode: 파일의 메타데이터를 저장하는 파일 시스템의 데이터 구조
즉, 하드 링크가 있는 모든 파일 경로는 동일한 inode를 참조하여 동일한 데이터를 가리키게 된다.
아래 예시는 두 파일의 inode 번호가 동일(19793696)하다는 것을 보여주며 두 경로가 동일한 파일 데이터를 가리키고 있다는 것이다.
$ ls -i /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
19793696 /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
$ ls -i /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
19793696 /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
이 결과의 시사점은 Docker의 여러 이미지 레이어가 동일한 파일을 공유하더라도, 하드 링크를 사용함으로써, 동일한 파일 데이터는 실제로 한 번만 디스크에 저장되어진다.
즉, 디스크 공간은 최소한으로 사용하고 중복된 파일저장을 피할 수 있다는 것이다.
Reference
https://github.com/jquast/docker/blob/master/docs/userguide/storagedriver/overlayfs-driver.md
https://blog.naver.com/alice_k106/221530340759
https://jvns.ca/images/overlay.jpeg
https://velog.io/@lom/Docker-overlay2
댓글