Device Mapper 스토리지 드라이버

Device Mapper 스토리지 드라이버 (사용 중단됨)

Device Mapper 드라이버는 Docker Engine v25.0에서 완전히 제거될 예정이에요. 지금 Device Mapper를 사용 중이라면 업그레이드 전에 지원되는 다른 스토리지 드라이버로 반드시 옮기셔야 해요. 이 문서에서는 devicemapper 스토리지 드라이버가 무엇인지, 어떻게 설정하는지, 그리고 성능을 최적화하는 방법까지 하나씩 차근차근 설명해 드릴게요.

사용 중단(Deprecated) 안내

Device Mapper 드라이버는 공식적으로 사용이 중단되었으며, Docker Engine v25.0에서 제거돼요. Device Mapper를 사용 중이라면 Docker Engine v25.0으로 업그레이드하기 전에 지원되는 스토리지 드라이버로 마이그레이션해야 해요. 지원되는 스토리지 드라이버 목록은 Docker storage drivers 페이지에서 확인할 수 있어요.

Device Mapper는 리눅스의 많은 고급 볼륨 관리 기술을 뒷받침하는 커널 기반 프레임워크예요. Docker의 devicemapper 스토리지 드라이버는 이 프레임워크의 thin provisioning(씬 프로비저닝)과 스냅샷 기능을 활용해서 이미지와 컨테이너를 관리해요. 여기서 용어를 하나 구분하고 갈게요. 문서에서 devicemapper라고 하면 Docker 스토리지 드라이버를 가리키는 거고, Device Mapper라고 하면 커널 프레임워크 자체를 가리키는 거예요.

devicemapper는 지원되는 시스템에서는 리눅스 커널에 포함되어 있지만, Docker에서 사용하려면 별도의 설정이 필요해요. 이 드라이버는 파일 레벨이 아니라 블록 레벨에서 동작하며, Docker 전용 블록 디바이스를 사용해요. 물리 스토리지를 Docker 호스트에 추가해서 확장할 수 있고, OS 레벨에서 파일시스템을 사용하는 것보다 성능이 더 좋아요.

출처: 공식문서

본문

사전 준비사항

  • devicemapper는 Docker Engine - Community에서 CentOS, Fedora, SLES 15, Ubuntu, Debian, RHEL에서 지원돼요.
  • devicemapper를 사용하려면 lvm2device-mapper-persistent-data 패키지가 설치되어 있어야 해요.
  • 스토리지 드라이버를 변경하면 기존에 만들어 둔 컨테이너에 로컬 시스템에서 접근할 수 없게 돼요. docker save로 컨테이너를 저장하고, 기존 이미지는 Docker Hub나 프라이빗 저장소에 푸시해 두세요. 나중에 다시 만들 필요 없도록 말이죠.

devicemapper 스토리지 드라이버 설정하기

설정을 시작하기 전에 위의 사전 준비사항을 모두 충족했는지 먼저 확인하세요.

테스트용 loop-lvm 모드 설정

이 설정은 테스트 전용이에요. loop-lvm 모드는 'loopback' 메커니즘을 사용하는데, 이 메커니즘 덕분에 로컬 디스크의 파일을 실제 물리 디스크나 블록 디바이스처럼 읽고 쓸 수 있어요. 그런데 loopback 메커니즘과 OS 파일시스템 레이어 간의 상호작용 때문에 IO 작업이 느리고 리소스를 많이 잡아먹을 수 있어요. loopback 디바이스를 사용하면 레이스 컨디션(race condition)이 발생할 수도 있고요.

하지만 loop-lvm 모드를 먼저 설정해 보면 direct-lvm 모드의 더 복잡한 설정을 시도하기 전에 기본적인 문제들(사용자 공간 패키지 누락, 커널 드라이버 문제 등)을 파악하는 데 도움이 돼요. 그러니까 loop-lvm 모드는 direct-lvm을 설정하기 전에 기초 테스트를 수행할 목적으로만 사용하세요.

프로덕션 시스템이라면 프로덕션용 direct-lvm 모드 설정을 참고하세요.

  1. Docker를 중지하세요.
$ sudo systemctl stop docker
  1. /etc/docker/daemon.json 파일을 편집하세요. 파일이 없다면 새로 만들면 돼요. 파일이 비어 있었다면 다음 내용을 추가하세요.
{
 "storage-driver": "devicemapper"
}

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

daemon.json 파일에 JSON 형식이 잘못되어 있으면 Docker가 시작되지 않으니 주의하세요!

  1. Docker를 시작하세요.
