容器僵尸进程造成集群节点NotReady

问题

集群工作节点频繁NotReady

NAME  STATUS  ROLE  AGE  VERSION
10.9.x.x NotReady <none> 120d v1.13.9

 

查看工作节点/var/log/messages日志,发现PLEG not healthy导致Not become not ready。

Jul 12 03:24:22 hostnamex kubelet:I0712 03:24:22.233328 2653 kubelet.go:1846] skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m7.15341635s ago; threshold is 3m0s]
Jul 12 03:24:23 hostnamex kubelet:I0712 03:24:23.731971 2563 setters.go:520] Node vecame not ready:{Type:Ready Status:False LastHeartbeatTime:2020-07-12 -3:24:23.731942491 +0800 CST m=+6020732.425522098 LastTransitionTime:2020-07-12 03:24:23.731942491 +0800 CST m=+6020732.425522098 Reason:KubeletNotReady Message:PLEG is not healthy: pleg was last seen active  3m8.652082872s ago; threshold is 3m0s}

 

排查临近时间点的日志,发现PLEG一直无法获取某一个POD 的状态。

Jul 12 03:21:14 hostnamex kubelet: E0712 03:21:14.079575 2563 generic.go:277] PLEG: pod xxx-xxx-xxx-xxx/xxx-xxx-xxx failed reinspection: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jul 12 03:23:15 hostnamex kubelet: E0712 03:23:15.100271 2563 generic.go:247] PLEG: Ignoring events for pod xxx-xxx-xxx-xxx/xxx-xxx-xxx: rpc error: code = DeadlineExceeded desc = context deadline exceeded

 

执行'kubectl get pod xxx-xxx-xxx-xxx -n xxx-xxx-xxx',发现这个POD不存在。

执行'docker ps | grep xxx-xxx-xxx-xxx',发现容器还在,但是执行'docker inspect <container id>'时命令卡死无响应。

 

查看/var/lib/docker/containers/<container id>/config.v2.json'文件中的Pid字段,获取容器PID。

'ps -ef | grep <PID>' 发现有僵尸进程,并且这些僵尸进程均为sh或su进程。

 

进入用户正常的POD中查看进程,发现用户在同一个容器中运行了多个不同的应用,例如shell,uwsgi,nginx。而容器的原则是应该尽量保持容器功能的单一,把不同功能的应用拆分成多个容器。

并且,用户容器中有多个sh和su进程,其父进程ID为0,并且启动时间比容器启动时间晚了几天。怀疑是用户进入容器执行的命令。

 

经测试,如果用户通过kubectl exec进入容器,会生成父进程ID为0的进程。如果用户正常退出的话,这些进程会自动结束,但如果会话因为超时退出,这些进程则无法自动结束,会遗留在容器中。

 

为什么会话会超时呢?kubectl exec其实是通过kube-api -> kubelet -> docker daemon -> docker 容器执行命令的,kubelet本身有超时限制(--streaming-connection-idle-timeout),默认是4小时。另外,有些平台自行开发了在网页登录容器的功能,这些平台本身也会有超时限制。

 

解决方案

因为用户的容器是通过shell进程启动的,所以容器中的PID 1进程为shell进程,如果shell进程不能正常回收容器中因为kubectl exec遗留下的进程,则可能会造成僵尸进程。

于是建议用户通过dumb-init启动应用。关于dumb-init的介绍及下载可以参考以下链接:

https://github.com/Yelp/dumb-init

https://github.com/Yelp/dumb-init/releases/

 

下载dumb-init二进制文件,在编译镜像时拷贝到镜像中,并且在Dockerfile文件中添加以下内容:

# 设置可执行权限
RUN chmod +x /path/to/dumb-init

ENTRYPOINT ["/path/to/dumb-init", "--"]
CMD ["应用的启动命令"]

 

posted @ 2020-08-08 19:45  雨后彩虹,如此绚烂  阅读(782)  评论(0编辑  收藏  举报