今天使用 Kubernetes 发布 Deployment 时 Pod 拉取镜像频繁失败。查看日志发现报错信息如下:
Warning Failed 4m kubelet, 172.16.1.1 Failed to pull image "xxx.xxx.com/<image-name>:<version>": rpc error: code = Unknown desc = failed to register layer: link /data/docker/storage/overlay/48420e2e1cacbf1e5a0766f892556b6b5aed9b86bbc118fb4ab5b1f236bcb55a/root/var/lib/yum/yumdb/o/e23ec6b5d5de041869ec5f3e436685acce4a7354-openssh-server-7.4p1-13.el7_4-x86_64/checksum_type /data/docker/storage/overlay/7545256b7c82ea95260e05f3eb0ac6b4138e9f206febd09f82863f1ef17575da/tmproot525452492/var/lib/yum/yumdb/o/e23ec6b5d5de041869ec5f3e436685acce4a7354-openssh-server-7.4p1-13.el7_4-x86_64/checksum_type: too many links
最初还以为是网络的问题,但出现得实在是太过于频繁。仔细观察发现每次 Pod 拉取镜像失败,只会发生在特定的一台 K8s Node 上 172.16.1.1
。而 Kubernetes 事件中提示的是 too many links
,对问题原因的怀疑开始转向文件系统。
Inode 与硬链接
先验知识请参看文件系统。
Inode 是文件系统描述一段数据内容的核心结构。目录下的文件名/目录名通过指向一个特定的 Inode 来绑定到数据内容上。同一个 Inode 可以有很多很多个与众不同的文件名/目录名,只需要保证这些文件/目录最终指向同一个 Inode,这对应的概念也就是“硬链接”。
文件系统的硬链接数有上限吗?显然是有的,这个链接数总是需要一个字段来存储的。首先 CHECK 下使用的文件系统。
[root@172-16-1-1]$ mount
...
/dev/sdc on /data/docker/storage/overlay type ext4 (rw,relatime,seclabel)
再 CHECK 下出问题的文件,64854,差不多等同于 $2^{16}$。
[root@172-16-1-1]$ ls -alh checksum_type
-rw-r--r--. 64854 root root 64 Fed 2 2018 checksum_type
查查 EXT4 的硬链接上限
/* fs/ext4/ext4.h */
/*
* Maximal count of links to a file
*/
#define EXT4_LINK_MAX 65000
struct ext4_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size_lo; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Inode Change time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks_lo; /* Blocks count */
__le32 i_flags; /* File flags */
/* ... some things omitted ... */
}
i_links_count
字段用来描述 Inode 被引用的次数,类型是 __le16
,小端存储的 16 位数。又有一个宏“最大 EXT4 链接数”,硬编码的上限值被定在了 65000
OK,为什么还差了 146 个硬链呢?按照逻辑应该能够再添加几个。且耐心往下看。
Overlay File System
首先确认下 docker
使用的存储驱动,
[root@172-16-1-1]$ docker info
# ... some thing omitted ...
Storage Driver: overlay
Backing Filesystem: extfs
Supports d_type: true
使用了 Overlay 驱动;overlay 使用硬链接的方式完成镜像的叠加,这部分也算是这个 UnionFS 和 Docker 组合使用下的硬伤了。
最后检查一下这个文件被哪些文件链接吧。
[root@172-16-1-1]$ ls -i checksum_type
34997956 /data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/6e92fab82fc8215308e066f5f984fb4fcf26a404-e2fsprogs-1.42.9-10.el7-x86_64/checksum_type
[root@172-16-1-1]$ cd /data/docker/storage/overlay/
[root@172-16-1-1]$ find . -inum 34997956 | wc
64854
恰好符合之前得到的硬链接数量。最后的最后,就是为什么离上限还差了百余链接数。实际上看一下 find . -inum 34997956
打印出的前几项就能得出结论了。
[root@172-16-1-1]$ find . -inum 34997956
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/6e92fab82fc8215308e066f5f984fb4fcf26a404-e2fsprogs-1.42.9-10.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/50be40805794126a56fecc7f720a00eddbeb0ac4-ebtables-2.0.10-15.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/6afbf327df2e805ec7f41172744151395afefb4b-elfutils-default-yama-scope-0.168-8.el7-noarch/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/cdde997a3984c69574975c4516593744d0578fe5-e2fsprogs-libs-1.42.9-10.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/3e23a7d10536c7794ef133302f2213f3eca11028-elfutils-libs-0.168-8.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/7e621968f73154565600b86be2fbc162cc6e0f47-ethtool-4.8-1.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/838dfbc19277a0435fd7bf182149d624dc4d2ecf-elfutils-libelf-0.168-8.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/e/60ff81da46672c6d0409df1180d5076641c50934-expat-2.1.0-10.el7_3-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/G/5d1d730c5f843277da5723b9d79e05fa10cd8019-GeoIP-1.5.0-11.el7-x86_64/checksum_type
/data/docker/storage/overlay/071d913f222a78d3fd42e371ae506cd44ce134514b80a9045a787f01cad74d30/root/var/lib/yum/yumdb/g/a20c407739d081345b8b2ab34d4d9e5f8e054f65-glib2-2.50.3-3.el7-x86_64/checksum_type
# ... more omitted ...
checksum_type
这个文件在单一镜像中就已经被链接了近两百次,这就意味了一个新的 Layer 的创建必然带来新的200+链接,在创建过程中也就必然失败了。
解决方案?
替掉 overlay,换上 overlay2 是官方最为推荐的方案了。毕竟这个问题是 overlay 设计上引起的问题,是一种必然。
临时性的解决方案就是删除掉不再使用的镜像: docker system prune -a