$ sudo systemctl start docker
  1. 데몬이 devicemapper 스토리지 드라이버를 사용하는지 확인해 보세요. docker info 명령을 실행하고 Storage Driver 항목을 찾아보면 돼요.
$ docker info

 Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
 Images: 0
 Server Version: 17.03.1-ce
 Storage Driver: devicemapper
 Pool Name: docker-202:1-8413957-pool
 Pool Blocksize: 65.54 kB
 Base Device Size: 10.74 GB
 Backing Filesystem: xfs
 Data file: /dev/loop0
 Metadata file: /dev/loop1
 Data Space Used: 11.8 MB
 Data Space Total: 107.4 GB
 Data Space Available: 7.44 GB
 Metadata Space Used: 581.6 KB
 Metadata Space Total: 2.147 GB
 Metadata Space Available: 2.147 GB
 Thin Pool Minimum Free Space: 10.74 GB
 Udev Sync Supported: true
 Deferred Removal Enabled: false
 Deferred Deletion Enabled: false
 Deferred Deleted Device Count: 0
 Data loop file: /var/lib/docker/devicemapper/data
 Metadata loop file: /var/lib/docker/devicemapper/metadata
 Library Version: 1.02.135-RHEL7 (2016-11-16)
<...>

이 호스트는 loop-lvm 모드로 실행 중이에요. 그런데 이 모드는 프로덕션 시스템에서 지원되지 않아요. Data loop fileMetadata loop file/var/lib/docker/devicemapper 아래의 파일에 있다는 점에서 확인할 수 있죠. 이건 loopback 마운트된 스파스 파일(sparse file)이에요. 프로덕션 시스템이라면 프로덕션용 direct-lvm 모드 설정을 참고하세요.

프로덕션용 direct-lvm 모드 설정

프로덕션 호스트에서 devicemapper 스토리지 드라이버를 사용하려면 반드시 direct-lvm 모드를 사용해야 해요. 이 모드는 블록 디바이스를 사용해서 thin pool을 만들어요. loopback 디바이스를 사용하는 것보다 빠르고, 시스템 리소스를 더 효율적으로 사용하며, 필요에 따라 블록 디바이스를 확장할 수도 있어요. 다만 loop-lvm 모드보다 설정이 더 필요해요.

사전 준비사항을 충족했다면 아래 단계에 따라 Docker를 direct-lvm 모드로 설정하세요.

⚠️ 경고

스토리지 드라이버를 변경하면 기존에 만들어 둔 컨테이너에 로컬 시스템에서 접근할 수 없게 돼요. docker save로 컨테이너를 저장하고, 기존 이미지는 Docker Hub나 프라이빗 저장소에 푸시해 두세요. 나중에 다시 만들 필요 없도록요!

Docker가 직접 direct-lvm 모드를 설정하도록 하기

Docker가 블록 디바이스를 직접 관리해 주기 때문에 direct-lvm 모드 설정이 훨씬 간단해져요. 이 방법은 Docker를 새로 설치한 환경에서만 적합해요. 블록 디바이스는 하나만 사용할 수 있어요. 여러 개의 블록 디바이스를 사용해야 한다면 수동으로 direct-lvm 모드 설정을 해야 해요. 새로 추가된 설정 옵션들은 다음과 같아요.

옵션 설명 필수? 기본값 예시
dm.directlvm_device direct-lvm으로 설정할 블록 디바이스의 경로 dm.directlvm_device="/dev/xvdf"
dm.thinp_percent 전달된 블록 디바이스에서 스토리지로 사용할 공간의 비율 아니요 95 dm.thinp_percent=95
dm.thinp_metapercent 전달된 블록 디바이스에서 메타데이터 저장용으로 사용할 공간의 비율 아니요 1 dm.thinp_metapercent=1
dm.thinp_autoextend_threshold lvm이 thin pool을 자동 확장할 임계값 (전체 스토리지 공간 대비 비율) 아니요 80 dm.thinp_autoextend_threshold=80
dm.thinp_autoextend_percent 자동 확장이 트리거될 때 thin pool을 증가시킬 비율 아니요 20 dm.thinp_autoextend_percent=20
dm.directlvm_device_force 블록 디바이스에 이미 파일시스템이 있어도 포맷할지 여부. false로 설정하고 파일시스템이 존재하면 오류가 기록되고 파일시스템은 그대로 유지돼요. 아니요 false dm.directlvm_device_force=true

daemon.json 파일을 편집해서 적절한 옵션을 설정한 다음, Docker를 재시작하면 변경 사항이 적용돼요. 아래 daemon.json 설정은 위 표의 모든 옵션을 설정한 예시예요.

