Kubernetes Pod无法删除,Docker: Device is busy问题的解决
2018-02-05
问题发现 #
最近测试环境Kubernetes集群的两台主机重启后,发现这两台主机上(CentOS 7.3)的Pod无法删除。 发现这个问题的经过如下:
某个服务的自动发布触发后,监控系统告警有Pod处于Terminated状态,具体去查看发现前面重启过的两台主机上因为服务发布需要被删掉的Pod一直处于Terminated: ExitCode
状态。因为是测试环境,开始没有特别关注,而是直接使用kubectl delete pod <podname> --namespace=<namspacer> --grace-period=0 --force
尝试将有问题Pod强制删除,结果强制删除不起作用。马上觉得问题可能没有那么简单,于是分别登录到这两台主机上通过docker ps -a
查看对应docker容器的状态,发现这两个Pod的docker容器处于Dead状态。使用docker rm <container id>
,提示Device is Busy
,无法删除。
之前遇到过的相似问题 #
关于Docker: Device is busy
的问题以前在使用docker compose的时候遇到过,当时根据网上的两篇文章找到的解决方案,两篇文章如下:
出现Docker: Device is busy
已经停止的docker容器无法删除,可能是因为存在systemd unit file 中带有 PrivateTmp=true 的 serivce,例如ntpd.service,nginx.service,当时在使用docker comose的时候是因为碰巧那台机器上安装了nginx。nginx的systemd文件中有下面的配置:
1[Service]
2PrivateTmp=true
这会导致nginx运行在私有挂载命名空间,而docker容器在销毁时需要卸载挂载的磁盘信息,此时出现冲突引起销毁docker容器失败。
当把引起这个问题的nginx关闭后,再使用docker rm <container id>
可以将Dead的容器删除,但是当重新启动nginx容器后,后边可能还会出现这个问题。
这个是CentOS/RedHat 3.10.0内核NameSpace的bug,以下是Readhat和docker关于这个bug的记录:
- https://access.redhat.com/errata/RHBA-2017:1620
- https://github.com/moby/moby/issues/22260
- https://github.com/moby/moby/issues/27381
当时的解决方法是在docker的systemd unit file中加入了
1[Service]
2MountFlags=slave
确保docker容器运行在私有 的 mount namespace中,执行systemctl daemon-reload
后重启docker服务。
问题解决 #
根据之前相似问题的解决方法,这次的问题自然想到了可能是某个systemd服务PrivateTmp=true
引起的,但是这次这两台主机上并没有安装nginx,而且nptd服务也没有启动。
对其上已有的systemd文件进行查看,最终终于找到了,前段时间为了测试,将这两台主机上安装并启动了ceph-osd
,/usr/lib/systemd/system/[email protected]
中配置了PrivateTmp=true
。测试将其中一台主机上的ceph-osd停止,去删除对应docker容器,测试可以删除。
于是将这两台主机docker的systemd unit file中都加入了MountFlags=slave
,重启docker服务后,新调度到该主机上的Pod将不会再有问题。
对于遗留的无法删除的Pod和处于Dead状态的Docker容器可以尝试使用如下的方法进行清理:
首先尝试使用docker rm <containerid>
进行经理,一般会得到如下的错误:
1docker rm cb4
2Error response from daemon: driver "overlay" failed to remove root filesystem for cb48a9914c6d9015eef2519f70bed648b070345e5acd271de0a03cb931d78a69: remove /var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/merged: device or resource busy
查看对应的挂载信息:
1grep docker /proc/*/mountinfo | grep /var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/merged
2/proc/168793/mountinfo:3967 3441 0:639 / /var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/merged rw,relatime shared:415 - overlay overlay rw,lowerdir=/var/lib/docker/overlay/125dad94af82485b8ea6cd3808df431f7180033d278bdadcbc21d7d7054678a4/root,upperdir=/var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/upper,workdir=/var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/work
从上面的输出结果可以得出当前168793这个进程,查看这个进程:
1ps -ef | grep 168793
2ntp 168793 1 0 Feb04 ? 00:00:00 /usr/sbin/ntpd -u ntp:ntp -g
这里经确认这个进程是ntpd,重启ntpd后再次尝试使用docke rm <containerid>
删除容器,一般情况下是可以删除的。
当然上面在查看对应的挂载信息时,可以能会列出很多信息,这个时候挨个去找出冲突的进程太繁琐了,可以强制umount无法删除的目录:
1umount -f /var/lib/docker/overlay/cd38b6e94e55a36b43cefe9cb3a74050fdc485e233c72a1fa125277d6b3f630d/merged
之后再重新尝试删除容器,重复上边的过程。
当处于Dead状态的容器清理干净后,使用kubectl将处于Terminating的Pod删除掉即可:
1kubectl get pod -n <namespace> | grep Term | awk '{system("kubectl delete pod "$1" -n <namespace>")}'
后续处理 #
因为我们主机的操作系统大多数都是CentOS 7, 后续将调整部署docker的ansible role,加入MountFlags=slave
,同时在测试环境所有主机中做配置的变更,并在测试环境测试观察。
关于Systemd的MountFlags #
- MountFlags: 配置Systemd服务的Mount Namespace配置。会影响服务进程上下文中挂载点的信息,即服务是否会继承主机上已有的挂载点,以及如果服务运行时执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值为shared、slave和private
- shared:服务与主机共用一个Mount Namespace,会继承主机挂载点,服务挂载或卸载设备时会真实地反映到主机上
- slave:服务使用独立的Mount Namespace,会继承主机挂载点,但服务对挂载点的操作只在自己的Namespace内生效,不会反映到主机上
- private: 服务使用独立的Mount Namespace,在启动时没有任何挂载点,服务对挂载点的操作也不会反映到主机上