一个人膨胀只会害了自己,抱持既定成见故步自封不可取。
我起初还看不上 Python 写的 Supervisor,沉迷 screen,甚至自己手写过服务*。
* 不是 .service
插进 /etc/systemd/system/
就叫服务(准确地说是 daemon 进程)。
单脚本无依赖支持 start
/ stop
/ restart
/ status
才叫服务。
支持开机自启 / 状态自检 / 自动重启当然要有,但那是「特性」,不是「依赖」。
在前段时间为了 WSLg 重装 WSL 之前,我用 Hugo 跑的本地网页文档都还是手写的服务。
舍不得换。刚写好的时候更是不得了,后来机缘巧合用上 Supervisor 才感觉自己像个沙口。

1
| $ apt install -y supervisor
|
从 官方文档 可知配置文件的读取顺序:
../etc/supervisord.conf
../supervisord.conf
$CWD/supervisord.conf
$CWD/etc/supervisord.conf
/etc/supervisord.conf
/etc/supervisor/supervisord.conf
(v3.3.0+)
很多 Debian 和 Ubuntu 版本的 Supervisor 都把 /etc/supervisor/supervisord.conf
加入了搜索路径。但 PyPI 包只有 3.3.0 以上的版本才包括这个路径,用 pip 安装的话要注意。
vim /etc/supervisor/supervisord.conf
编辑主配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| [unix_http_server]
file=/tmp/supervisor.sock # UNIX的socket文件 supervisorctl会用XML-RPC连接到这个文件来和supervisord通信
;chmod=0700 # socket文件的权限 默认为0700
;chown=root:root # socket文件的owner 格式uid:gid
;[inet_http_server] # 注意这一行也被注释掉了
;port=127.0.0.1:9001 # web管理后台运行的ip和端口 建议不要放给公网 一定要放的话注意安全
;username=user # web管理后台的用户名
;password=123 # web管理后台的密码
[supervisord]
logfile=/tmp/supervisord.log # 主程序的日志文件 默认为$CWD/supervisord.log
logfile_maxbytes=50MB # 单个日志文件大小 默认为50MB 0为不限制大小
logfile_backups=10 # 日志记录文件备份数量 默认为10份 0为不备份
loglevel=info # 日志级别 默认为info 可以为critical/error/warn/info/debug/trace/blather
umask=022 # 主程序的umask 默认为022
pidfile=/tmp/supervisord.pid # 记录pid的文件 默认为$CWD/supervisord.pid
nodaemon=false # 是否在前台启动 默认为false(以daemon方式启动)
minfds=1024 # 允许打开文件描述符的最小值 默认为1024
minprocs=200 # 允许打开的进程数的最小值 默认为200
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock # 连接到supervisord的url
;serverurl=http://localhost:9001 # 通过http的方式连接supervisord
[include]
files=/etc/supervisor/conf.d/*.conf # 其他配置文件路径
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| [unix_http_server]
file = /var/run/supervisor.sock
chmod = 0700
chown = root:users
[inet_http_server]
port = 127.0.0.1:9001
username = admin
password = 123456
[supervisord]
logfile = /var/log/supervisor/supervisord.log
logfile_maxbytes = 50MB
logfile_backups = 10
pidfile = /var/run/supervisord.pid
childlogdir = /var/log/supervisor
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl = unix:///var/run/supervisor.sock
[include]
files = /root/app/supervisor/conf
[group:hugo]
programs = docs,blog
priority = 500
|
放在主程序配置文件 [include]
设置的路径下。
比如 vim /etc/supervisor/conf.d/test.conf
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| [program:test] # 子程序的名字 supervisorctl管理的就是这个
user=test # 运行子程序的用户
directory=/tmp/ # 子程序运行的工作目录
command=/bin/sleep 100 # 子程序运行的具体命令
autostart=true # 子程序是否跟随supervisor主程序一起启动 默认true
autorestart=unexpected # false从不重启 true总是重启只要一退出就重启 unexpected仅抛出错误退出时重启
exitcodes=0,2 # 定义[正常退出]的退出码 不在这个列表里的错误代码会触发unexpected的autorestart
startretries=3 # 尝试重启的次数 3为3次以后仍未启动则不再重启并显示状态FATAL 默认3次
startsecs=1 # 启动成功的判断时间 1为启动后1秒还在运行(没有抛出错误并退出)则视为启动成功 默认1秒
stdout_logfile=/tmp/test_sv.log # 正常日志log输出文件路径
stdout_logfile_maxbytes=20MB # stdout日志记录文件大小 默认50MB
stdout_logfile_backups=10 # stdout日志记录文件备份数量 默认10份
redirect_stderr=false # 是否将error重定向至普通log 换句话说是否合并所有日志输出
stderr_logfile=/tmp/test_sv.err # 错误日志error输出文件路径
stderr_logfile_maxbytes=20MB # stderr日志记录文件大小 对应stdout
stderr_logfile_backups=10 # stderr日志记录文件备份数 对应stdout
priority=999 # 优先级 值越小越优先 默认999
numprocs=1 # 进程数量 >1表示多进程 产生的参数process_num是第几个启动的进程
process_name=%(program_name)s_%(process_num)02d # 如果开启多进程 某个子进程的命名格式 形如test_00/test_01
|
1
2
3
4
5
6
7
8
9
10
11
| [program:docs]
user=root
directory=/mnt/d/Workspace/Git/Repo/Blog/local-docs/
command=/root/app/hugo server -p 1314 --minify --theme book --watch -D -E -F
autostart=true
autorestart=true
startsecs=2
stdout_logfile=/tmp/log/supervisor/docs.out.log
stdout_logfile_maxbytes=2MB
stderr_logfile=/tmp/log/supervisor/docs.err.log
stderr_logfile_maxbytes=2MB
|
1
2
3
4
5
6
7
8
| $ supervisorctl help # 查看用法
$ supervisorctl status # 查看所有子程序的状态
$ supervisorctl stop hugo # 停止名为 hugo 的子程序
$ supervisorctl clear api # 清除名为 api 的子程序的输出日志
$ supervisorctl start test # 启动名为 test 的子程序
$ supervisorctl restart all # 重启所有子程序
$ supervisorctl update all # 重载全部配置文件
$ supervisorctl reload # 重启 supervisord 主程序
|
你确定 Supervisor 真的正在运行中吗?
重启服务器并不代表就会自动运行 Supervisor,它是值守其他程序的,不是值守它自己。
supervisord -c /etc/supervisord.conf
手动启动 Supervisor。
这种情况有可能是启动了多个 Supervisor,导致同一时间有多个 Supervisor 在一起管理。
ps -aux | grep supervisord | grep -v grep
查看正在运行的 Supervisor 进程;
如果有两个或以上,那就说明确实是这种情况。
原因可能是 Supervisor 本来已经在运行了,结果又运行了一个新的。
不是人家业务逻辑写得烂,Linux 程序还特意写单例的有心人真的不多。
Windows 的应用程序动动鼠标就点开了,开个软件还要特意过一遍脑子才是奇葩。
Linux 操作系统这么底层,启动程序的心智成本高也很合理,不然瞎玩玩坏了怎么办。
可以运行:
1
| $ ps -aux | grep supervisord | grep -v grep | awk '{print $2}' | xargs kill -9
|
强制关闭正在运行的所有 Supervisor,然后重新运行:
1
| $ supervisord -c /etc/supervisor/supervisord.conf
|
启动唯一指定 Supervisor。