前幾天準(zhǔn)備部署一個go應(yīng)用,由于目前go的daemon方案還不完善,只能借助其他工具進行部署,所以發(fā)現(xiàn)了一個非常好用的進程管理工具——Supervisor。
Supervisor(http://supervisord.org/)是用Python實現(xiàn)的一款非常實用的進程管理工具。supervisor會幫你把管理的應(yīng)用程序轉(zhuǎn)成daemon程序,而且可以方便的通過命令開啟、關(guān)閉、重啟等操作,而且它管理的進程一旦崩潰會自動重啟,這樣就可以保證程序執(zhí)行中斷后的情況下有自我修復(fù)的功能。
本文將介紹如何在CentOS上Supervisor的安裝和使用。
1 安裝
1.1 安裝環(huán)境
可以先運行以下命令看系統(tǒng)是否安裝了python,以及python的版本
[root@192 ~]# python -V

Supervisor需要的python版本是python2 2.4以上(暫未明確是否支持python3)
如果系統(tǒng)沒有安裝python2的話,可以使用系統(tǒng)自帶安裝工具安裝python2
[root@192 ~]# yum install python2
再安裝python包管理工具easy_install
[root@192 ~]# yum install python-setuptools
1.2 安裝supervisor
使用easy_install安裝supervisor
[root@192 ~]# easy_install supervisor
驗證是否安裝成功
[root@192 ~]# supervisord -v

注意:supervisor的命令是supervisord,命令比名稱多一個d
supervisor主要是兩個命令:
supervisord是主進程命令,用于啟動supervisorsupervisorctl是管理命令,用于管理supervisor中的應(yīng)用程序
2 配置
2.1 配置文件
執(zhí)行如下命令
[root@192 ~]# echo_supervisord_conf
我們可以看到一份標(biāo)準(zhǔn)的配置文件模板
下面簡單說明(翻譯)一下這份模板
[unix_http_server]
file=/tmp/supervisor.sock ; UNIX socket文件默認路徑,supervisorctl命令會使用到。
;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] ; HTTP服務(wù)器,提供Web UI管理界面。默認不開啟。
;port=127.0.0.1:9001 ; Web管理后臺運行的IP和端口,如果開放到公網(wǎng)上需注意安全性。
;username=user ; Web管理后臺登錄的用戶名
;password=123 ; Web管理后臺登錄的密碼
[supervisord]
logfile=/tmp/supervisord.log ; 主進程的日志文件路徑,默認為當(dāng)前路徑下的supervisord.log即$CWD/supervisord.log
logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
loglevel=info ; log level; default info; others: debug,warn,trace
pidfile=/tmp/supervisord.pid ; 主進程編號文件,默認文件名為supervisord.pid
nodaemon=false ;主進程是否在前臺啟動,默認false即以守護進程daemon的方式在后臺運行。
minfds=1024 ; 主進程可以打開的文件描述符的最小數(shù)量,默認為1024。
minprocs=200 ; 主進程可以打開的進程最小數(shù)量,默認為200。
;umask=022 ; process file creation umask; default 022
;user=supervisord ; setuid to this UNIX account at startup; recommended if root
;identifier=supervisor ; supervisord identifier, default is 'supervisor'
;directory=/tmp ; default is not to cd during start
;nocleanup=true ; don't clean up tempfiles at start; default false
;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP
;environment=KEY="value" ; key value pairs to add to environment
;strip_ansi=false ; strip ansi escape codes in logs; def. false
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; 通過UNIX socket連接supervisord主進程,路徑與unix_http_server部分的file一致。
;serverurl=http://127.0.0.1:9001 ; 使用HTTP方式連接supervisord主進程
;username=chris ; should be same as in [*_http_server] if set
;password=123 ; should be same as in [*_http_server] if set
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history ; use readline history if available
;[program:theprogramname] ; 被管理的進程的配置參數(shù),theprogramname表示進程的名字。
;command=/bin/cat ; 程序啟動命令的路徑
;process_name=%(program_name)s ; 用來表示supervisor進程啟動時的名字,是一個Python字符串表達式,默認為%(program_name)s。
;numprocs=1 ; 啟動的進程實例數(shù)
;directory=/tmp ; 進程運行目錄
;umask=022 ; umask for process (default None)
;priority=999 ; 進程啟動優(yōu)先級,默認999,值越小越優(yōu)先啟動??刂瞥绦騿雍完P(guān)閉的順序,越早啟動越晚關(guān)閉。
;autostart=true ; 在supervisord主進程啟動時此程序自動啟動
;startsecs=1 ; 啟動1秒后沒有異常退出則表示進程正常運行
;startretries=3 ; 啟動失敗時自動重試次數(shù),默認為3次。
;autorestart=unexpected ;程序退出后自動重啟,可選值為unexpected/true/false,unexpected表示進程意外殺死后才重啟。
;exitcodes=0 ; 自動重啟預(yù)設(shè)的退出返回碼,默認為0。
;stopsignal=QUIT ; 當(dāng)收到stop請求時發(fā)送信號給程序,默認為TERM,可選值HUP/INT/QUIT/KILL/USR1/USR2
;stopwaitsecs=10 ; 操作系統(tǒng)給主進程發(fā)送SIGCHILD信號時等待的時長
;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 (0 means none, default 10)
;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false ; emit events on stdout writes (default false)
;stdout_syslog=false ; send stdout to syslog with process name (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 (0 means none, default 10)
;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;stderr_syslog=false ; send stderr to syslog with process name (default false)
;environment=A="1",B="2" ; process environment additions (def no adds)
;serverurl=AUTO ; override serverurl computation (childutils)
;[eventlistener:theeventlistenername]
;command=/bin/eventlistener ; 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)
;events=EVENT ; event notif. types to subscribe to (req'd)
;buffer_size=10 ; event buffer queue size (default 10)
;directory=/tmp ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=-1 ; the relative start priority (default -1)
;autostart=true ; start at supervisord start (default: true)
;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
;startretries=3 ; max # of serial start failures when starting (default 3)
;autorestart=unexpected ; autorestart if exited after running (def: unexpected)
;exitcodes=0 ; 'expected' exit codes used with autorestart (default 0)
;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=false ; redirect_stderr=true is not allowed for eventlisteners
;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 (0 means none, default 10)
;stdout_events_enabled=false ; emit events on stdout writes (default false)
;stdout_syslog=false ; send stdout to syslog with process name (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 (0 means none, default 10)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;stderr_syslog=false ; send stderr to syslog with process name (default false)
;environment=A="1",B="2" ; process environment additions
;serverurl=AUTO ; override serverurl computation (childutils)
;[group:thegroupname] ; 組服務(wù)
;programs=progname1,progname2 ; 配置多個服務(wù)的名稱
;priority=999 ; 啟動優(yōu)先級,默認999。
;[include] ; 包含配置文件
;files = relative/directory/*.ini
這個配置文件包含了supervisor主進程配置和應(yīng)用程序(我們希望被管理的程序)配置,一般推薦將主程序配置和應(yīng)用程序配置分開配置。
2.1 配置
配置文件里面默認將很多文件放置到/tmp/下,但是有時候系統(tǒng)會自動清理該目錄的文件,導(dǎo)致supervisor無故掛掉或者控制不了,所以我們需要先創(chuàng)建幾個目錄來存放文件
首先,推薦創(chuàng)建/etc/supervisord.d/來放置應(yīng)用程序的配置
然后,推薦創(chuàng)建/var/supervisord/來放置sock和pid文件
最后,推薦創(chuàng)建/var/supervisord/log/來放置日志文件
下面的操作都是以這幾個路徑來配置
2.1.1 創(chuàng)建主進程配置文件
生成主進程配置文件
[root@192 ~]# echo_supervisord_conf > /etc/supervisord.conf
注意:主配置文件請以這個名稱放在這個目錄,如果放在其他地方請創(chuàng)建一個軟連接,否則supervisorctl將無法管理應(yīng)用程序
下面講一些需要修改的地方,不需要修改的保持原樣即可
[unix_http_server]
file=/var/supervisord/supervisor.sock ; sock文件
..
..
;[inet_http_server] ; 出于安全考慮這個模塊一般情況不開啟
..
..
[supervisord]
logfile=/var/supervisord/log/supervisord.log ; 主進程log文件
..
..
pidfile=/var/supervisord/supervisord.pid ; pid文件
..
..
childlogdir=/var/supervisord/log ; 應(yīng)用程序日志文件
..
..
[rpcinterface:supervisor]
..
..
[supervisorctl]
serverurl=unix:///var/supervisord/supervisor.sock ; 讀取主進程sock文件
..
..
;[program:theprogramname] ; 這個模塊是應(yīng)用程序配置,在主進程中可以刪除或者不管
..
..
;[eventlistener:theeventlistenername] ;這個模塊在主進程中可以刪除或者不管
..
..
;[group:thegroupname]
..
..
[include] ; 把這個模塊前面的';'去掉,啟用這個模塊
files = /etc/supervisord.d/*.ini ; 應(yīng)用程序配置目錄
2.1.2 創(chuàng)建應(yīng)用程序文件
首先,在/etc/supervisord.d/創(chuàng)建一個ini文件
[root@192 ~]# vim /etc/supervisord.d/demo.ini
然后把[program:theprogramname]模塊整個拷貝進去
同樣,講一些需要修改的地方
[program:demo_cat] ; 名稱很重要,涉及后面的管理
command=/bin/cat ; 應(yīng)用程序的路徑,以cat為例子
..
..
directory=/tmp ; 應(yīng)用程序的運行目錄。如果應(yīng)用程序涉及相對路徑的文件,這個一定要修改好
..
..
autostart=true ; start at supervisord start (default: true)
startsecs=5 ; 設(shè)置得太低可能會因為沒來得及啟動被判啟動失敗
startretries=3 ; max # of serial start failures when starting (default 3)
autorestart=true ; when to restart if exited after running (def: unexpected)
..
..
stopasgroup=true ; send stop signal to the UNIX process group (default false)
killasgroup=true ; SIGKILL the UNIX process group (def false)
..
..
..
這樣就配置完成了
3 啟動
3.1 啟動supervisor
[root@192 ~]# supervisord -c /etc/supervisord.conf
使用該命令啟動supervisor
查看是否啟動成功
[root@192 ~]# supervisorctl status

可以看到,我們剛才配置的demo_cat程序已經(jīng)在運行了
3.2 管理應(yīng)用程序
主要是通過supervisorctl來管理應(yīng)用程序
supervisorctl status // 查看所有應(yīng)用程序狀態(tài)
supervisorctl stop programxxx // 停止某個應(yīng)用程序
supervisorctl start programxxx // 啟動某個應(yīng)用程序
supervisorctl restart programxxx // 重啟某個應(yīng)用程序
supervisorctl stop all // 停止所有應(yīng)用程序
supervisorctl reread // 讀取有更新(增加)的配置文件,不會啟動新添加的程序
supervisorctl update // 更新配置文件變化并重啟變化的服務(wù)
supervisorctl reload // 更新配置并重啟supervisor
supervisorctl shutdown // 停止supervisor
supervisorctl tail programxxx stdout // 查看某個應(yīng)用程序的輸出
同時,根據(jù)上面的配置,supervisor會重定向應(yīng)用程序的輸出,并保存到/var/supervisord/log里面

3.3 supervisor開機自啟動
新建開機啟動服務(wù)
[root@192 ~]# vim /lib/systemd/system/supervisord.service
在supervisord.service中添加以下內(nèi)容:
# supervisord service for systemd (CentOS 7.0+)
# by ET-CS (https://github.com/ET-CS)
[Unit]
Description=Supervisor daemon
[Service]
Type=forking
ExecStart=/usr/bin/supervisord
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
將服務(wù)腳本添加到systemctl自啟動服務(wù):
[root@192 ~]# systemctl enable supervisord.service
重啟系統(tǒng)測試開機啟動。
4 可能遇到的問題
4.1 error: <class 'socket.error'>, [Errno 113] No route to host: file: /usr/lib64/python2.7/socket.py line: 571
這個問題網(wǎng)上很多人說是防火墻問題,他們的解決辦法都不適用。supervisor是本地服務(wù),跟防火墻沒有關(guān)系。后面我發(fā)現(xiàn)是因為supervisorctl默認在/etc/里面有supervisord.conf。所以如果你的/etc/里面沒有supervisord.conf,建議你建個軟連接,把主程序配置文件連接過來。例如:
[root@192 ~]# ln -s /etc/supervisord.conf /etc/supervisord.conf
4.2 error: <class 'socket.error'>, [Errno 111] Connection refused: file: /usr/lib64/python2.7/socket.py line: 224
這個問題是因為supervisor沒有啟動導(dǎo)致的。請確認superviosr是否已經(jīng)啟動。
4.3 unix:///tmp/supervisor.sock no such file
這個問題是因為sock文件不見了。sock文件被刪除一般是因為放在默認的/tmp/里面被系統(tǒng)刪除了。也有可能跟我一樣,根據(jù)網(wǎng)上其他supervisor開機自啟動的辦法,將supervisrod.conf里面的nodaemon=false改成true,導(dǎo)致系統(tǒng)一直重啟supervisor,sock文件也就會一直被刪除和創(chuàng)建。
5 更新
本人也是新手,歡迎大家一起討論學(xué)習(xí)。后面有新的學(xué)習(xí)經(jīng)驗和體會會進一步更新。
參考鏈接
- https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/12.3.md
- https://blog.csdn.net/xyang81/article/details/51555473
- https://www.missshi.cn/api/view/blog/5aafcf405b925d681e000000
- https://blog.csdn.net/u012724150/article/details/54616600
- http://www.itdecent.cn/p/3c70e12b656a
- https://blog.csdn.net/sinat_21302587/article/details/76836283
- http://wonse.info/centos7-supervisor-auto-start.html