日常开发和运维场景中,有些应用进程我们希望它能在后台稳定运行,不要意外崩溃,而如果万一发生异常崩溃也能自动重启,保证运行的稳定,典型的需求场景如celery任务、Web服务等。今天我们就来介绍一下这种需求的解决方案,即守护进程,重点介绍一下Supervisor这一进程管理神器。
认识守护进程
关于进程的相关知识这里不再展开表述,各位同学可以回顾一下操作系统相关的内容,也可以参考这篇文章。
守护进程的概念
在Linux或者Unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。由于在Linux中,每个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭。但是守护进程却能突破这种限制,它脱离于终端并且在后台运行,并且它脱离终端的目的是为了避免进程在运行的过程中的信息在任何终端中显示并且进程也不会被任何终端所产生的终端信息所打断。它从被执行的时候开始运转,直到整个系统关闭才退出。
前台任务和后台任务
要创建守护进程首先要让进程转变为后台任务,区别于常见命令行中的前台任务是独占命令行窗口,只有在运行完毕或手动终止才可以继续执行其他命令,后台任务创建后,用户还可以继续输入其他命令。
后台任务具有以下特点:
- 不再继承当前session(对话)的标准输入(stdin),无法向该任务继续输入指令,如果它试图读取标准输入,就好暂停执行(halt),这一点是后台任务和前台任务的本质区别;
- 继承当前session(对话)的标准输出(stdout)和标准错误(stderr),所以该后台任务的输出信息依然会同步地在命令行中显示,你可以把这些信息重定向到文件中作为日志;
将前台任务转化为后台任务的方法是在命令的尾部添加“&”符号,示例如下:
1 2 |
python helloworld.py #前台任务 python helloworld.py & #后台任务 |
后台任务到守护进程:SIGHUP信号和标准 I/O
后台任务能否成为守护进程,或用户退出当前session后后台任务是否可以继续运行,这个问题要根据Linux系统的设计来看:
- 用户准备退出 session;
- 系统向该 session 发出 SIGHUP 信号;
- session 收到 SIGHUP信号发送给所以子进程;
- 子进程收到 SIGHUP 信号后,自动退出;
不难看出,因为这样的设计,前台任务在 session 退出时因为收到 SIGHUP 信号必然会退出。而后台任务是否收到 SIGHUP 信号取决于 Shell 的 huponexit 参数。我们可以通过以下命令查看 hoponexit 参数的值:
1 |
$ shopt | grep huponexit |
大多数Linux系统,这个参数都是默认关闭的,即“huponexit off”。因此,session退出时,SIGHUP 信号并不会发送给后台任务,所以一般后台任务并不会随着session一起退出。
假设 huponexit 参数是关闭的(开启的解决方案我们下面讨论 ),还有一个问题就是退出 session 后,如果后台进程与标准 I/O 有交互还是会挂掉,原因在于后台任务的标准 I/O 继承自当前 session,一旦后台任务读写标准 I/O,就会发现已经不存在了,所以报错终止运行。
解决方法就是对后台任务的标准 I/O 进行重定向:
1 |
python helloworld.py > stdout.txt 2>stderr.txt < /dev/null & |
创建守护进程的方法
如上文介绍,我们可以通过将前台任务转为后台任务来创建守护进程,但是这种方法并不保险,因为有的系统 huponexit 参数可能是开启的(on)。这里我们来介绍几种常见的创建守护进程的方法:
disown 命令
disown命令可以将制定任务从后台任务列表(jobs命令返回的结果)中移除,后台任务只要不在这个列表中,session就肯定不会向它发送 SIGHUP 信号了。使用方法如下:
1 2 |
python helloworld.py & disown #从后台任务列表中移除helloworld进程,可以通过jobs命令验证,输出结果中找不到helloworld了 |
disown的用法如下:
1 2 3 4 5 6 |
disown # 移出最近一个正在执行的后台任务 disown -r # 移出所有正在执行的后台任务 disown -a # 移出所有后台任务 disown -h # 不移出后台任务,但是让它们不会收到SIGHUP信号 disown %2 # 根据jobId,移出指定的后台任务 disown -h %2 # 根据jobId,移出指定的后台任务 |
nohup 命令
nohup是比 disown 更为方便的命令,也是大家用的比较多的方式,方法如下:
1 |
nohup python helloworld.py & # nohup命令不会自动将进程变为后台任务,必须加& |
nohup的原理是阻止 SIGHUP 信号发送到该进程;关闭标准输入,使得该进程不再能够接收任何输入,即使运行在前台;重定向标准输出和标准错误到文件 nohup.out 中。不难看出,nohup命令实际上是将子进程和它所在的 session 分离了。
使用terminal multiplexer( 终端复用器 )
不同于上面的两种方式,终端复用器是在同一个终端中管理多个session,常用的是Screen命令和Tmux命令。它们可以在当前session中新建另一个session,一旦当前session结束,并不影响其他session,同时以后重新登录,还可以再次连接早先新建的session。
Screen命令
Screen的使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 |
screen # 新建一个session python helloworld.py # 此时使用“ctrl + A”和“ctrl + D”可以回到原本的session screen -r # 重新连接之前的新建session screen -S name # 新建多个后台session,为方便区分,可以指定名字 screen -r name # 切换到指定的session screen -r pid_number screen -ls # 列出所有的session # 要停掉某个session,可以切换到它,然后使用“ctrl + c”和“ctrl + d” |
Tmux命令
Tmux命令比Screen功能更多,更强大,使用方法如下:
1 2 3 4 5 6 7 8 |
tmux python helloworld.py tmux detach # 退出当前session,返回前一个session,或者使用“ctrl + B + d” tmux attach #下次登录时,返回后台正在运行服务session tmux new -s session_name # 新建session名命名 tmux attach -t session_name # 切换到指定session tmux list-sessions # 列出所有session tmux kill-session -t session-name # 杀死指定session |
Systemd
Linux系统有自己的守护进程管理工具Systemd,它是操作系统的部分,直接与内核交互,性能出色,功能极其强大。我们可以将程序交给Systemd,让系统统一管理,成为真正意义上的系统服务,当然这是最最推荐的方式。这里不展开讲了,参考文章。
Node 应用专用工具
对于Node应用来说,可以使用专门的工具: forever,nodemon 和 pm2。
Daemontools & supervise
supervise来管理常驻进程。基于supervise的两个比较重要的工具是Daemontools和Supervisor。实际上,supervise也算Daemontools的一个工具。Daemontools是一个包含了很多管理Unix服务的工具的软件包, 是svscanboot,svscan,supervise,svc,svok,svstat等一系列工具的合集。Daemontools最经典的搭配是和lighttpd一起使用 ,其中最核心的工具是supervise,它的功能是监控一个指定的服务,当该服务进程消亡,则重新启动该进程。
要添加让supervise监控的服务非常容易,只需要添加一个被监控的服务的目录,在该目录中添加启动服务器的名字为run的脚本文件即可。supervisor是所有项目的父进程:supervisor(pid=29208)会成为主进程,管理各个项目的进程。当项目A(pid=11531)挂掉,supervisor根据设置autorestart、startretries是否重启。若要重启,则会重启项目A(pid=13357),若不重启,则项目A(pid=0)。当supervisor被kill,管理的项目全部死掉,重启supervisor(pid=14140),管理的项目都重启。
其他工具
- monit;
- runit ;
- mini supervisor;
- Mozilla的circus (已停止更新);
- setsid;
- runnit;
- Ruby编写的God;
- debian系统的 upstart ;
- start-stop-daemon;
- 自己撸一个监控重启服务的程序;
- crontab定时重启服务;
进程管理工具Supervisor
要说当前最为主流的进程管理工具,或者说最推荐的工具,当然是Supervisor了。作为一款实用Python编写的工具,Supervisor可以很方便的监听、启动、停止、重启一个或多个进程,采用C/S架构,支持Linux/Unix系统,不支持Windows系统。使用Superviosr管理的进程,在意外崩溃后,Supervisor监听到会自动将其拉起,极其方便的实现了进程的自动恢复,保证了服务的稳定可靠,让我们解放双手,不再依赖手写shell脚本了,是每位Pythoner的骄傲。
下面简单介绍一下Supervisor的使用:
安装Supervisor
由于Supervisor使用Python开发,所以安装非常方便:
1 2 |
pip install supervisor apt-get install supervisor #Ubuntu |
Supervisor安装后会生成三个执行程序:supervisortd、supervisorctl、echo_supervisord_conf, 分别是supervisor的守护进程服务(用于接收进程管理命令)、客户端(用于和守护进程通信,发送管理进程的指令)、生成初始配置文件程序。
Supervisor配置
Supervisor在运行时需要指定配置文件,如果没有指定,默认会在下面这些目录中查找:
1 2 3 4 5 6 |
$CWD/supervisord.conf #$CWD表示运行supervisord程序的目录 $CWD/etc/supervisord.conf /etc/supervisord.conf /etc/supervisor/supervisord.conf (since Supervisor 3.3.0) ../etc/supervisord.conf (Relative to the executable) ../supervisord.conf (Relative to the executable) |
一般我们需要自行配置,可以运行 echo_supervisord_conf 程序生成 supervisor的初始化配置文件:
1 2 |
cd /etc/supervisor echo_supervisord_conf > /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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
[unix_http_server] file=/tmp/supervisor.sock ;UNIX socket 文件,supervisorctl 会使用 ;chmod=0700 ;socket文件的mode,默认是0700 ;chown=nobody:nogroup ;socket文件的owner,格式:uid:gid ;[inet_http_server] ;HTTP服务器,提供web管理界面 ;port=127.0.0.1:9001 ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性 ;username=user ;登录管理后台的用户名 ;password=123 ;登录管理后台的密码 [supervisord] logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log logfile_maxbytes=50MB ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小 logfile_backups=10 ;日志文件保留备份数量默认10,设为0表示不备份 loglevel=info ;日志级别,默认info,其它: debug,warn,trace pidfile=/tmp/supervisord.pid ;pid 文件 nodaemon=false ;是否在前台启动,默认是false,即以 daemon 的方式启动 minfds=1024 ;可以打开的文件描述符的最小值,默认 1024 minprocs=200 ;可以打开的进程数的最小值,默认 200 [supervisorctl] serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致 ;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord ; [program:xx]是被管理的进程配置参数,xx是进程的名称 [program:xx] command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run ; 程序启动命令 autostart=true ; 在supervisord启动的时候也自动启动 startsecs=10 ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒 autorestart=true ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启 startretries=3 ; 启动失败自动重试次数,默认是3 user=tomcat ; 用哪个用户启动进程,默认是root priority=999 ; 进程启动优先级,默认999,值小的优先启动 redirect_stderr=true ; 把stderr重定向到stdout,默认false stdout_logfile_maxbytes=20MB ; stdout 日志文件大小,默认50MB stdout_logfile_backups = 20 ; stdout 日志文件备份数,默认是10 ; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out stopasgroup=false ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程 killasgroup=false ;默认为false,向进程组发送kill信号,包括子进程 ;包含其它配置文件 [include] files = relative/directory/*.ini ;可以指定一个或多个以.ini结束的配置文件 |
这里比较重要的是最下面的include,你可以在这里继续添加新的进程管理配置文件。可以把所有配置项都写到 supervisord.conf 文件里,但并不推荐这样做,而是通过 include 的方式把不同的程序(组)写到不同的配置文件里。以Tomcat进程为例,我们可以创建一个新的ini配置文件,并在include声明,文件内容可以包含以下基本信息:
1 2 3 4 5 6 7 8 9 |
[program:tomcat] command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out autostart=true autorestart=true startsecs=5 priority=1 stopasgroup=true killasgroup=true |
启动Supervisor
安装好Supervisor并做好配置后,这时我们就可以运行Supervisor服务啦。运行方式很简单:
1 |
supervisord -c /etc/supervisor/supervisord.conf |
使用Supervisor做进程管理
supervisord启动后,我们可以通过supervisorctl客户端来管理进程,控制进程的启动、重启、停止、查看状态等,你可以通过“ supervisorctl help”来查看所有的命令:

当然你常用的命令一般就下面这些:
- status:查看所有进程的状态;
- reload:重新加载supervisor的配置文件,并重启所有进程;
- reread:读取有更新(增加)的配置文件,不会启动新添加的程序,也不会重启任何程序
- restart name:重启特定的程序;
- stop name:停止特定的程序;
- start name:启动特定的程序;
- update:重启配置文件修改过的程序,配置没有改动的进程不会收到影响而重启;
Supervisor的WEB管理界面
出于安全考量,Supervisor默认配置并没有开启WEB管理界面,你可以通过修改supervisord.conf配置文件来打开http访问权限,将原配置:
1 2 3 4 |
;[inet_http_server] ; inet (TCP) server disabled by default ;port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) ;username=user ; (default is no username (open server)) ;password=123 ; (default is no password (open server)) |
修改为:
1 2 3 4 |
[inet_http_server] ; inet (TCP) server disabled by default port=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface) username=user ; (default is no username (open server)) password=123 ; (default is no password (open server)) |
这个 web 后台使用 Basic Auth 的方式进行身份认证。除了单个进程的控制,还可以配置 group,进行分组管理。经常查看日志文件,包括 supervisord 的日志和各个 pragram 的日志文件,程序 crash 或抛出异常的信息一半会输出到 stderr,可以查看相应的日志文件来查找问题。

Supervisor自带的管理界面不满意的话,可以引入cesi来管理Supervisor集群,同事也可以管理集群。
参考文献
- Linux 守护进程的启动方法;
- supervise进程管理利器;
- Linux下进程管理利器—supervise(监控并将死掉的程序重启);
- nohup、&以及守护进程 ;
- Supervisor 为服务创建守护进程;
转载请注明:北凉柿子 » Linux/Unix进程管理工具 ——Supervisor