{
 "storage-driver": "devicemapper",
 "storage-opts": [
 "dm.directlvm_device=/dev/xdf",
 "dm.thinp_percent=95",
 "dm.thinp_metapercent=1",
 "dm.thinp_autoextend_threshold=80",
 "dm.thinp_autoextend_percent=20",
 "dm.directlvm_device_force=false"
 ]
}

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

Docker를 재시작하면 변경 사항이 적용돼요. Docker가 블록 디바이스 설정 명령을 직접 실행해 주거든요.

⚠️ 경고

Docker가 블록 디바이스를 준비한 후에 이 값들을 변경하는 것은 지원되지 않으며 오류가 발생해요.

그리고 여전히 주기적인 유지보수 작업은 직접 해주셔야 해요.

수동으로 direct-lvm 모드 설정하기

아래 절차는 thin pool로 구성된 논리 볼륨을 만들어 스토리지 풀의 백킹으로 사용하는 방법이에요. /dev/xvdf에 여유 공간이 충분한 블록 디바이스가 있다고 가정할게요. 디바이스 식별자와 볼륨 크기는 환경에 따라 다를 수 있으니, 여러분의 환경에 맞는 값으로 바꿔서 진행하세요. 이 절차는 Docker 데몬이 stopped 상태라고 가정해요.

  1. 사용할 블록 디바이스를 확인하세요. 디바이스는 /dev/ 아래에 있고(예: /dev/xvdf), 호스트에서 실행하는 워크로드의 이미지와 컨테이너 레이어를 저장할 충분한 여유 공간이 있어야 해요. SSD가 이상적이에요.

  2. Docker를 중지하세요.

$ sudo systemctl stop docker
  1. 다음 패키지들을 설치하세요:
  • RHEL / CentOS: device-mapper-persistent-data, lvm2, 그리고 모든 의존성
  • Ubuntu / Debian / SLES 15: thin-provisioning-tools, lvm2, 그리고 모든 의존성
  1. 1단계의 블록 디바이스에 물리 볼륨(physical volume)을 pvcreate 명령으로 생성하세요. /dev/xvdf 자리에 여러분의 디바이스 이름을 넣으면 돼요.

⚠️ 경고

다음 몇 단계는 파괴적인 작업이에요. 올바른 디바이스를 지정했는지 꼭 확인하세요!

$ sudo pvcreate /dev/xvdf

Physical volume "/dev/xvdf" successfully created.
  1. 같은 디바이스에 docker 볼륨 그룹(volume group)을 vgcreate 명령으로 생성하세요.
$ sudo vgcreate docker /dev/xvdf

Volume group "docker" successfully created
  1. thinpoolthinpoolmeta라는 두 개의 논리 볼륨을 lvcreate 명령으로 생성하세요. 마지막 파라미터는 공간이 부족할 때 데이터나 메타데이터의 자동 확장을 위해 남겨두는 여유 공간의 비율이에요. 아래 값들이 권장 값이에요.
$ sudo lvcreate --wipesignatures y -n thinpool docker -l 95%VG

Logical volume "thinpool" created.

$ sudo lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG

Logical volume "thinpoolmeta" created.
  1. lvconvert 명령으로 볼륨들을 thin pool과 thin pool용 메타데이터 저장소로 변환하세요.
$ sudo lvconvert -y \
--zero n \
-c 512K \
--thinpool docker/thinpool \
--poolmetadata docker/thinpoolmeta

WARNING: Converting logical volume docker/thinpool and docker/thinpoolmeta to
thin pool's data and metadata volumes with metadata wiping.
THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)
Converted docker/thinpool to thin pool.
  1. lvm 프로필을 통해 thin pool의 자동 확장을 설정하세요.
$ sudo vi /etc/lvm/profile/docker-thinpool.profile
  1. thin_pool_autoextend_thresholdthin_pool_autoextend_percent 값을 지정하세요.

thin_pool_autoextend_thresholdlvm이 자동 확장을 시도하는 공간 사용 비율이에요 (100 = 비활성화, 권장하지 않음).

thin_pool_autoextend_percent는 자동 확장 시 디바이스에 추가할 공간의 비율이에요 (0 = 비활성화).

아래 예시는 디스크 사용량이 80%에 도달하면 20%의 용량을 추가하는 설정이에요.

activation {
 thin_pool_autoextend_threshold=80
 thin_pool_autoextend_percent=20
}

파일을 저장하세요.

  1. lvchange 명령으로 LVM 프로필을 적용하세요.
$ sudo lvchange --metadataprofile docker-thinpool docker/thinpool

