事故起因
版本說明:本文中docker版本主要基於1.10版本,操作系統為centos7。devicemapper在文中縮寫為dm。
某個用戶的容器啟動不起來,啟動時候一直報錯。通過docker log查看日志,可以看到報錯信息如下
Timestamp: 2019-04-01 16:19:26.33690413 +0800 CST
Code: System error
Message: can't create pivot_root dir , error mkdir /export/docker/devicemapper/mnt/7a35b08cc8815ef439dfcc74fd375e2fbb618208674d6eff1ea5d0b61cc7ef1c/rootfs/.pivot_root492082001: no space left on device
對照docker源碼,可以看到報錯位置,就是在容器啟動時候,docker會在容器中創建一個臨時文件夾。而由於空間不足,因此導致報錯,容器無法啟動。
pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root")
if err != nil {
return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err)
}
初步可以判斷是由於用戶將dm的根目錄磁盤寫滿了導致。那么要解決這個問題,其實思路也就很簡單,就是將容器的dm設備手動掛載起來,然后清理冗余文件后,再恢復。那么在docker中,是如何將容器的dm盤進行創建掛載的,筆者對此進行了一下梳理。
docker中容器的dm盤掛載原理
獲取dm設備信息
這里我們假設要模擬的是f34cee76e36a
容器的掛載。
首先進入到docker的存儲目錄,可以從image的層文件夾中根據容器的id,獲取mount-id。
而后從devicemapper/metadata/
文件夾中,根據mount-id,獲取device_id。這里,該容器的device_id為34709。
[root@localhost docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f34cee76e36a xuxinkun/kubesql "kubesql-server" 3 weeks ago Exited (137) 3 hours ago kubesql-server
[root@localhost docker]# cat image/devicemapper/layerdb/mounts/f34cee76e36af13dbda34f78406ebe1c9a426d16e4eaae02055ace6b557b7539/mount-id
dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824
[root@localhost docker]# cat devicemapper/metadata/dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824
{"device_id":34709,"size":10737418240,"transaction_id":844170,"initialized":false,"deleted":false}
先看一下已經創建的dm設備列表和table。
[root@localhost docker]# dmsetup ls
cl-swap (253:1)
cl-root (253:0)
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 (253:4)
cl-home (253:2)
docker-253:2-3693860-pool (253:3)
[root@localhost docker]# dmsetup table
cl-swap: 0 32899072 linear 8:2 2048
cl-root: 0 104857600 linear 8:2 1647560704
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547: 0 20971520 thin 253:3 34719
cl-home: 0 1614659584 linear 8:2 32901120
docker-253:2-3693860-pool: 0 167772160 thin-pool 7:1 7:0 128 32768 1 skip_block_zeroing
這其中主要要關注的是docker-253:2-3693860-pool
,也就是253:3
這個設備。后面的所有的docker容器的dm設備都是從這個pool中分出去的。
從table中可以看到一個已經創建好的,它的table是0 20971520 thin 253:3 34719
。table的格式如下
logical_start_sector num_sectors target_type target_args
開始扇區 扇區數 設備類型 設備參數
20971520的扇區數應與docker啟動時為容器分配的容量相關。
其中,設備參數的34719即為容器對應的device_id。
創建dm設備
那么現在模擬容器掛載,首先創建一個dm設備。
[root@localhost docker]# dmsetup create docker-253:2-3693860-dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824 --table "0 20971520 thin 253:3 34709"
然后可以查看下dm設備。
[root@localhost docker]# dmsetup ls
cl-swap (253:1)
cl-root (253:0)
docker-253:2-3693860-dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824 (253:5)
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 (253:4)
cl-home (253:2)
[root@localhost tmp]# ls /dev/dm-*
/dev/dm-0 /dev/dm-1 /dev/dm-2 /dev/dm-3 /dev/dm-4 /dev/dm-5
可以看到,新增了一個dm-5的設備。
當然,也可以在/dev/mapper下查看
掛載dm設備
現在將/dev/dm-5設備掛載到自己創建了/tmp/test目錄下。
[root@localhost docker]# mount /dev/dm-5 /tmp/test
[root@localhost docker]# ll /tmp/test/
total 4
-rw------- 1 root root 64 Mar 11 15:40 id
drwxr-xr-x 16 root root 274 Apr 2 09:54 rootfs
[root@localhost docker]# ll /tmp/test/rootfs/
total 16
-rw-r--r-- 1 root root 12076 Dec 5 09:37 anaconda-post.log
lrwxrwxrwx 1 root root 7 Dec 5 09:36 bin -> usr/bin
drwxr-xr-x 4 root root 43 Mar 11 17:50 dev
drwxr-xr-x 48 root root 4096 Mar 11 17:50 etc
drwxr-xr-x 3 root root 21 Mar 11 17:38 home
lrwxrwxrwx 1 root root 7 Dec 5 09:36 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Dec 5 09:36 lib64 -> usr/lib64
drwxr-xr-x 2 root root 6 Apr 11 2018 media
drwxr-xr-x 2 root root 6 Apr 11 2018 mnt
drwxr-xr-x 2 root root 6 Apr 11 2018 opt
drwxr-xr-x 2 root root 6 Dec 5 09:36 proc
dr-xr-x--- 4 root root 140 Mar 11 17:37 root
drwxr-xr-x 12 root root 163 Mar 11 17:50 run
lrwxrwxrwx 1 root root 8 Dec 5 09:36 sbin -> usr/sbin
drwxr-xr-x 2 root root 6 Apr 11 2018 srv
drwxr-xr-x 2 root root 6 Dec 5 09:36 sys
drwxrwxrwt 7 root root 132 Mar 11 17:38 tmp
drwxr-xr-x 13 root root 155 Dec 5 09:36 usr
drwxr-xr-x 18 root root 238 Dec 5 09:36 var
可以在目錄下看到rootfs文件夾,該文件夾下即為容器的所有文件。而后找出冗余文件刪除即可。
卸載移除設備
最后清理恢復,卸載目錄,移除dm設備。
[root@localhost docker]# umount /tmp/test
[root@localhost docker]# dmsetup remove docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547
重啟容器后,恢復正常。至此,該問題得以修復。
以上是手動進行容器dm設備的創建掛載流程。其實在docker中,也基本上是這樣來進行操作的。具體代碼可以參考graphdriver下的devmapper中的相關流程。代碼這里就不再一一走讀了。
docker中mnt掛載點不可見的問題
在這里順帶還解釋下docker掛載的一個疑惑,就是docker中dm設備最終都會mount到devicemapper/mnt/{mount-id}/目錄下。
但是在/proc/mounts
下並不可見。這是因為docker daemon單獨創建了一個mount的namespace。
[root@localhost tmp]# ll /proc/1/ns/mnt
lrwxrwxrwx 1 root root 0 Mar 19 19:03 /proc/1/ns/mnt -> mnt:[4026531840]
[root@localhost tmp]# ps -fe|grep docker
root 845 23449 0 17:04 pts/1 00:00:00 grep --color=auto docker
root 23121 1 0 09:09 ? 00:00:38 /usr/bin/docker-current daemon --selinux-enabled --log-driver=journald -H tcp://127.0.0.1:5050 -H unix:///var/run/docker.sock -g /home/docker --storage-opt dm.blkdiscard=false --storage-opt dm.mountopt=nodiscard --storage-opt dm.loopdatasize=80G --storage-opt dm.loopmetadatasize=4G
[root@localhost tmp]# ll /proc/23121/ns/mnt
lrwxrwxrwx 1 root root 0 Apr 2 10:25 /proc/23121/ns/mnt -> mnt:[4026532450]
23121為docker daemon的進程id。可以通過查看該進程的mounts即可看到相應的掛載信息。
[root@localhost tmp]# cat /proc/23121/mounts|grep mnt
/dev/mapper/docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 /home/docker/devicemapper/mnt/e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 xfs rw,relatime,nouuid,attr2,inode64,logbsize=64k,sunit=128,swidth=128,noquota 0 0
當然,也可以通過nsenter切入到docker daemon進程的mount ns下進行查看。
[root@localhost tmp]# nsenter -t 23121 -m mount -l|grep mnt
/dev/mapper/docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 on /home/docker/devicemapper/mnt/e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 type xfs (rw,relatime,nouuid,attr2,inode64,logbsize=64k,sunit=128,swidth=128,noquota)