容器化服务信号量传递
创始人
2024-01-28 03:45:22
0

背景

在Kubernetes中,Pod 停止时 kubelet 会先给容器中的主进程也就是PID为1的进程发SIGTERM 信号来通知进程进行shutdown 以实现优雅停止,如果超时进程还未完全停止则会使用SIGKILL 来强行终止。比如,容器内运行一个 Java 程序, 那么信号传递给 JVM 后 Java 相关的框架比如 Spring Boot 等就会检测到此信号, 然后开始执行一些关闭前的清理工作, 这被称之为 “优雅关闭(Graceful shutdown)”。
但有时我们会遇到一种情况: 业务逻辑处理了 SIGTERM 信号,但 Pod 停止时好像没收到信号导致优雅停止逻辑不生效。比如,我们容器化 Java 应用时没有正确的让信号传递给 JVM, 那么调度程序如 Kubernetes 在等待容器关闭超时以后就会进行强制关闭, 这很可能导致一些 Java 程序无法正常释放资源, 比如数据库连接没有关闭、注册中心没有反注册等。
通常是因为我们的业务进程是在脚本中启动的,容器的启动入口使用了脚本,所以容器中的主进程并不是我们所希望的业务进程而是 shell 进程,导致业务进程收不到 SIGTERM 信号。

正确的信号传递

直接运行方式

要解决信号传递这个问题其实很简单, 也有很多方法; 比如常见的直接使用 CMD 或 ENTRYPOINT 指令运行 java 程序:

FROM alpine-oraclejdk8:latest
COPY target/xxx-0.0.1-SNAPSHOT.jar /
CMD ["java", "-jar", "/xxx-0.0.1-SNAPSHOT.jar"]

间接 Exec 方式

在 Dockerfile 里直接运行命令无法解析环境变量,但是有些时候我们又依赖脚本进行变量解析, 这时候我们可以先在脚本内解析完成, 并采用 exec 的方式进行最终执行; 这种方式也可以保证信号传递。在 shell 中启动二进制的命令前加一个 exec 即可让该二进制启动的进程代替当前 shell 进程,即让新启动的进程成为主进程:

#! /bin/bash
...exec /bin/yourapp # 脚本中执行二进制

然后业务进程就可以正常接收所有信号了。

exec命令解释

shell的内建命令exec将并不启动新的shell,而是将要被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。
因此,如果你在一个shell里面,执行exec ls;那么,当列出了当前目录后,这个shell就自己退出了,因为这个shell进程已被替换为仅仅执行ls命令的一个进程,执行结束自然也就退出了。为了避免这个影响我们的使用,一般将exec命令放到一个shell脚本里面,用主脚本调用这个脚本,调用点处可以用bash a.sh,(a.sh就是存放该命令的脚本),这样会为a.sh建立一个sub shell去执行,当执行到exec后,该子脚本进程就被替换成了相应的exec的命令。source命令或者”.”,不会为脚本新建shell,而只是将脚本包含的命令在当前shell执行。不过,要注意一个例外,当exec命令来对文件描述符操作的时候,就不会替换shell,而且操作完成后,还会继续执行接下来的命令。

Bash-c 方式

除了直接执行和 exec 方式,还有一个是使用bash -c 来执行命令; 在使用 bash -c 执行一些简单命令时, 其行为会跟 exec 很相似, 也会把子进程命令替换到父进程从而让 -c 后的命令直接接受到系统信号; 但需要注意的是, 这种方式不一定百分百成功, 比如当 -c 后面的命令中含有管道、重定向等可能仍会触发 fork, 这时子命令仍然无法完成优雅关闭。

FROM alpine-oraclejdk8:latestCOPY entrypoint.sh /
COPY target/xxx-0.0.1-SNAPSHOT.jar /
CMD ["bash", "-c", "java -jar /xxx-0.0.1-SNAPSHOT.jar"]

比较完善的方案:使用 init 系统

前面几种方案实际是用脚本实现了一个极简的 init 系统 (或 supervisor) 来管理所有子进程,只不过它的逻辑很简陋,仅仅简单的透传指定信号给子进程,其实社区有更完善的方案,这两个工具是大部分人都熟知的利器, 甚至连 Docker 本身都集成了; dumb-init 和 tini 都可以作为 init 进程,作为主进程 (PID 1) 在容器中启动,然后它再运行 shell 来执行我们指定的脚本 (shell 作为子进程),shell 中启动的业务进程也成为它的子进程,当它收到信号时会将其传递给所有的子进程,从而也能完美解决 SHELL 无法传递信号问题,并且还有回收僵尸进程的能力。
这里以tini为例制作镜像,下面是 Dockerfile 示例:

FROM alpine-oraclejdk8:latest
COPY entrypoint.sh /ENTRYPOINT ["/sbin/tini", "--","/entrypoint.sh"]

参考博客:https://www.cnblogs.com/bulh/articles/12760617.html

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...