Logical volume docker/thinpool changed.
  1. 논리 볼륨의 모니터링이 활성화되어 있는지 확인하세요.
$ sudo lvs -o+seg_monitor

LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert Monitor
thinpool docker twi-a-t--- 95.00g 0.00 0.01 not monitored

Monitor 열의 출력이 위처럼 not monitored로 나오면 모니터링을 명시적으로 활성화해야 해요. 이 단계를 건너뛰면 적용된 프로필의 설정과 관계없이 논리 볼륨의 자동 확장이 발생하지 않아요.

$ sudo lvchange --monitor y docker/thinpool

sudo lvs -o+seg_monitor 명령을 한 번 더 실행해서 모니터링이 활성화되었는지 다시 확인하세요. Monitor 열에 이제 monitored라고 표시되어야 해요.

  1. 이전에 이 호스트에서 Docker를 실행한 적이 있거나 /var/lib/docker/ 디렉토리가 존재한다면, Docker가 새 LVM 풀을 사용해서 이미지와 컨테이너 내용을 저장할 수 있도록 옮겨두세요.
$ sudo su -
# mkdir /var/lib/docker.bk
# mv /var/lib/docker/* /var/lib/docker.bk
# exit

아래 단계 중 실패해서 복원해야 한다면 /var/lib/docker를 제거하고 /var/lib/docker.bk로 다시 교체하면 돼요.

  1. /etc/docker/daemon.json을 편집하고 devicemapper 스토리지 드라이버에 필요한 옵션을 설정하세요. 파일이 이전에 비어 있었다면 이제 다음 내용이 들어가야 해요:
{
 "storage-driver": "devicemapper",
 "storage-opts": [
 "dm.thinpooldev=/dev/mapper/docker-thinpool",
 "dm.use_deferred_removal=true",
 "dm.use_deferred_deletion=true"
 ]
}
  1. Docker를 시작하세요.

systemd:

$ sudo systemctl start docker

service:

$ sudo service docker start
  1. docker info 명령으로 Docker가 새 구성을 사용하는지 확인해 보세요.
$ docker info

Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ce
Storage Driver: devicemapper
 Pool Name: docker-thinpool
 Pool Blocksize: 524.3 kB
 Base Device Size: 10.74 GB
 Backing Filesystem: xfs
 Data file:
 Metadata file:
 Data Space Used: 19.92 MB
 Data Space Total: 102 GB
 Data Space Available: 102 GB
 Metadata Space Used: 147.5 kB
 Metadata Space Total: 1.07 GB
 Metadata Space Available: 1.069 GB
 Thin Pool Minimum Free Space: 10.2 GB
 Udev Sync Supported: true
 Deferred Removal Enabled: true
 Deferred Deletion Enabled: true
 Deferred Deleted Device Count: 0
 Library Version: 1.02.135-RHEL7 (2016-11-16)
<...>

Docker가 올바르게 설정되었다면 Data fileMetadata file이 비어 있고, 풀 이름이 docker-thinpool로 표시돼요.

  1. 구성이 올바른지 확인한 후에는 이전 구성이 들어 있는 /var/lib/docker.bk 디렉토리를 제거할 수 있어요.
$ sudo rm -rf /var/lib/docker.bk

devicemapper 관리하기

thin pool 모니터링

LVM 자동 확장 기능만 믿으면 안 돼요. 볼륨 그룹이 자동으로 확장되긴 하지만, 볼륨이 가득 찰 수는 있어요. lvslvs -a 명령으로 볼륨의 여유 공간을 모니터링할 수 있어요. Nagios 같은 OS 레벨 모니터링 도구를 사용하는 것도 고려해 보세요.

LVM 로그를 확인하려면 journalctl을 사용하면 돼요:

$ sudo journalctl -fu dm-event.service

thin pool에 반복적인 문제가 발생한다면 /etc/docker/daemon.json에서 dm.min_free_space 스토리지 옵션을 (백분율 값으로) 설정할 수 있어요. 예를 들어 10으로 설정하면 여유 공간이 10%에 도달하거나 그 근처일 때 경고와 함께 작업이 실패하게 돼요. 자세한 내용은 Engine daemon 참조의 스토리지 드라이버 옵션을 확인하세요.

실행 중인 디바이스의 용량 늘리기

실행 중인 thin-pool 디바이스의 풀 용량을 늘릴 수 있어요. 데이터 논리 볼륨이 가득 차고 볼륨 그룹도 최대 용량일 때 유용하죠. 구체적인 절차는 loop-lvm thin pool을 사용하는지 direct-lvm thin pool을 사용하는지에 따라 달라져요.

