You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保执行supervisorctl stop时能终止Supervisor启动的服务?

Supervisor无法终止Shell脚本启动的Python多进程服务问题

问题背景

我的Supervisor配置如下:

[program:myprogram]
command=/usr/local/bin/run_script.sh
directory=/usr/local/bin
user=myuser
numprocs=1
process_name=%(program_name)s
stdout_logfile=/var/log/run_script.log
stderr_logfile=/var/log/run_script.log
autostart=true
autorestart=true
startsecs=10
stopsignal=KILL

其中run_script.sh会先完成Shell初始化操作,再启动阻塞运行的Python脚本run_script.py,该脚本通过joblib.Parallel启动多进程。

启动服务时一切正常,但执行sudo supervisorctl stop myprogram后,Supervisor显示服务已停止,但run_script.sh和run_script.py及其子进程仍在运行。

原因分析

核心问题在于Supervisor仅管理它直接启动的进程:

  • Supervisor启动的是run_script.sh对应的Bash进程,执行stop命令时,只会向这个Bash进程发送指定的KILL信号。
  • 默认情况下,Bash不会将收到的终止信号转发给它启动的子进程(即Python脚本进程)。当Bash进程被杀死后,Python进程及其joblib创建的多进程会变成孤儿进程,被系统的init或systemd进程接管,继续运行。
  • 即使指定了stopsignal=KILL,也只是终止了顶层的Bash进程,无法触及其子进程链。

解决方案

方案1:让Python进程取代Shell进程(推荐)

修改run_script.sh,用exec命令启动Python脚本,这样Python进程会直接替换当前的Bash进程,Supervisor将直接管理Python进程:

# run_script.sh 修改后内容
# 保留你的Shell初始化操作
# ...

# 用exec启动Python脚本,替换当前Bash进程
exec /usr/local/bin/run_script.py

这样当执行supervisorctl stop时,KILL信号会直接发送给Python进程,Python进程退出时会自动终止其所有子进程(包括joblib创建的多进程)。

方案2:在Shell脚本中添加信号转发逻辑

如果必须保留Shell脚本的独立进程(比如需要复杂的初始化操作),可以在run_script.sh中添加信号捕获和转发逻辑,确保终止信号能传递给Python进程:

# run_script.sh 修改后内容
# 保留你的Shell初始化操作
# ...

# 定义信号处理函数:将收到的终止信号转发给Python进程
trap 'kill -KILL $PYTHON_PID' TERM INT KILL

# 启动Python脚本并记录其PID
/usr/local/bin/run_script.py &
PYTHON_PID=$!

# 等待Python进程结束,确保Shell脚本不会提前退出
wait $PYTHON_PID

当Bash进程收到KILL信号时,会触发trap定义的逻辑,向Python进程发送KILL信号,wait命令会让Shell脚本等到Python进程完全终止后再退出,避免子进程成为孤儿。

补充说明

对于joblib.Parallel创建的多进程,只要父Python进程被正常终止,这些子进程通常会随之退出(因为父进程退出后,子进程会收到SIGHUP信号,默认行为是终止)。所以关键是确保Supervisor能正确终止父Python进程,无论是通过exec让Supervisor直接管理,还是通过Shell转发信号。

内容的提问来源于stack exchange,提问作者Cerin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:05:47