如何用supervisor守護php-fpm主進程以實現php-fpm的自動重啟,supervisorphp-fpm
最近有同事有個針對php-fpm進程的監護需求,也即:如果php-fpm的master進程意外退出(可能是crash,也可能是被誤kill),那麼希望master進程能被自動拉起,以免中斷服務。
我們知道,supervisor是一個非常強大的進程監控(monitor & control)工具,它理論上可以實現php-fpm master進程的守護需求。因此,我幫同事實驗了如何用supervisor完成他的需求,結果表明,supervisor確實是神器,只需一個合理的設定檔,它就能解決問題。
下面是我的調研過程及最終實現php-fpm主進程守護功能的設定檔,在此做個記錄,也希望能協助到別人。
1. 安裝supervisor
supervisor本身是python實現的,而且是調研階段,故先建立一個新的virtualenv環境,然後用pip安裝好supervisor包。
至此,基本的調研環境搭建完畢。當然,php-fpm和PHP環境以及前端的Nginx是早就ready的。
2. 分析php-fpm.sh指令碼
通常編譯安裝PHP後,php-fpm這個2進位的C程式也會被編譯並安裝好,典型路徑在php_install_path/sbin/目錄下。該目錄下還有個名為php-fpm.sh的指令碼用於控制php-fpm進程的start/stop/restart/reload等動作。
./sbin/php-fpm.sh指令碼中,”start”操作啟動了php-fpm主進程,其餘的操作都是通過向php-fpm master進程發signal實現的。
## code segment in php-fpm.shcase "$1" in start) echo -n "Starting php-fpm " ## 下面這行是關鍵命令 $php_fpm_BIN --daemonize $php_opts if [ "$?" != 0 ] ; then echo " failed" exit 1 fi wait_for_pid created $php_fpm_PID if [ -n "$try" ] ; then echo " failed" exit 1 else echo " done" fi ;;
從上面是終端輸入”./sbin/php-fpm.sh start”時,實際執行的代碼,可以看到,php-fpm進程的啟動參數是–daemonize $php_opts,而$php_opts的值為”–fpm-config $php_fpm_CONF –pid $php_fpm_PID”。
注意: php-fpm.sh啟動php-fpm master進程時,傳入了daemonize參數,表明php-fpm master process以守護(daemon)方式啟動,而根據supervisor文檔的說明,當用supervisor監護進程時,被監護進程不能是守護進程,這是由於守護進程通常會在fork完子進程後就讓父進程”結束生命”,也即由supervisor建立的父進程退出,此時,supervisor無法再監護已退出進程建立出來的子進程。關於daemon process的行為,可以參考Linux Daemon Writing HOWTO一文來理解。
根據上面的分析,我們知道,只要supervisor啟動php-fpm進程時,不傳入daemonize參數即可。
3. 實現php-fpm主進程守護功能的supervisor設定檔
上面的分析已經告訴我們應該怎麼解決問題了,下面直接上驗證可用的設定檔。檔案位於php-fpm.conf同級目錄下(典型路徑為php_install_path/etc/)。
[inet_http_server] ; inet (TCP) server disabled by defaultport=127.0.0.1:9015 ; (ip_address:port specifier, *:port for all iface)[supervisord]logfile=./var/log/supervisord.log ; (main log file;default $CWD/supervisord.log)logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)logfile_backups=2 ; (num of main logfile rotation backups;default 10)loglevel=info ; (log level;default info; others: debug,warn,trace)pidfile=./var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)nodaemon=false ; (start in foreground if true;default false)minfds=1024 ; (min. avail startup file descriptors;default 1024)minprocs=200 ; (min. avail process descriptors;default 200)identifier=sup.php-fpm ; (supervisord identifier, default is 'supervisor')[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]serverurl=http://127.0.0.1:9015 ; use an http:// url to specify an inet socket[program:php-fpm]command=bash -c "sleep 1 && /home/slvher/tools/php/5.6.11/sbin/php-fpm --fpm-config /home/slvher/tools/php/5.6.11/etc/php-fpm.conf --pid /home/slvher/tools/php/5.6.11/var/run/php-fpm.pid" ; the program (relative uses PATH, can take args)process_name=%(program_name)s ; process_name expr (default %(program_name)s)autostart=true ; start at supervisord start (default: true)autorestart=true ; whether/when to restart (default: unexpected)startretries=5 ; max # of serial start failures (default 3)exitcodes=0,2,70 ; 'expected' exit codes for process (default 0,2)stopsignal=QUIT ; signal used to kill process (default TERM)stopwaitsecs=2 ; max num secs to wait b4 SIGKILL (default 10)
設定檔結構通過查看supervisor文檔很容易就能掌握,有兩個配置項需要特別注意:
1) command
它指定了supervisor要監控的進程的啟動命令,可以看到,這裡我們沒有給php-fpm傳入daemonize參數,其餘參數只是展開了php-fpm.sh中的shell變數而已。
大家已經注意到,command也不是直接調起php-fpm,而是通過bash -c執行了兩個命令,而第一個命令是sleep 1。這是由於php-fpm在stop後,其佔用的連接埠通常不能立即釋放,此時,supervisor以極快的速度試圖重新拉起進程時,可能會由於報如下錯誤而導致幾次retry均失敗:
## var/log/php-fpm.error.log[18-Jul-2015 21:35:28] ERROR: unable to bind listening socket for address '127.0.0.1:9002': Address already in use (98)[18-Jul-2015 21:35:28] ERROR: FPM initialization failed
而supervisor目前還不支援delay restart功能,因此,這裡只能通過先sleep再啟動的略顯tricky的方法來解決問題,結果表明,療效不錯且無副作用。-_-
2) autorestart
其文檔描述如下:
May be one of false, unexpected, or true. If false, the process will never be autorestarted. If unexpected, the process will be restart when the program exits with an exit code that is not one of the exit codes associated with this process’ configuration (see exitcodes). If true, the process will be unconditionally restarted when it exits, without regard to its exit code.
其預設值是unexpected,表示若被監護進程的exit code異常時,supervisor才會重新拉起進程。這裡設定為true,表明任何時候進程退出均會被再次拉起。
這樣配置好後,在本文第1步搭建好的virtualenv環境中,運行如下命令即可完成supervisor對php-fpm master進程的監護:
shell> supervisord -c etc/sup.php-fpm.conf
然後,通過ps x | fgrep fpm可以看到,php-fpm主進程已經被拉起了。
然後,kill掉php-fpm主進程,再次ps x | fgrep fpm可以看到,一個新的php-fpm主進程會被supervisor建立出來。
至此,用supervisor守護php-fpm主進程以實現php-fpm的自動重啟的需求已經解決了。
參考資料
========================= EOF ====================
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。