loop-lvm thin pool 크기 조정

loop-lvm thin pool의 크기를 조정하는 가장 쉬운 방법은 device_tool 유틸리티 사용이에요. 하지만 OS 유틸리티 사용 방법을 사용해도 돼요.

device_tool 유틸리티 사용

커뮤니티에서 기여한 device_tool.go 스크립트가 moby/moby Github 저장소에 있어요. 이 도구를 사용하면 위의 긴 과정을 피해서 loop-lvm thin pool의 크기를 조정할 수 있어요. 이 도구가 항상 동작한다는 보장은 없지만, 어차피 loop-lvm은 프로덕션이 아닌 시스템에서만 사용해야 하니까 괜찮아요.

device_tool을 사용하고 싶지 않다면 thin pool을 수동으로 크기 조정할 수도 있어요.

  1. 도구를 사용하려면 Github 저장소를 클론하고, contrib/docker-device-tool 디렉토리로 이동한 다음, README.md의 지침에 따라 도구를 컴파일하세요.

  2. 도구를 사용하세요. 다음 예시는 thin pool을 200GB로 크기 조정하는 거예요.

$ ./device_tool resize 200GB

OS 유틸리티 사용

device-tool 유틸리티를 사용하고 싶지 않다면 다음 절차로 loop-lvm thin pool을 수동으로 크기 조정할 수 있어요.

loop-lvm 모드에서는 데이터 저장용 loopback 디바이스 하나와 메타데이터 저장용 loopback 디바이스 하나를 사용해요. loop-lvm 모드는 성능과 안정성에 심각한 단점이 있기 때문에 테스트용으로만 지원된답니다.

loop-lvm 모드를 사용 중이라면 docker info 출력에 Data loop fileMetadata loop file의 파일 경로가 표시돼요:

$ docker info |grep 'loop file'

 Data loop file: /var/lib/docker/devicemapper/data
 Metadata loop file: /var/lib/docker/devicemapper/metadata

다음 단계에 따라 thin pool의 크기를 늘려보세요. 이 예시에서는 thin pool이 100GB이고 200GB로 늘린다고 가정할게요.

  1. 디바이스들의 크기를 확인하세요.
$ sudo ls -lh /var/lib/docker/devicemapper/

total 1175492
-rw------- 1 root root 100G Mar 30 05:22 data
-rw------- 1 root root 2.0G Mar 31 11:17 metadata
  1. truncate 명령으로 data 파일의 크기를 200G로 늘리세요. truncate는 파일 크기를 늘리거나 줄이는 데 사용돼요. 크기를 줄이는 것은 파괴적인 작업이라는 점 명심하세요!
$ sudo truncate -s 200G /var/lib/docker/devicemapper/data
  1. 파일 크기가 변경되었는지 확인하세요.
$ sudo ls -lh /var/lib/docker/devicemapper/

total 1.2G
-rw------- 1 root root 200G Apr 14 08:47 data
-rw------- 1 root root 2.0G Apr 19 13:27 metadata
  1. loopback 파일은 디스크에서는 변경되었지만 메모리에는 아직 반영되지 않았어요. 메모리의 loopback 디바이스 크기를 GB 단위로 확인하고, 리로드한 다음 다시 크기를 확인하세요. 리로드 후에는 200GB가 되어 있어요.
$ echo $[ $(sudo blockdev --getsize64 /dev/loop0) / 1024 / 1024 / 1024 ]

100

$ sudo losetup -c /dev/loop0

$ echo $[ $(sudo blockdev --getsize64 /dev/loop0) / 1024 / 1024 / 1024 ]

200
  1. devicemapper thin pool을 리로드하세요.

a. 먼저 풀 이름을 확인하세요. 풀 이름은 :로 구분된 첫 번째 필드예요. 다음 명령으로 추출할 수 있어요.

$ sudo dmsetup status | grep ' thin-pool ' | awk -F ': ' {'print $1'}
docker-8:1-123141-pool

b. thin pool의 device mapper 테이블을 덤프하세요.

$ sudo dmsetup table docker-8:1-123141-pool
0 209715200 thin-pool 7:1 7:0 128 32768 1 skip_block_zeroing

c. 출력의 두 번째 필드로 thin pool의 총 섹터 수를 계산하세요. 이 숫자는 512-k 섹터로 표현돼요. 100G 파일은 209715200개의 512-k 섹터를 가져요. 이 숫자를 200G에 맞게 두 배로 늘리면 419430400개의 512-k 섹터가 돼요.

