[Kubernetes] 为什么有时会出现删除POD之后,需要等待一段时间才能被删掉

正常情况下,执行kubectl delete pod之后,pod一般会立即被删除。但是偶尔会出现这样一种情况,删除pod之后,pod的状态一直显示为Terminating,需要等待一段时间才会被删除,这是什么原因呢?

复制代码
NAME    READY   STATUS        RESTARTS   AGE
nginx   1/1     Terminating   0          4m34s

首先我们来了解一下,删除pod时,k8s做了哪些操作

Typically, with this graceful termination of the pod, kubelet makes requests to the container runtime to attempt to stop the containers in the pod by first sending a TERM (aka. SIGTERM) signal, with a grace period timeout, to the main process in each container. The requests to stop the containers are processed by the container runtime asynchronously. There is no guarantee to the order of processing for these requests. Many container runtimes respect the STOPSIGNAL value defined in the container image and, if different, send the container image configured STOPSIGNAL instead of TERM. Once the grace period has expired, the KILL signal is sent to any remaining processes, and the Pod is then deleted from the API Server

上图为k8s官方文档上的说明,这一大段简单概括起来就是如下两步:

  1. kubelet发送kill 1到pod
  2. 经过terminationGracePeriodSeconds(一般为30s)之后,如果pod还没被删掉,则直接发送kill -9 1强制杀掉进程
graph TD A[kubectl delete pod] --> B{send 'kill -1'} --> C[Terminated] B --> |still alive after some time| D[send 'kill -9 1'] D --> C

至于这里为什么会等待30s,原因如下: k8s pod在结束前可能需要执行一些命令,这些命令可以设置在preStop中进行设置,在删除pod的时候,preStop Hook和SIGTERM 信号并行发生,但是Kubernetes 不会等待 preStop Hook 完成,所以这里需要设置一个等待时间让preStop执行完成之后,在删除pod,这个等待时间就是通过terminationGracePeriodSeconds进行设置的.

但是我们的pod里并没有设置preStop,还是等待了30s pod才彻底被删除

所以这里的问题可能是第1步中, kill 1并没有将进程杀掉, 也就是说进程并没有响应SIGTERM信号

为什么会出现这种情况呢, 进入到容器中看下具体的进程:

bash 复制代码
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:58 ?        00:00:00 bash /start.sh
root         7     1 94 08:58 ?        00:00:13 python3 /server.py

可以看到有两个进程, 其中1为主进程, 7为1的子进程, start.sh内容如下:

bash 复制代码
#!/usr/bin/env bash
python3 /server.py

通过执行shell脚本拉起真正的业务进程, 而且以阻塞方式运行, 删除pod时, 发送的SIGTERM信号不会有任何响应, 因为传递不到子进程7, 无法结束子进程, 进而导致pod无法结束.

这里可能会有这样的疑问, 为什么不直接启动业务进程呢? 这是因为有些场景下, 在启动业务进程之前, 需要进行一些初始化操作, 又不想或者不能通过init-container来完成, 只能通过启动脚本去做, 启动脚本初始化结束之后, 再将业务进程拉起.

如何避免这类问题

  1. 尽量直接启动业务进程, 不要依赖进程拉起业务进程, 初始化操作尽量通过init-container来完成
  2. 在启动脚本里捕获SIGTERM, 并将其传递到子进程
shell 复制代码
#!/usr/bin/env bash

exit_func() {
    pkill python3
    exit
}

trap 'exit_func' SIGTERM

python3 /server.py &

while true
do
    sleep 1
done

如上所示, 将start.sh调整一下, 这样就能将SIGTERM传递到子进程, 让pod快速结束

相关推荐
小马爱打代码17 小时前
SpringBoot:封装 starter
java·spring boot·后端
STARSpace888817 小时前
SpringBoot 整合个推推送
java·spring boot·后端·消息推送·个推
Marktowin18 小时前
玩转 ZooKeeper
后端
蓝眸少年CY19 小时前
(第十二篇)spring cloud之Stream消息驱动
后端·spring·spring cloud
码界奇点19 小时前
基于SpringBoot+Vue的前后端分离外卖点单系统设计与实现
vue.js·spring boot·后端·spring·毕业设计·源代码管理
lindd91191119 小时前
4G模块应用,内网穿透,前端网页的制作第七讲(智能头盔数据上传至网页端)
前端·后端·零基础·rt-thread·实时操作系统·项目复刻
Loo国昌20 小时前
【LangChain1.0】第八阶段:文档处理工程(LangChain篇)
人工智能·后端·算法·语言模型·架构·langchain
vx_bisheyuange20 小时前
基于SpringBoot的海鲜市场系统
java·spring boot·后端·毕业设计
李慕婉学姐21 小时前
【开题答辩过程】以《基于Spring Boot和大数据的医院挂号系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
大数据·spring boot·后端
源代码•宸21 小时前
Leetcode—3. 无重复字符的最长子串【中等】
经验分享·后端·算法·leetcode·面试·golang·string