supervisor —— 非 systemd 场景的进程守护
supervisor —— 非 systemd 场景的进程守护
分类:05 服务与计划任务 适用:Linux / macOS(Python 实现),老系统、容器内、无 systemd 环境 一句话:把任意前台脚本变成"挂了自动拉起、能统一启停看日志"的托管服务。
1. 它解决什么问题
- CentOS 6 或容器里没有 systemd,脚本挂了没人管。
- 一台机器上跑十几个 Python/Node 小任务,想要一个面板统一管理。
- 需要把进程的 stdout 自动切成日志文件并轮转。
- 非 root 用户管理自己的进程组。
不适合:有 systemd 的现代 Linux 上管理正式服务——直接用 systemd 更可靠。
2. 安装
# Python 环境(推荐 pip,版本新)
python3 -m pip install supervisor
# 发行版
apt install -y supervisor
dnf install -y supervisor
配置文件位置:
| 安装方式 | 主配置 | 子配置目录 |
|---|---|---|
| apt | /etc/supervisor/supervisord.conf |
/etc/supervisor/conf.d/ |
| pip | /etc/supervisord.conf(需自己生成) |
自定义 |
| 源码 | 同 pip |
生成默认配置:
echo_supervisord_conf > /etc/supervisord.conf
3. 启动方式
# 有 systemd 的系统(用它托管 supervisor 自身)
systemctl enable --now supervisor
# 无 systemd
supervisord -c /etc/supervisord.conf
supervisorctl -c /etc/supervisord.conf status
# 前台运行(容器内作 PID 1)
supervisord -n -c /etc/supervisord.conf
容器里用
-n(nodaemon)让 supervisord 在前台,否则容器会立刻退出。
4. 配置结构
; /etc/supervisord.conf
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisord]
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor
loglevel=info
nodaemon=false
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[include]
files = /etc/supervisor/conf.d/*.conf
单个程序
; /etc/supervisor/conf.d/myapp.conf
[program:myapp]
command=/opt/app/venv/bin/python /opt/app/main.py --port 8080
directory=/opt/app
user=app
autostart=true
autorestart=true
startsecs=5
startretries=3
stopwaitsecs=30
stopsignal=TERM
environment=PYTHONUNBUFFERED="1",TZ="Asia/Shanghai",DATABASE_URL="mysql://..."
stdout_logfile=/var/log/supervisor/myapp.out.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
stderr_logfile=/var/log/supervisor/myapp.err.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
redirect_stderr=false
priority=100
关键字段
| 字段 | 作用 | 建议 |
|---|---|---|
command |
启动命令 | 必须前台运行,不能自己 daemonize |
directory |
工作目录 | 避免相对路径问题 |
user |
运行用户 | 生产别用 root |
autostart |
supervisord 启动时自动起 | true |
autorestart |
退出后重启 | true / unexpected / false |
startsecs |
启动后存活多少秒算成功 | 5~10,太短会误判 |
startretries |
启动失败重试次数 | 3 |
stopwaitsecs |
停止时等待优雅退出 | 按业务,30 |
stopsignal |
停止信号 | 一般 TERM |
environment |
环境变量 | 用逗号分隔,值要引号 |
stdout_logfile |
标准输出日志 | 必须配,否则可能丢失 |
maxbytes/backups |
日志轮转 | 50MB × 5 |
redirect_stderr |
stderr 合并到 stdout | 不配则分文件 |
priority |
启动顺序(小先起) | 有依赖时用 |
numprocs |
启动几个实例 | 配合 process_name=%(program_name)s_%(process_num)02d |
umask |
文件权限掩码 | 022 |
多实例
[program:worker]
command=/opt/app/worker
process_name=%(program_name)s_%(process_num)02d
numprocs=4
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/worker-%(process_num)02d.log
分组(统一启停)
[group:backend]
programs=myapp,worker
priority=999
之后可以 supervisorctl restart backend:*。
5. supervisorctl
supervisorctl status # 所有程序状态
supervisorctl status myapp
supervisorctl start myapp
supervisorctl stop myapp
supervisorctl restart myapp
supervisorctl restart all
supervisorctl update # 应用配置变更(增删程序)
supervisorctl reread # 只读配置,不应用
supervisorctl reload # 重启 supervisord 本身(会重启所有子进程)
supervisorctl tail -f myapp stderr # 跟踪错误日志
supervisorctl tail -100 myapp stdout # 最近 100 行
supervisorctl clear myapp # 清空日志文件
supervisorctl pid myapp
supervisorctl signal HUP myapp
supervisorctl avail # 未加载的配置
status 输出:
myapp RUNNING pid 1234, uptime 1 day, 3:12:45
worker:00 FATAL Exited too quickly (process log may have details)
| 状态 | 含义 |
|---|---|
RUNNING |
正常 |
STARTING |
启动中(startsecs 内) |
BACKOFF |
启动失败,正在重试 |
FATAL |
重试次数用尽,放弃 |
STOPPED |
已停止 |
EXITED |
正常退出(autorestart=false) |
UNKNOWN |
状态异常,通常 supervisord 与子进程失联 |
6. 增删改程序的正确流程
# 1. 新增/修改 /etc/supervisor/conf.d/xxx.conf
# 2. 读取新配置
supervisorctl reread
# 3. 应用(会启动新增的、停掉删除的、重启改动的)
supervisorctl update
# 4. 验证
supervisorctl status
只 reread 不 update 不会生效,这是最常见的操作失误。
7. 实战场景
7.1 托管一个 Python 服务
[program:api]
command=/opt/api/venv/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
directory=/opt/api
user=api
environment=PATH="/opt/api/venv/bin:%(ENV_PATH)s",PYTHONUNBUFFERED="1"
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=30
stdout_logfile=/var/log/api/access.log
stderr_logfile=/var/log/api/error.log
%(ENV_PATH)s 引用 supervisord 进程的 PATH。
7.2 容器内多进程
Dockerfile:
RUN apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/*
COPY supervisord.conf /etc/supervisor/conf.d/app.conf
CMD ["supervisord", "-n", "-c", "/etc/supervisord.conf"]
[supervisord]
nodaemon=true
logfile=/dev/stdout
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:app]
command=/opt/app/server
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true
容器最佳实践仍是"一个容器一个进程"。用 supervisor 是折中,注意信号转发问题(见坑 8)。
7.3 失败自动拉起并留证
[program:crawler]
command=/opt/crawler/run.sh
autorestart=true
startretries=10
startsecs=3
stdout_logfile=/var/log/crawler/out.log
stderr_logfile=/var/log/crawler/err.log
FATAL 时先看 err.log,再 supervisorctl clear crawler + start。
7.4 用 HTTP 接口远程管理
[inet_http_server]
port=127.0.0.1:9001
username=admin
password=StrongPass
浏览器访问 http://127.0.0.1:9001。只绑 127.0.0.1,要远程访问走 SSH 隧道,别暴露公网。
8. 常见坑
- 程序自己 daemonize:supervisor 要求前台运行,程序 fork 到后台会被判定"启动后立即退出"。加
-g "daemon off;"(nginx)或--nodaemon。 reread后没update:配置不生效。command用相对路径:找不到可执行文件,FATAL。写绝对路径。- 环境变量没继承:
PATH、JAVA_HOME、代理都要在environment里显式给。 startsecs太短:程序启动需要 20 秒,设 5 秒会不停重启。- 日志不轮转:不配
maxbytes/backups会把磁盘写满。supervisor 自己轮转,不要再用 logrotate 管同一文件。 redirect_stderr=true后 stderr 文件不生成:正常,错误都进 stdout 文件了。- 容器里信号转发:supervisord 作 PID 1 时,
docker stop的 SIGTERM 由它接收,需配置stopsignal并确保子进程能优雅退出,否则 10 秒后被 SIGKILL。 user=需要 root 启动:supervisord 以普通用户运行时无法切换用户。supervisorctl reload会重启所有进程:生产用reread + update。unix_http_server权限:socket 权限不对时supervisorctl报Permission denied,配chmod=0700并用同用户执行。- SELinux:
/var/log/supervisor上下文不对会写不进日志,restorecon -Rv修复。 - Python 3.12+ 兼容:老版本 supervisor 4.2 之前有兼容问题,升级到 4.2.5+。
numprocs改了要update,不是restart。
9. 速查表
# 安装与启动
pip install supervisor
echo_supervisord_conf > /etc/supervisord.conf
supervisord -c /etc/supervisord.conf
systemctl enable --now supervisor
# 管理
supervisorctl status
supervisorctl start|stop|restart <name>
supervisorctl restart all
supervisorctl reread && supervisorctl update # 改配置的标准流程
supervisorctl reload # 重启 supervisord(慎用)
supervisorctl tail -f <name> stderr
supervisorctl clear <name>
supervisorctl avail
# 配置要点
# command 用绝对路径且前台运行
# autostart=true autorestart=true startsecs=5
# 配 stdout_logfile + maxbytes + backups
# environment 显式声明变量