k8s容器优雅退出一则研究

本文探讨了在Kubernetes(k8s)环境中,通过脚本启动的业务容器在停止时出现退出缓慢的问题。问题源于启动脚本未正确处理SIGTERM信号,导致业务程序无法快速退出。解决方案包括:在启动脚本中添加信号截获处理,使用`exec`使业务程序成为一号进程,或者采用init容器。文章强调了容器内一号进程的重要性,并建议在复杂场景下使用systemd来管理服务。

在工作中遇到k8s利用脚本启动业务的容器,在停止容器时,总需要最大的停止时间。但直接在容器中启动业务程序,则可以比较快地退出。

同时,在k8s中,用busybox作为镜像启动无限循环停留一秒打印日志的脚本,则清除pod时也遭遇了比较长的时间。根据k8s官网介绍,如果没有提供preStop配置,则直接发送SIGTERM信号,明显启动脚本对于此信号没有处理好。对启动脚本加入截获信号操作,则可以正常退出。

# 主动截获信号
trap "exit" TERM

while True; do
echo "print $(date) ..."
sleep 1
done

由此,推想到经由启动脚本启动的业务程序,已经不是容器的1号进程,所以,SIGTERM信号不能够正常接收到,而且启动脚本屏蔽了SIGTERM信号处理,故退出缓慢!

解决由启动脚本带来的退出缓慢问题 ,存在几个解决办法:

  • 启动脚本增加信号截获处理,启动脚本退出后,会进一步导致子进程业务程序退出
  • 在启动脚本最后启动业务程序的地方,利用exec让业务程序替代启动脚本成为一号进程
  • 分拆为多容器、利用k8s官方推荐的init容器+业务程序容器的办法,让业务程序成为容器的一号进程


在解决办法中,容器内的一号进程是谁影响颇大,而且是否可以正确处理SIGTERM信号也至关重要!

容器内一号进程


如果你是Linux的教徒,则容器内仅提供一种业务是一个源自KISS原则的自然延伸

在Linux程序世界中,经常见到单一职责的进程,和各种Shell小工具。Linux并不倾向于将问题复杂化,而是小而美,控制其简单性。

但是,见到很多文章介绍在docker容器内装入多种服务的介绍,其实,如果是合适的场景,估计,也不是不可以。

但是,基本上来讲,大多数的情况是不需要的!在容器的世界里,利用消息中心、日志收集和持久化存储机制,基本上来讲将类似sshd服务并入容器以提供基础设施服务的需要是非常小的,程序运行的环境和世界已经发生大的变化,所谓“云原生”而已。同时,将sshd纳入到容器后,容器内的一号进程问题由冒了出来,建议的解决方式是采用近些年Linux发行版中常见的systemd作为一号进程,其他需要的服务或业务程序做成service,将其组团启动起来。

anolios docker

FROM openanolis/anolisos:8.2-x86_64
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

在Linux中利用pstree可以观察系统内的1号进程和0号进程,也非常有意思。补充,在CentOS6时代,还并没有0号进程的显现!

在 Kubernetes 中配置边车容器(Sidecar Container)的优雅退出,主要依赖于生命周期钩子(Lifecycle Hooks)和终止宽限期(terminationGracePeriodSeconds)的合理设置。以下是一个详细的配置方法: ### 配置优雅退出的基本机制 Kubernetes 为 Pod 提供了 `terminationGracePeriodSeconds` 参数,默认值为 **30 秒**,表示在发送 SIGTERM 信号后等待容器正常退出的最大时间[^1]。此外,可以使用 `preStop` 生命周期钩子来执行清理操作或延迟容器退出,以确保数据一致性或连接关闭。 ### 示例:为 Sidecar 容器配置优雅退出 ```yaml apiVersion: v1 kind: Pod metadata: name: sidecar-pod spec: containers: - name: main-app image: my-main-app ports: - containerPort: 8080 - name: sidecar image: my-sidecar lifecycle: preStop: exec: # 执行一个脚本或命令,用于优雅关闭 sidecar command: ["sh", "-c", "echo 'Shutting down sidecar gracefully'; sleep 10"] terminationGracePeriodSeconds: 15 ``` 上述配置中: - `preStop` 钩子会在容器被终止前执行,例如模拟一个 10 秒的关闭过程。 - `terminationGracePeriodSeconds` 设置为 15 秒,保证有足够的时间完成 `preStop` 的操作。 - 如果 `preStop` 脚本执行超过该时间,则 Kubernetes 会强制发送 SIGKILL 杀死容器[^2]。 ### 注意事项 - 确保 `preStop` 命令是阻塞型的,否则脚本执行完后容器将立即退出。 - `terminationGracePeriodSeconds` 应大于 `preStop` 所需时间,以避免强制终止。 - 若 Sidecar 容器负责网络代理、日志收集等关键任务,建议增加宽限时间以防止服务中断。 ### 更高级的场景 对于需要与主应用协调退出的场景,可以考虑通过共享卷(emptyDir)传递状态文件,或者利用 initContainer 在主容器退出前进行协调操作。这类方式通常适用于更复杂的微服务架构集成测试或高可用部署需求[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值