d. 다음 세 개의 dmsetup 명령으로 새 섹터 번호를 사용해 thin pool을 리로드하세요.

$ sudo dmsetup suspend docker-8:1-123141-pool
$ sudo dmsetup reload docker-8:1-123141-pool --table '0 419430400 thin-pool 7:1 7:0 128 32768 1 skip_block_zeroing'
$ sudo dmsetup resume docker-8:1-123141-pool

direct-lvm thin pool 크기 조정

direct-lvm thin pool을 확장하려면 먼저 새 블록 디바이스를 Docker 호스트에 연결하고, 커널이 할당한 이름을 확인해야 해요. 이 예시에서는 새 블록 디바이스가 /dev/xvdg라고 가정할게요.

다음 절차에 따라 direct-lvm thin pool을 확장하세요. 여러분의 블록 디바이스와 기타 파라미터는 상황에 맞게 바꿔서 진행하면 돼요.

  1. 볼륨 그룹에 대한 정보를 수집하세요.

pvdisplay 명령으로 thin pool이 현재 사용 중인 물리 블록 디바이스와 볼륨 그룹 이름을 확인할 수 있어요.

$ sudo pvdisplay |grep 'VG Name'

PV Name /dev/xvdf
VG Name docker

다음 단계에서는 여러분의 블록 디바이스나 볼륨 그룹 이름으로 바꿔서 사용하세요.

  1. 이전 단계에서 확인한 VG Name 블록 디바이스 이름을 사용해서 vgextend 명령으로 볼륨 그룹을 확장하세요.
$ sudo vgextend docker /dev/xvdg

Physical volume "/dev/xvdg" successfully created.
Volume group "docker" successfully extended
  1. docker/thinpool 논리 볼륨을 확장하세요. 이 명령은 자동 확장 없이 볼륨의 100%를 바로 사용해요. 메타데이터 thinpool을 확장하려면 docker/thinpool_tmeta를 사용하면 돼요.
$ sudo lvextend -l+100%FREE -n docker/thinpool

Size of logical volume docker/thinpool_tdata changed from 95.00 GiB (24319 extents) to 198.00 GiB (50688 extents).
Logical volume docker/thinpool_tdata successfully resized.
  1. docker info 출력의 Data Space Available 필드로 새 thin pool 크기를 확인하세요. docker/thinpool_tmeta 논리 볼륨을 확장했다면 Metadata Space Available를 확인하면 돼요.
Storage Driver: devicemapper
 Pool Name: docker-thinpool
 Pool Blocksize: 524.3 kB
 Base Device Size: 10.74 GB
 Backing Filesystem: xfs
 Data file:
 Metadata file:
 Data Space Used: 212.3 MB
 Data Space Total: 212.6 GB
 Data Space Available: 212.4 GB
 Metadata Space Used: 286.7 kB
 Metadata Space Total: 1.07 GB
 Metadata Space Available: 1.069 GB
<...>

재부팅 후 devicemapper 활성화하기

호스트를 재부팅했는데 docker 서비스가 시작에 실패했다면 "Non existing device" 오류를 찾아보세요. 다음 명령으로 논리 볼륨을 다시 활성화해야 해요:

$ sudo lvchange -ay docker/thinpool

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

⚠️ 경고

/var/lib/docker/ 안의 파일이나 디렉토리를 직접 조작하지 마세요! 이 파일과 디렉토리는 Docker가 관리해요.

lsblk 명령으로 운영체제 관점에서 디바이스와 풀을 확인할 수 있어요:

$ sudo lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdf 202:80 0 100G 0 disk
├─docker-thinpool_tmeta 253:0 0 1020M 0 lvm
│ └─docker-thinpool 253:2 0 95G 0 lvm
└─docker-thinpool_tdata 253:1 0 95G 0 lvm
 └─docker-thinpool 253:2 0 95G 0 lvm

mount 명령으로 Docker가 사용하는 마운트 포인트를 확인할 수 있어요:

$ mount |grep devicemapper
/dev/xvda1 on /var/lib/docker/devicemapper type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

devicemapper를 사용하면 Docker는 이미지와 레이어 내용을 thinpool에 저장하고, /var/lib/docker/devicemapper/의 하위 디렉토리에 마운트해서 컨테이너에 노출시켜요.

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

/var/lib/docker/devicemapper/metadata/ 디렉토리에는 Devicemapper 구성 자체와 각 이미지 및 컨테이너 레이어에 대한 메타데이터가 들어 있어요. devicemapper 스토리지 드라이버는 스냅샷을 사용하는데, 이 메타데이터에는 스냅샷에 대한 정보도 포함돼요. 이 파일들은 JSON 형식이에요.

