以拉取镜像失败问题排查为例,谈谈我是如何排查底层问题的

0 阅读4分钟

问题

放空脑子的时候在告警群里看到了一个拉取镜像失败的报错,Failed|Error: ImagePullBackOff,当时看到就觉得非常奇怪,这个报错意味着镜像拉取失败,通常原因是

  • 网络不通,导致无法访问镜像地址
  • 镜像url或者其他信息错误,导致找不到镜像
  • 节点故障(IO满了、内存满了等等),导致无法拉取镜像

于是终止休息,开始排查问题。

分析过程

为什么会奇怪呢?

  1. 网络问题几乎不可能出现,因为我们的节点和镜像仓库都处于内网中,并且节点上架流程有着一套完整的配置,网络绝对是互通的,只有断网这种物理故障才有可能导致无法访问镜像地址
  2. 节点故障通常也不可能,因为k8s的调度器 + 自定义的调度器会尽量匹配低负载的节点作为服务的运行节点,只有在集群负载非常高的时候才有可能将pod分配给高负载节点,而我们的集群在业务高峰期的时候也不到80%的负载

综上,我的第一感觉是:业务的镜像是不是错了?但是这又有点奇怪,一个成熟的业务基本上都有一套ci/cd流水线,从提交代码到合并分支到自动生成镜像都有着一套完整的流水线,如果镜像有问题应该在前面就卡住,而不应该是在发布这一步。

查看对应的服务,我发现服务居然已经正常运行了,这说明之前的拉镜像失败问题是偶现问题,之后拉镜像成功并且容器成功启动。

k8s event简单看了一遍,前面几条错误没什么价值,分别是ImagePullBackOffErrImagePull,这也是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.91.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的老哥(虽然他可能已经知道答案了)