Docker容器内多进程无法接收SIGTERM信号问题排查求助
问题原因与解决方案
核心原因:信号无法传递到Go进程
当K8s销毁容器时,会向容器的PID 1进程发送SIGTERM信号,但你的场景中PID 1是run.sh对应的shell进程,存在两个关键问题:
- Bash作为PID 1时,默认忽略SIGTERM信号(Unix系统对PID 1的特殊规则),不会主动将信号转发给后台子进程。
run.sh中用&启动Go进程作为后台任务,随后用wait等待,但shell没有配置信号转发逻辑,导致SIGTERM无法传递到Go进程,自然看不到信号处理日志。
具体解决方案
方案1:让Go进程直接成为PID 1(推荐单实例场景)
修改K8s Deployment的启动命令,跳过shell脚本,直接启动Go二进制文件,让它成为PID 1,这样就能直接接收SIGTERM信号:
- name: ztest image: xxx/test imagePullPolicy: IfNotPresent args: - ./ztest - --config-location - plugins_hello/your-config-dir/ - --config-name - your-config-name
如果需要启动多个Go实例,建议将遍历配置目录的逻辑迁移到Go代码内部,用goroutine或进程管理方式实现,避免依赖shell。
方案2:修改shell脚本,添加信号转发逻辑(多实例场景)
在run.sh中添加信号捕获与转发逻辑,当shell收到SIGTERM时,主动将信号发送给所有后台Go进程:
# 定义信号处理函数,转发SIGTERM给所有子进程 handle_sigterm() { echo "Shell received SIGTERM, forwarding to child processes..." kill -TERM "${child_pids[@]}" wait } # 捕获SIGTERM信号,触发处理函数 trap 'handle_sigterm' SIGTERM # 存储子进程PID的数组 child_pids=() for d in plugins_"$repo_name"/*/; do ./ztest --config-location "$dir"/ --config-name $(basename "$filename") & # 记录每个Go进程的PID child_pids+=($!) done; wait
这样shell收到SIGTERM后,会将信号传递给所有后台Go进程,你的Go代码就能正常捕获并输出日志。
额外检查点
- 确认K8s的
terminationGracePeriodSeconds配置(默认30秒)足够长,确保Go进程有时间完成优雅退出逻辑。 - 验证Go代码的信号监听逻辑:你的代码中
signal.Notify已经正确注册SIGTERM,只要信号能传递到进程,就能触发日志输出。
内容的提问来源于stack exchange,提问作者BAE
相关产品推荐
相关产品推荐