/var/lib/docker/devicemapper/mnt/ 디렉토리에는 각 이미지와 컨테이너 레이어의 마운트 포인트가 있어요. 이미지 레이어의 마운트 포인트는 비어 있지만, 컨테이너의 마운트 포인트는 컨테이너 내부에서 보이는 컨테이너 파일시스템을 보여줘요.

이미지 레이어링과 공유

devicemapper 스토리지 드라이버는 포맷된 파일시스템 대신 전용 블록 디바이스를 사용하고, copy-on-write(CoW) 작업 중 최대 성능을 위해 블록 레벨에서 파일을 처리해요.

스냅샷

devicemapper의 또 다른 핵심 기능은 스냅샷(때로는 thin devicesvirtual devices라고도 불러요)을 사용한다는 점이에요. 각 레이어에서 도입된 차이점을 매우 작고 가벼운 thin pool로 저장해요. 스냅샷은 많은 이점을 제공해요:

  • 컨테이너 간에 공유되는 레이어는 쓰기 가능하지 않은 한 디스크에 한 번만 저장돼요. 예를 들어 alpine을 기반으로 하는 10개의 서로 다른 이미지가 있다면, alpine 이미지와 모든 상위 이미지는 각각 디스크에 한 번만 저장돼요.

  • 스냅샷은 copy-on-write(CoW) 전략의 구현이에요. 즉, 특정 파일이나 디렉토리는 해당 컨테이너에 의해 수정되거나 삭제될 때만 컨테이너의 쓰기 가능한 레이어로 복사돼요.

  • devicemapper는 블록 레벨에서 동작하므로 쓰기 가능한 레이어의 여러 블록을 동시에 수정할 수 있어요.

  • 스냅샷은 표준 OS 레벨 백업 유틸리티로 백업할 수 있어요. /var/lib/docker/devicemapper/를 복사하기만 하면 되죠.

Devicemapper 워크플로우

devicemapper 스토리지 드라이버로 Docker를 시작하면 이미지 및 컨테이너 레이어와 관련된 모든 객체가 /var/lib/docker/devicemapper/에 저장돼요. 이 디렉토리는 하나 이상의 블록 레벨 디바이스(loopback 디바이스(테스트 전용) 또는 물리 디스크)로 뒷받침돼요.

  • *베이스 디바이스(base device)*는 가장 낮은 수준의 객체예요. 바로 thin pool 자체이죠. docker info로 확인할 수 있어요. 파일시스템을 포함하고 있고, 모든 이미지와 컨테이너 레이어의 시작점이 돼요. 베이스 디바이스는 Docker 레이어가 아니라 Device Mapper 구현 세부 사항이에요.

  • 베이스 디바이스와 각 이미지 또는 컨테이너 레이어에 대한 메타데이터는 /var/lib/docker/devicemapper/metadata/에 JSON 형식으로 저장돼요. 이 레이어들은 copy-on-write 스냅샷이라서 부모 레이어와 달라지기 전까지는 비어 있어요.

  • 각 컨테이너의 쓰기 가능한 레이어는 /var/lib/docker/devicemapper/mnt/의 마운트 포인트에 마운트돼요. 각 읽기 전용 이미지 레이어와 중지된 컨테이너마다 빈 디렉토리가 존재해요.

각 이미지 레이어는 그 아래 레이어의 스냅샷이에요. 각 이미지의 최하위 레이어는 풀에 존재하는 베이스 디바이스의 스냅샷이에요. 컨테이너를 실행하면 컨테이너가 기반으로 하는 이미지의 스냅샷이 돼요. 다음 예시는 두 개의 실행 중인 컨테이너가 있는 Docker 호스트를 보여줘요. 첫 번째는 ubuntu 컨테이너이고 두 번째는 busybox 컨테이너예요.

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

파일 읽기

devicemapper에서는 읽기가 블록 레벨에서 발생해요. 아래 다이어그램은 예시 컨테이너에서 단일 블록(0x44f)을 읽는 높은 수준의 프로세스를 보여줘요.

애플리케이션이 컨테이너의 블록 0x44f에 대한 읽기 요청을 해요. 컨테이너는 이미지의 thin 스냅샷이기 때문에 블록을 직접 가지고 있지 않아요. 대신 가장 가까운 부모 이미지에서 해당 블록이 존재하는 위치에 대한 포인터를 가지고 있고, 그곳에서 블록을 읽어와요. 이제 블록이 컨테이너의 메모리에 존재하게 되는 거예요.

