k8s pod 中的程序为啥服务优雅关闭不生效?收不到 sigterm 信号?

2,234 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

咱们工作的环境在不断的变好,我们也会思考去提升程序运行的环境,让我们的服务更加容易部署,极简维护,现在很多公司都在向着 devpos 发展,殊不知已经被某些大企玩剩下了

不过没关系,路得一步一步的走,饭得一口一口的吃,这样我们才会走的稳,长的好

情况描述:

起初我们还是主机环境的时候,使用 ansible 来一键部署我们主机环境上的服务,对于我们的服务自然也是做无状态的

对于服务我们有做优雅关闭,简单来说,就是当程序收到 sigterm 等关闭信号的时候,咱们的服务不会一下子断掉,服务会将当前手里的事情迅速做完再关闭咱们的服务

例如咱们一般在 golang 里面会这样来实现:

stopChan := make(chan os.Signal)
// bind OS events to the signal channel
signal.Notify(stopChan,  syscall.SIGTERM)

// 优雅关闭咱们的服务
defer func() {
       log.Println("closing....")
       // xxxx 做关闭的动作
       // TODO ...
}()
// 阻塞等待关闭信号
select {
case <-stopChan:
}

相信对于熟悉 golang 的兄弟对于这个已经不再陌生了,这个会有啥问题呢?

当然,对于在主机环境里面我们过去都跑了很久了,优雅关闭的功能都是正常运行的,久而久之,就没有人关注他了

开始应用 k8s 来部署我们的服务

慢慢的我们过渡到了容器化的方式来部署我们的服务

一般使用容器化,我们回去编写 Dockerfile ,写我们的启动脚本,做成镜像,进而做成 helm 包,推到 helm 仓库中,在环境中我们就可以使用 helm 工具来高效的部署咱们的服务了,此处就过多赘述了,感兴趣的可以查看如下 2 篇历史文章:

实际情况上,服务在线上跑了一年多了,最近要做一个需求,涉及到服务被 kill 的时候,要到优雅关闭中做一些事情,例如清空某些过程数据

万万没想到,正是优雅关闭在 k8s 部署的时候出了问题,还记当刚才我们说到的在 k8s 中部署的时候,咱们会写启动脚本吗?,例如启动脚本是这样的:

start.sh

#!/bin/bash
./my_demo_svr

例如我们的 Dockerfile 是这样的(仅做示例):

FROM centos
ADD my_demo_svr /
ADD start.sh /
ENTRYPOINT ["sh", "start.sh"]

正是因为咱们在容器中是通过 shell 脚本来启动咱们的 my_demo_svr 服务,那么实际情况是这样来的

bash(xxxpid) --- shell(xxpid) --- my_demo_svr(xxxpid)

那就相当于 my_demo_svr 是 shell 的子进程,shell 收到 k8s 发送的 sigterm 信号的时候,是不会传递给子进程 my_demo_svr 的,因此 my_demo_svr 是不会进行优雅关闭的

看到这里,实际上我们处理的思路就是:

  • 如何让 shell 收到 sigterm 信号的时候,可以传递给他的子进程

实际应用了两种方式

  • 在脚本中,实际启动程序的时候 我们加上 exec 命令exec 命令可以用于调用并执行命令,我们可以这样来修改
#!/bin/bash
exec ./my_demo_svr

简单的修改了这个脚本之后,咱们的 my_demo_svr 程序会替换 shell,并且不会出现子进程,此时 k8s 发送 sigterm 信号的时候,那么接收信号的直接就是 my_demo_svr 服务,此时的优雅关闭就可以正常触发了

  • 使用 linux 中的 trap 命令

trap 命令,可以用来传递信号,我们正好就可以应用它来解决我们的实际问题

例如我们就可以这样来写

此处要注意,咱们的服务启动后面是有 & 的,让我们的 my_demo_svr 在后台运行

#!/bin/sh

./my_demo_svr &

pid="$!"

_kill() {
  echo "start closing ..."
  kill $pid 
  wait $pid
  exit 0
}

trap _kill SIGTERM 
wait

这个时候,当我们的 k8s 中的 pod 被 delete 或者被 rollout restart 的时候,会给咱们容器中的 shell 发送 sigterm 信号,脚本中由于我们使用 trap 命令来传递信号给到 my_demo_svr 程序中,进而触发 my_demo_svr 优雅关闭

至此,请大家引以为鉴,尽量减少踩坑吧,如果有相同经历的欢迎评论交流哦

感谢阅读,欢迎交流,点个赞,关注一波 再走吧

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~