Supervisor 是基於 Python 的進程管理工具,可以輕鬆管理一些需要以守護程序方式執行的程序,也就是後台任務,例如用來啟動和管理基於 Tornado 寫的 Web 程序。
Supervisor 是基於 Python 的進程管理工具,只能運行在 Unix-Like 的系統上,也就是無法運作在 Windows 上。 Supervisor 官方版目前只能運行在 Python 2.4 以上版本,但還無法運行在 Python 3 上,不過已經有一個 Python 3 的移植版 supervisor-py3k。
什麼情況下我們需要進程管理呢?就是執行一些需要以守護程式方式執行的程序,例如一個後台任務,我最常用的是用來啟動和管理基於 Tornado 寫的 Web 程式。
除此之外,Supervisor 還能很友善的管理程式在命令列上輸出的日誌,可以將日誌重新導向到自訂的日誌檔案中,還能依檔案大小分割日誌。
Supervisor 有兩個主要的組成部分:
supervisord,在執行Supervisor 時會啟動一個進程supervisord,它負責啟動所管理的進程,並將所管理的進程會作為自己的子進程來啟動,而且可以在所管理的進程出現崩潰時自動重新啟動。
supervisorctl,是命令列管理工具,可以用來執行 stop、start、restart 等命令,來對這些子程序進行管理。
安裝
sudo pip install supervisor
#建立設定檔
echo_supervisord_conf > /etc/supervisord.conf
#想要了解怎麼設定需要管理的進程,只要開啟supervisord.conf 就可以了,裡面有很詳細的註解資訊。
開啟設定檔vim /etc/supervisord.conf
預設的設定檔是下面這樣的,但是這裡有個坑需要注意,supervisord.pid 以及supervisor.sock 是放在/tmp 目錄下,但是/tmp 目錄是存放臨時文件,裡面的文件是會被Linux 系統刪除的,一旦這些文件丟失,就無法再透過supervisorctl 來執行restart 和stop 指令了,只會得到unix:///tmp/supervisor.sock 不存在的錯誤。
[unix_http_server] ;file=/tmp/supervisor.sock ; (the path to the socket file) ;修改为 /var/run 目录,避免被系统删除 file=/var/run/supervisor.sock ; (the path to the socket file) ;chmod=0700 ; socket file mode (default 0700) ;chown=nobody:nogroup ; socket file uid:gid owner ;username=user ; (default is no username (open server)) ;password=123 ; (default is no password (open server)) ;[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)) ... [supervisord] ;logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) ;修改为 /var/log 目录,避免被系统删除 logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log) logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) logfile_backups=10 ; (num of main logfile rotation backups;default 10) loglevel=info ; (log level;default info; others: debug,warn,trace) ;pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) ;修改为 /var/run 目录,避免被系统删除 pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) ... ;设置启动supervisord的用户,一般情况下不要轻易用root用户来启动,除非你真的确定要这么做 ;user=chrism ; (default is current user, required if root) ... [supervisorctl] ; 必须和'unix_http_server'里面的设定匹配 ;serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket ;修改为 /var/run 目录,避免被系统删除 serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket ;serverurl=http://www.php.cn/:9001 ; use an http:// url to specify an inet socket ;username=chris ; should be same as http_username if set ;password=123 ; should be same as http_password if set ...
預設情況下,進程的日誌檔案達到50MB時,將進行分割,最多保留10個文件,當然這些配置也可以對每個進程單獨配置。
權限問題設定好設定檔後,應先建立上述設定檔中新增的資料夾。如果指定了啟動使用者 user,這裡以 oxygen 為例,那麼應注意相關文件的權限問題,包括日誌文件,否則會出現沒有權限的錯誤。例如設定了啟動使用者oxygen,然後啟動supervisord 出現錯誤
Error: Cannot open an HTTP server: socket.error reported errno.EACCES (13)
##################### #就是由於上面的設定檔中/var/run 資料夾,沒有授予啟動supervisord 的使用者oxygen 的寫入權限。 /var/run 資料夾實際上是連結到 /run,因此我們修改 /run 的權限。 #########sudo chmod 777 /run#############這樣有點簡單粗暴,也可以考慮把上述設定檔中.sock,.pid 等檔案修改到其他文件夾中,並確保有相應的權限即可。一般情況下,我們可以用 root 使用者啟動 supervisord 進程,然後在其所管理的進程中,再具體指定需要以那個使用者啟動這些進程。 ######使用瀏覽器來管理######supervisor 同時提供了透過瀏覽器來管理進程的方法,只需要註解掉如下幾行就可以了。 #########
;[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)) [supervisorctl] ... ;serverurl=http://www.php.cn/:9001 ; use an http:// url to specify an inet socket ;username=chris ; should be same as http_username if set ;password=123 ; should be same as http_password if set######### #############使用include########在設定檔的最後,有一個[include] 的設定項,跟Nginx 一樣,可以include 某個資料夾下的所有設定文件,這樣我們就可以為每個進程或相關的幾個進程的配置單獨寫成一個檔案。 #########
[include] files = /etc/supervisord.d/*.ini############程式的組態範例#########一個簡單的範例如下#########
; 设置进程的名称,使用 supervisorctl 来管理进程时需要使用该进程名 [program:your_program_name] command=python server.py --port=9000 ;numprocs=1 ; 默认为1 ;process_name=%(program_name)s ; 默认为 %(program_name)s,即 [program:x] 中的 x directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录 user=oxygen ; 使用 oxygen 用户来启动该进程 ; 程序崩溃时自动重启,重启次数是有限制的,默认为3次 autorestart=true redirect_stderr=true ; 重定向输出的日志 stdout_logfile = /var/log/supervisord/tornado_server.log loglevel=info############設定日誌等級#########loglevel 指定了日誌的級別,用Python 的print 語句輸出的日誌是不會被記錄到日誌檔案中的,需要搭配Python 的logging 模組來輸出有指定等級的日誌。 #########多個行程#######
按照官方文档的定义,一个 [program:x] 实际上是表示一组相同特征或同类的进程组,也就是说一个 [program:x] 可以启动多个进程。这组进程的成员是通过 numprocs 和 process_name 这两个参数来确定的,这句话什么意思呢,我们来看这个例子。
; 设置进程的名称,使用 supervisorctl 来管理进程时需要使用该进程名 [program:foo] ; 可以在 command 这里用 python 表达式传递不同的参数给每个进程 command=python server.py --port=90%(process_num)02d directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录 ; 若 numprocs 不为1,process_name 的表达式中一定要包含 process_num 来区分不同的进程 numprocs=2 process_name=%(program_name)s_%(process_num)02d; user=oxygen ; 使用 oxygen 用户来启动该进程 autorestart=true ; 程序崩溃时自动重启 redirect_stderr=true ; 重定向输出的日志 stdout_logfile = /var/log/supervisord/tornado_server.log loglevel=info
上面这个例子会启动两个进程,process_name 分别为 foo:foo_01 和 foo:foo_02。通过这样一种方式,就可以用一个 [program:x] 配置项,来启动一组非常类似的进程。
再介绍两个配置项 stopasgroup 和 killasgroup
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进程的子进程。如果这个配置项为 true,那么也隐含 killasgroup 为 true。例如在 Debug 模式使用 Flask 时,Flask 不会将接收到的 stop 信号也传递给它的子进程,因此就需要设置这个配置项。
stopasgroup=false ; send stop signal to the UNIX process ; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进程的子进程。如果这个程序使用了 python 的 multiprocessing 时,就能自动停止它的子线程。 killasgroup=false ; SIGKILL the UNIX process group (def false)
更详细的配置例子,可以参考如下,官方文档在这里
;[program:theprogramname] ;command=/bin/cat ; the program (relative uses PATH, can take args) ;process_name=%(program_name)s ; process_name expr (default %(program_name)s) ;numprocs=1 ; number of processes copies to start (def 1) ;directory=/tmp ; directory to cwd to before exec (def no cwd) ;umask=022 ; umask for process (default None) ;priority=999 ; the relative start priority (default 999) ;autostart=true ; start at supervisord start (default: true) ;autorestart=unexpected ; whether/when to restart (default: unexpected) ;startsecs=1 ; number of secs prog must stay running (def. 1) ;startretries=3 ; max # of serial start failures (default 3) ;exitcodes=0,2 ; 'expected' exit codes for process (default 0,2) ;stopsignal=QUIT ; signal used to kill process (default TERM) ;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10) ;stopasgroup=false ; send stop signal to the UNIX process group (default false) ;killasgroup=false ; SIGKILL the UNIX process group (def false) ;user=chrism ; setuid to this UNIX account to run the program ;redirect_stderr=true ; redirect proc stderr to stdout (default false) ;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO ;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) ;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10) ;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) ;stdout_events_enabled=false ; emit events on stdout writes (default false) ;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO ;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) ;stderr_logfile_backups=10 ; # of stderr logfile backups (default 10) ;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) ;stderr_events_enabled=false ; emit events on stderr writes (default false) ;environment=A="1",B="2" ; process environment additions (def no adds) ;serverurl=AUTO ; override serverurl computation (childutils)
将多个进程按组管理
Supervisor 同时还提供了另外一种进程组的管理方式,通过这种方式,可以使用 supervisorctl 命令来管理一组进程。跟 [program:x] 的进程组不同的是,这里的进程是一个个的 [program:x] 。
[group:thegroupname] programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions priority=999 ; the relative start priority (default 999)
当添加了上述配置后,progname1 和 progname2 的进程名就会变成 thegroupname:progname1 和 thegroupname:progname2 以后就要用这个名字来管理进程了,而不是之前的 progname1。
以后执行 supervisorctl stop thegroupname: 就能同时结束 progname1 和 progname2,执行 supervisorctl stop thegroupname:progname1 就能结束 progname1。supervisorctl 的命令我们稍后介绍。
启动 supervisord
执行 supervisord 命令,将会启动 supervisord 进程,同时我们在配置文件中设置的进程也会相应启动。
# 使用默认的配置文件 /etc/supervisord.conf supervisord # 明确指定配置文件 supervisord -c /etc/supervisord.conf # 使用 user 用户启动 supervisord supervisord -u user
更多参数请参考文档
supervisorctl 命令介绍
# 停止某一个进程,program_name 为 [program:x] 里的 x supervisorctl stop program_name # 启动某个进程 supervisorctl start program_name # 重启某个进程 supervisorctl restart program_name # 结束所有属于名为 groupworker 这个分组的进程 (start,restart 同理) supervisorctl stop groupworker: # 结束 groupworker:name1 这个进程 (start,restart 同理) supervisorctl stop groupworker:name1 # 停止全部进程,注:start、restart、stop 都不会载入最新的配置文件 supervisorctl stop all # 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程 supervisorctl reload # 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启 supervisorctl update
注意:显示用 stop 停止掉的进程,用 reload 或者 update 都不会自动重启。也可以参考这里
开机自动启动 Supervisord
Supervisord 默认情况下并没有被安装成服务,它本身也是一个进程。官方已经给出了脚本可以将 Supervisord 安装成服务,可以参考这里查看各种操作系统的安装脚本,但是我用官方这里给的 Ubuntu 脚本却无法运行。
安装方法可以参考 serverfault 上的回答。
比如我是 Ubuntu 系统,可以这么安装,这里选择了另外一个脚本
# 下载脚本 sudo su - root -c "sudo curl http://www.php.cn/ > /etc/init.d/supervisord" # 设置该脚本为可以执行 sudo chmod +x /etc/init.d/supervisord # 设置为开机自动运行 sudo update-rc.d supervisord defaults # 试一下,是否工作正常 service supervisord stop service supervisord start
注意:这个脚本下载下来后,还需检查一下与我们的配置是否相符合,比如默认的配置文件路径,pid 文件路径等,如果存在不同则需要进行一些修改。
其实还有一个简单的方法,因为 Linux 在启动的时候会执行 /etc/rc.local 里面的脚本,所以只要在这里添加执行命令就可以
# 如果是 Ubuntu 添加以下内容 /usr/local/bin/supervisord -c /etc/supervisord.conf # 如果是 Centos 添加以下内容 /usr/bin/supervisord -c /etc/supervisord.conf
以上内容需要添加在 exit 命令前,而且由于在执行 rc.local 脚本时,PATH 环境变量未全部初始化,因此命令需要使用绝对路径。
在添加前,先在终端测试一下命令是否能正常执行,如果找不到 supervisord,可以用如下命令找到
sudo find / -name supervisord
更多基于Python 的进程管理工具supervisor使用指南相关文章请关注PHP中文网!