问题
放空脑子的时候在告警群里看到了一个拉取镜像失败的报错,Failed|Error: ImagePullBackOff
,当时看到就觉得非常奇怪,这个报错意味着镜像拉取失败,通常原因是
- 网络不通,导致无法访问镜像地址
- 镜像url或者其他信息错误,导致找不到镜像
- 节点故障(IO满了、内存满了等等),导致无法拉取镜像
于是终止休息,开始排查问题。
分析过程
为什么会奇怪呢?
- 网络问题几乎不可能出现,因为我们的节点和镜像仓库都处于内网中,并且节点上架流程有着一套完整的配置,网络绝对是互通的,只有断网这种物理故障才有可能导致无法访问镜像地址
- 节点故障通常也不可能,因为k8s的调度器 + 自定义的调度器会尽量匹配低负载的节点作为服务的运行节点,只有在集群负载非常高的时候才有可能将pod分配给高负载节点,而我们的集群在业务高峰期的时候也不到80%的负载
综上,我的第一感觉是:业务的镜像是不是错了?但是这又有点奇怪,一个成熟的业务基本上都有一套ci/cd
流水线,从提交代码到合并分支到自动生成镜像都有着一套完整的流水线,如果镜像有问题应该在前面就卡住,而不应该是在发布这一步。
查看对应的服务,我发现服务居然已经正常运行了,这说明之前的拉镜像失败问题是偶现问题,之后拉镜像成功并且容器成功启动。
把k8s event
简单看了一遍,前面几条错误没什么价值,分别是ImagePullBackOff
和ErrImagePull
,这也是k8s event
比较糙的一点:在拉取镜像失败的时候大部分情况只会简单的告诉你失败,而不会告诉你失败的原因
最后一条报错大概长这样failed to unmount target /var/lib/containerd/tmpmounts/containerd-mountxxxxxxxx: device or resource busy: unknown
很显然这个就是导致拉取镜像偶现失败的问题,而且很显然是containerd
拉镜像挂卷的问题
排查过程
1.搜索引擎
首先英文搜索一下failed to unmount target device or resource busy: unknown
这个报错,能看到一个github链接,这是一个containerd
社区的issue,点进去能看到有个老哥碰到了同样的问题,并且已经提供了解决方案: 关闭安全软件
显然,这个方案并不能在生产环境中使用,极大的危害了生产环境的安全性,于是我提问他是否知道关闭安全软件是如何解决这个问题的
不过因为开源是松散的社区活动,没人24小时盯着电脑,所以也不用太期待别人把答案喂到你的嘴里,而是自己尝试去解决答案。
2.社区排查
如果社区产生过一个Issue,那么大概率有同类型的issue,在社区的issue列表中搜索关键词device or resource busy: unknown
排查了几个issue之后看到了这么一个Issue,几乎也是一样的报错,提出者在1.3.9
和1.4.4
版本都碰到过这个问题,而我们的版本比这个版本更低,碰到这个问题是不可避免的。
里面有其他的哥们也提到了关闭了安全软件就可以解决这个问题了。
另一个社区成员提出了他对于这个问题的想法
1.这是一个ebusy
类型的错误,containerd/docker
在运行容器时,会在本地文件系统中创建一个层次结构目录,这个目录用于存储和管理镜像、容器和卷的数据
2.安全软件可能会在这些目录创建的时候就开始扫描目录,但是这个时候containerd仍然在使用这个目录,两个进程同时抢占一个设备,导致了ebusy
问题
解决办法
1.升版本,containerd
在1.4版本之后就不会通过本地文件系统去装载镜像
2.把安全软件停了
3.切换容器运行时CRI
,比如cri-o
这类容器运行时和containerd
的实现就不一样,可以避免此类问题
4.修改may_detach_mounts
参数,这是一个Linux内核参数,它决定了一个进程是否有权限去卸载其他进程正在使用的挂载点。如果may_detach_mounts
被设置为1,那么进程就可以卸载其他进程使用的挂载点
最后
综合考虑下上面四个方案,实现起来成本都很大,尤其是2和4可能引入安全性的问题,成本和结果不成正比,还是维持现状比较好(又不是不能用.jpg)
解决了这个问题之后我们再反馈一下结果给一开始提出那个issue
的老哥(虽然他可能已经知道答案了)