파일 쓰기

새 파일 쓰기: devicemapper 드라이버에서 컨테이너에 새 데이터를 쓰는 것은 allocate-on-demand 작업으로 수행돼요. 새 파일의 각 블록은 컨테이너의 쓰기 가능한 레이어에 할당되고 블록이 그곳에 기록돼요.

기존 파일 업데이트: 파일의 관련 블록이 존재하는 가장 가까운 레이어에서 읽혀요. 컨테이너가 파일을 쓸 때 수정된 블록만 컨테이너의 쓰기 가능한 레이어에 기록돼요.

파일 또는 디렉토리 삭제: 컨테이너의 쓰기 가능한 레이어에서 파일이나 디렉토리를 삭제하거나, 이미지 레이어가 부모 레이어에 존재하는 파일을 삭제하면 devicemapper 스토리지 드라이버가 해당 파일이나 디렉토리에 대한 추가 읽기 시도를 가로채서 파일이나 디렉토리가 존재하지 않는다고 응답해요.

파일을 쓰고 나서 삭제하기: 컨테이너가 파일을 썼다가 나중에 삭제하면 모든 작업이 컨테이너의 쓰기 가능한 레이어에서 발생해요. 이 경우 direct-lvm을 사용하면 블록이 해제돼요. 하지만 loop-lvm을 사용하면 블록이 해제되지 않을 수 있어요. 이것도 프로덕션에서 loop-lvm을 사용하지 말아야 하는 또 하나의 이유랍니다.

Device Mapper와 Docker 성능

  • allocate-on-demand 성능 영향:

devicemapper 스토리지 드라이버는 thin pool에서 컨테이너의 쓰기 가능한 레이어로 새 블록을 할당하기 위해 allocate-on-demand 작업을 사용해요. 각 블록은 64KB이므로 이것이 쓰기에 사용되는 최소 공간이에요.

  • Copy-on-write 성능 영향: 컨테이너가 특정 블록을 처음 수정하면 해당 블록이 컨테이너의 쓰기 가능한 레이어에 기록돼요. 이러한 쓰기는 파일 레벨이 아닌 블록 레벨에서 발생하므로 성능 영향이 최소화돼요. 하지만 많은 수의 블록을 쓰면 여전히 성능에 부정적인 영향을 줄 수 있고, 이 시나리오에서는 devicemapper 스토리지 드라이버가 다른 스토리지 드라이버보다 실제로 더 나쁜 성능을 보일 수도 있어요. 쓰기 작업이 많은 워크로드에는 스토리지 드라이버를 완전히 우회하는 데이터 볼륨을 사용해야 해요.

성능 모범 사례

devicemapper 스토리지 드라이버를 사용할 때 성능을 최대화하려면 다음 사항들을 꼭 기억하세요.

  • direct-lvm 사용: loop-lvm 모드는 성능이 좋지 않으므로 프로덕션에서 절대 사용하면 안 돼요.

  • 빠른 스토리지 사용: 솔리드 스테이트 드라이브(SSD)는 스피닝 디스크보다 읽기와 쓰기가 훨씬 빨라요.

  • 메모리 사용량: devicemapper는 다른 일부 스토리지 드라이버보다 더 많은 메모리를 사용해요. 실행되는 각 컨테이너는 같은 파일의 몇 블록이 동시에 수정되는지에 따라 파일의 한 개 이상의 복사본을 메모리에 로드해요. 메모리 압박 때문에 devicemapper 스토리지 드라이버는 고밀도 사용 사례의 특정 워크로드에는 적합하지 않을 수 있어요.

  • 쓰기 작업이 많은 워크로드에는 볼륨 사용: 볼륨은 쓰기 작업이 많은 워크로드에 가장 좋고 예측 가능한 성능을 제공해요. 스토리지 드라이버를 우회하고 thin provisioning과 copy-on-write로 인한 잠재적 오버헤드가 없기 때문이에요. 볼륨은 컨테이너 간 데이터 공유, 실행 중인 컨테이너가 없어도 유지되는 영속성 등의 다른 이점도 있어요.

참고

devicemapperjson-file 로그 드라이버를 사용할 때 컨테이너가 생성하는 로그 파일은 여전히 Docker의 dataroot 디렉토리(기본값 /var/lib/docker)에 저장돼요. 컨테이너가 많은 로그 메시지를 생성하면 디스크 사용량이 증가하거나 디스크가 가득 차서 시스템을 관리하지 못할 수 있어요. 로그 드라이버를 구성해서 컨테이너 로그를 외부에 저장할 수 있답니다.

더 알아보기