Python 進程管理工具 Supervisor 使用教程shell
Supervisor 是基於 Python 的進程管理工具,能夠幫助咱們更簡單的啓動、重啓和中止服務器上的後臺進程,是 Linux 服務器管理的效率工具。ubuntu
什麼狀況下咱們須要進程管理呢?就是執行一些須要以守護進程方式啓動的程序,好比一個後臺任務、一組 Web 服務的進程(說是一組,是由於常常用 Nginx 來作負載均衡),這些極可能是一些網站、REST API 的服務、消息推送的後臺服務、日誌數據的處理分析服務等等。vim
須要注意的是 Supervisor 是通用的進程管理工具,能夠用來啓動任意進程,不只僅是用來管理 Python 進程。瀏覽器
Supervisor 常常被用來管理由 gunicorn 啓動的 Django 或 Flask 等 Web 服務的進程。我最經常使用的是用來管理和啓動一組 Tornado 進程來實現負載均衡。服務器
除此以外,Supervisor 還能很友好的管理程序在命令行上輸出的日誌,能夠將日誌重定向到自定義的日誌文件中,還能按文件大小對日誌進行分割。
目前 Supervisor 只能運行在 Unix-Like 的系統上,也就是沒法運行在 Windows 上。Supervisor 官方版目前只能運行在 Python 2.4 以上版本,可是還沒法運行在 Python 3 上,不過已經有一個 Python 3 的移植版 supervisor-py3k。
Supervisor 有兩個主要的組成部分:
supervisord
,運行 Supervisor 時會啓動一個進程 supervisord,它負責啓動所管理的進程,並將所管理的進程做爲本身的子進程來啓動,並且能夠在所管理的進程出現崩潰時自動重啓。supervisorctl
,是命令行管理工具,能夠用來執行 stop、start、restart 等命令,來對這些子進程進行管理。
安裝
sudo pip install supervisor
建立配置文件
echo_supervisord_conf > /etc/supervisord.conf
若是出現沒有權限的問題,可使用這條命令
sudo su - root -c "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
不存在的錯誤 。
所以能夠單獨建一個文件夾,來存放這些文件,好比放在 /home/supervisor/
建立文件夾
mkdir /home/supervisor
而後對一些配置進行修改
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
|
[unix_http_server]
;file=/tmp/supervisor.sock ; (the path to the socket file)
;修改成 /home/supervisor 目錄,避免被系統刪除
file=/home/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)
;修改成 /home/supervisor 目錄,避免被系統刪除
pidfile=/home/supervisor/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
;修改成 /home/supervisor 目錄,避免被系統刪除
serverurl=unix:///home/supervisor/supervisor.sock ; use a unix:// URL for a unix socket
;serverurl=http://127.0.0.1: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)
就是因爲上面的配置文件中 /home/supervisor 文件夾,沒有授予啓動 supervisord 的用戶 oxygen 的寫權限,能夠將這個文件夾的擁有者設置該該帳號
sudo chown oxygen /home/supervisor
通常狀況下,咱們能夠用 root 用戶啓動 supervisord 進程,而後在其所管理的進程中,再具體指定須要以那個用戶啓動這些進程。
使用瀏覽器來管理
supervisor 同時提供了經過瀏覽器來管理進程的方法,只須要註釋掉以下幾行就能夠了。
1
2
3
4
5
6
7
8
9
10
|
;[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://127.0.0.1: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 某個文件夾下的全部配置文件,這樣咱們就能夠爲每一個進程或相關的幾個進程的配置單獨寫成一個文件。
1
2
|
[include]
files = /etc/supervisord.d/*.ini
|
進程的配置樣例
一個簡單的例子以下
1
2
3
4
5
6
7
8
9
10
11
12
|
; 設置進程的名稱,使用 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
這兩個參數來肯定的,這句話什麼意思呢,咱們來看這個例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
; 設置進程的名稱,使用 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
1
2
3
4
5
|
; 默認爲 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)
|
更詳細的配置例子,能夠參考以下,官方文檔在這裏
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
|
;[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] 。
1
2
3
|
[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 進程,同時咱們在配置文件中設置的進程也會相應啓動。
1
2
3
4
5
6
|
# 使用默認的配置文件 /etc/supervisord.conf
supervisord
# 明確指定配置文件
supervisord -c /etc/supervisord.conf
# 使用 user 用戶啓動 supervisord
supervisord -u user
|
更多參數請參考文檔
supervisorctl 命令介紹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 中止某一個進程,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
方法1
有一個簡單的方法,由於 Linux 在啓動的時候會執行 /etc/rc.local
裏面的腳本,因此只要在這裏添加執行命令就能夠
1
2
|
# 若是是 Ubuntu 添加如下內容
/usr/
local/bin/supervisord -c /etc/supervisord.conf
|
1
2
|
# 若是是 Centos 添加如下內容
/usr/bin/supervisord -c /etc/supervisord.conf
|
以上內容須要添加在 exit 命令前,並且因爲在執行 rc.local 腳本時,PATH 環境變量未所有初始化,所以命令須要使用絕對路徑。能夠用 which supervisord
查看一下 supervisord 所在的路徑。
在添加前,先在終端測試一下命令是否能正常執行,若是找不到 supervisord,能夠用以下命令找到
sudo find / -name supervisord
若是是 Ubuntu 16.04 以上,rc.local 被當成了服務,並且默認是不會啓動,須要手動啓用一下服務。
https://askubuntu.com/questions/765120/after-upgrade-to-16-04-lts-rc-local-not-executing-command
啓用 rc.local 服務
sudo systemctl enable rc-local.service
方法2
Supervisord 默認狀況下並無被安裝成服務,它自己也是一個進程。官方已經給出了腳本能夠將 Supervisord 安裝成服務,能夠參考這裏查看各類操做系統的安裝腳本,可是我用官方這裏給的 Ubuntu 腳本卻沒法運行。
安裝方法能夠參考 serverfault 上的回答。
好比我是 Ubuntu 系統,能夠這麼安裝,這裏選擇了另一個腳本
1
2
3
4
5
6
7
8
9
|
# 下載腳本
sudo su - root -c
"sudo curl https://gist.githubusercontent.com/howthebodyworks/176149/raw/d60b505a585dda836fadecca8f6b03884153196b/supervisord.sh > /etc/init.d/supervisord"
# 設置該腳本爲能夠執行
sudo chmod +x /etc/init.d/supervisord
# 設置爲開機自動運行
sudo update-rc.d supervisord defaults
# 試一下,是否工做正常
service supervisord stop
service supervisord start
|