Supervisor 部署非容器应用

有些进程天生不适合放进容器:老项目依赖系统里的库、需要在同一台机上按 CPU 核数跑多个 worker、运维同事习惯用 Web 界面点一下重启。Supervisor 就是这类场景的工具——它是用 Python 写的进程管理守护进程,能拉起程序、崩溃自动重启、统一看日志。但能用 systemd 就别装 supervisor:发行版自带、不引入额外 Python 依赖、和 journalctl 打通,除非你确实需要多进程批量管理或 Web 界面。

什么情况下用 Supervisor

场景建议理由
Ubuntu / Debian / RHEL 等主流发行版用 systemd自带、无需额外依赖、日志由 journald 统一管理
CentOS 6 等老系统、无 systemd用 Supervisor系统没有可用的服务管理器
容器里需要管多个进程用 Supervisor容器 PID 1 不适合直接管一堆子进程
同一程序要按核数跑多个 worker用 Supervisornumprocs 一行搞定
运维需要 Web 界面启停进程用 Supervisor[inet_http_server] 即可

安装与目录结构

sudo apt -y install supervisor            # Debian / Ubuntu
sudo dnf -y install supervisor            # Alibaba Cloud Linux / RHEL 系
sudo systemctl enable --now supervisor
supervisord -v
路径用途备注
/etc/supervisor/supervisord.conf主配置末尾通过 [include] 引入应用配置
/etc/supervisor/conf.d/*.conf每个应用一个文件Debian/Ubuntu 的默认引入目录
/etc/supervisord.d/*.iniRHEL 系的引入目录后缀是 .ini,容易踩坑
/var/run/supervisor.sock控制用 unix socketsupervisorctl 通过它通信

主配置里必须确认这一行存在,否则放进 conf.d 的文件不会生效:

[include]
files = /etc/supervisor/conf.d/*.conf

[program:xxx] 关键项

配置项含义常用值
command启动命令,必须用绝对路径/home/deploy/apps/myapi/.venv/bin/gunicorn ...
directory工作目录/home/deploy/apps/myapi/current
user以哪个用户运行deploy(绝不要用 root)
autostart随 supervisord 启动true
autorestart退出后自动重启true;调试时可设 unexpected
startsecs启动后存活多久算成功5(太快会陷入重启循环)
startretries连续失败多少次放弃3
stopwaitsecs发信号后等多久再 SIGKILL30(要给优雅停机留时间)
stopsignal用哪个信号停进程TERM;Gunicorn 用 TERM,uWSGI 用 QUIT
stdout_logfile标准输出日志路径/var/log/myapi/app.log
stdout_logfile_maxbytes / _backups单文件上限与保留份数50MB / 5
redirect_stderr把 stderr 并入 stdouttrue(日志集中在一个文件里好查)
environment注入环境变量APP_ENV="prod",TZ="Asia/Shanghai"

一个完整配置

# /etc/supervisor/conf.d/myapi.conf
[program:myapi]
command=/home/deploy/apps/myapi/.venv/bin/gunicorn app.main:app -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000 --access-logfile -
directory=/home/deploy/apps/myapi/current
user=deploy
autostart=true
autorestart=true
startsecs=5
startretries=3
stopwaitsecs=30
stopsignal=TERM
stdout_logfile=/var/log/myapi/app.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
redirect_stderr=true
environment=APP_ENV="prod",TZ="Asia/Shanghai"

[group] 与 numprocs:一次管多个进程

[program:worker]
command=/home/deploy/apps/myapi/.venv/bin/python -m app.worker
directory=/home/deploy/apps/myapi/current
user=deploy
numprocs=4
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/myapi/worker.log

[group:myapp]
programs=myapi,worker

numprocs=4 会拉起 worker_00worker_03[group:myapp]supervisorctl 可以用 myapp:* 一次操作整组进程。

supervisorctl 常用命令

sudo supervisorctl status                 # 全部程序状态
sudo supervisorctl status myapp:*         # 只看某个组
sudo supervisorctl reread                 # 重新读取配置文件(不改动进程)
sudo supervisorctl update                 # 应用配置变更(新增/重启/停止)
sudo supervisorctl restart myapp:*        # 重启整组
sudo supervisorctl stop myapi             # 停止单个程序
sudo supervisorctl tail -f -100 myapi     # 跟随查看日志尾部

reread 与 update 的正确姿势

sudo supervisorctl reread && sudo supervisorctl update    # 标准姿势,两步都要执行
# 只 reread 不 update:配置被读了但进程没变,属于「改了没生效」的典型原因
sudo supervisorctl status && sudo supervisorctl tail -20 myapi

只有在主配置本身改动时,才需要 sudo systemctl restart supervisor——注意它会重启所有被管理的程序。

为什么 stop 会卡住

  • 应用没处理 SIGTERM,进程不退出,只能等满 stopwaitsecs 后被 SIGKILL 强杀,正在处理的请求会丢。
  • stopsignal 选错:Gunicorn 需要 TERM,uWSGI 需要 QUIT,选错等于发了它直接忽略的信号。
  • 程序自己 fork 了不受管的子进程(例如用 shell 脚本启动且没有 exec),父进程退了子进程还在。

排查顺序:supervisorctl status 看状态 → supervisorctl tail -100 <name> 看应用有没有打印退出日志 → ps -ef | grep <name> 看是否残留进程 → df -h 检查磁盘是否写满(磁盘满了进程会阻塞在写日志上)。

验证方法

sudo supervisorctl status                                   # 期望 RUNNING
ps -ef | grep -v grep | grep myapi                          # 期望以 deploy 用户运行
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/healthz
kill -9 $(sudo supervisorctl pid myapi) && sleep 10 && sudo supervisorctl status   # 期望自动拉起

最后一条是核心验证:杀掉进程后能自动恢复,才说明 autorestart 真的生效

常见坑

现象做法
主配置 [include] 没配或路径不对放进 conf.d 的配置完全不生效确认 files = /etc/supervisor/conf.d/*.conf
RHEL 系用了 .conf 后缀配置被忽略RHEL 系默认只读 /etc/supervisord.d/*.ini
command 里用相对路径报找不到命令写绝对路径,或把 venv 的 bin 放进 environment 的 PATH
user 没设以 root 跑业务进程,风险极高显式声明 user=deploy
stopwaitsecs 设得太小收到请求处理一半被 SIGKILL按最长请求耗时设置,30~60 秒起步
忘了 update改了配置没生效,查半天固定套路 reread + update
日志不轮转磁盘写满,进程卡死maxbytes + backups,或交给 logrotate

小结:Supervisor 适合老系统、容器内多进程与需要 Web 界面的场景,主流发行版上应优先用 systemd;配置的关键项是 command(绝对路径)、userstartsecsstopwaitsecsstopsignal;改完配置固定走 reread + update;stop 卡住先看应用是否忽略信号、再查残留子进程与磁盘,最后用「杀掉进程看是否自动拉起」验证 autorestart 真的生效。

笔记加载中…