k8s中shell脚本启动如何传递信号

  1. 背景
  2. 分析
  3. 解决方案
    1. exec
    2. trap
  4. docker-init

https://qingwave.github.io/docker-shell-signal/

背景

在k8s或docker中,有时候我们需要通过shell来启动程序,但是默认shell不会传递信号(sigterm)给子进程,当在pod终止时应用无法优雅退出,直到最大时间时间后强制退出(kill -9)。

分析

普通情况下,大多业务的启动命令如下

command: [“binary”, “-flags”, …]

主进程做为1号进程会收到sigterm信号,优雅退出(需要程序捕获信号); 而通过脚本启动时,shell作为1号进程,不会显示传递信号给子进程,造成子进程无法优雅退出,直到最大退出时间后强制终止。

解决方案

exec

如何只需一个进程收到信号,可通过execexec会替换当前shell进程,即pid不变

# do something
exec binay -flags …

正常情况测试命令如下,使用sleep来模拟应用sh -c 'echo "start"; sleep 100'
pstree展示如下,sleep进程会生成一个子进程

bash(28701)───sh(24588)───sleep(24589)

通过exec运行后,命令sh -c 'echo "start"; exec sleep 100'

bash(28701)───sleep(24664)

加入exec后,sleep进程替换了shell进程,没有生成子进程

此种方式可以收到信号,但只适用于一个子进程的情况

trap

在shell中可以显示通过trap捕捉信号传递给子进程

echo “start”
binary -flags… &
pid=”$!”

_kill() {
echo “receive sigterm”
kill $pid #传递给子进程
wait $pid
exit 0
}

trap _kill SIGTERM #捕获信号
wait #等待子进程退出

此种方式需要改动启动脚本,显示传递信号给子进程

docker-init

docker-init即在docker启动时加入--init参数,docker-int会作为一号进程,会向子进程传递信号并且会回收僵尸进程。

遗憾的是k8s并不支持--init参数,用户可在镜像中声明init进程,更多可参考container-init

RUN wget -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init\_1.2.2\_amd64
RUN chmod +x /usr/bin/dumb-init
ENTRYPOINT [“/usr/bin/dumb-init”, “-v”, “–”]

CMD [“nginx”, “-g”, “daemon off;”]


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 1209453173@qq.com