在說明如何穩定安全地升級、降級已經在運行中的nginx之前,需要先瞭解nginx支持的幾種信號。以下幾種是主進程可以接收的信號,註意worker進程也可以接收一些信號,但和主進程的信號處理機制有些不一樣,且主進程支持的信號worker進程不一定支持。具體可見man nginx。 graceful s ...
在說明如何穩定安全地升級、降級已經在運行中的nginx之前,需要先瞭解nginx支持的幾種信號。以下幾種是主進程可以接收的信號,註意worker進程也可以接收一些信號,但和主進程的信號處理機制有些不一樣,且主進程支持的信號worker進程不一定支持。具體可見man nginx。
SIGINT, SIGTERM 立即殺掉nginx主(即所有進程)
SIGQUIT graceful stop主進程
SIGWINCH graceful stop所有的worker進程
SIGHUP reload配置文件,並使老的worker進程graceful stop
SIGUSR1 重新打開日誌文件(Reopen log files)
SIGUSR2 線上切換nginx可執行程式(Upgrade the nginx executable on the fly)
graceful stop的行為是:(1)進程不再監聽、接受新的請求;(2)進程繼續處理正在處理的請求,但處理完成後銷毀。
1. 升級
如果想對一個已運行的nginx實例進行版本升級,或者因為重新編譯了一個版本而替換舊版本,可以考慮按照以下一系列過程來平穩、安全地升級。當然,如果直接停止服務不會產生多大影響,直接停掉再啟動新版本nginx實例更方便簡單。
- 1.將新版本的nginx命令路徑替換掉舊的nginx命令。
通常,對於編譯安裝的nginx來說,採用軟鏈接的方式比較便捷。例如舊版本的安裝路徑為/usr/local/nginx-1.12.0,為其建立一個軟鏈接/usr/local/nginx,如果有新版本/usr/local/nginx-1.12.1,只需修改軟鏈接/usr/local/nginx的指向目標為/usr/local/nginx-1.12.1即可。這樣/usr/local/nginx/sbin/nginx就會隨著軟鏈接的指向改變而指向新nginx程式。
- 2.對舊nginx實例的主進程發送USR2信號。
kill -USR2 `cat /var/run/nginx/nginx.pid`
該信號提示nginx舊的主進程要升級,並執行新的nginx程式。例如步驟1中,舊的nginx主進程為/usr/local/nginx/sbin/nginx,但其指向的是/usr/local/nginx-1.12.0/sbin/nginx,發送該信號後仍將執行/usr/local/nginx/sbin/nginx,但此時因為軟鏈接目標已改變,使得此時啟動的nginx已經是/usr/local/nginx-1.12.1/sbin/nginx程式。
[root@xuexi ~]# ps aux | egrep '(ngin[x]|PI[D])'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 103753 0.0 0.3 122720 7360 ? S 17:01 0:00 nginx: master process /usr/sbin/nginx
nginx 103754 0.0 0.1 125248 3520 ? S 17:01 0:00 nginx: worker process
nginx 103755 0.0 0.1 125248 3520 ? S 17:01 0:00 nginx: worker process
nginx 103756 0.0 0.1 125248 3520 ? S 17:01 0:00 nginx: worker process
nginx 103757 0.0 0.1 125248 3520 ? S 17:01 0:00 nginx: worker process
root 103919 0.0 0.3 122720 7364 ? S 17:44 0:00 nginx: master process /usr/sbin/nginx
nginx 103920 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103921 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103922 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103923 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
此外,發送該信號後將會切換pid文件,舊的pid文件被重命名為nginx.pid.oldbin,記錄的是舊的nginx主進程pid值,新的pid文件為nginx.pid,記錄的是新啟動的nginx的主進程pid值。
[root@xuexi ~]# ls /var/run/nginx*
/var/run/nginx.pid /var/run/nginx.pid.oldbin
- 3.graceful stop舊的主進程號。
kill -QUIT `cat /var/run/nginx/nginx.pid.oldbin`
向舊的主進程號發送QUIT信號,該信號將使得主進程以graceful的方式關閉。這將使得舊的主進程、舊的worker進程不再接受任何新請求,但卻會把正在處理過程中的請求處理完畢,然後被銷毀退出。
- 4.更穩妥的方式是先讓worker進程graceful stop,在新版本的nginx實例運行一小段時間後如果正常工作,再graceful stop舊的主進程。
kill -WINCH `cat /var/run/nginx/nginx.pid.oldbin`
# a period of time goes, graceful stop old master nginx
kill -QUIT `cat /var/run/nginx/nginx.pid.oldbin`
在發送WINCH信號給舊的主進程後,舊的worker進程將逐漸退出,但舊的主進程卻會保留不退出。
[root@xuexi ~]# ps aux | egrep '(ngin[x]|PI[D])'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 103753 0.0 0.3 122720 7432 ? S 17:01 0:00 nginx: master process /usr/sbin/nginx
root 103919 0.0 0.3 122720 7364 ? S 17:44 0:00 nginx: master process /usr/sbin/nginx
nginx 103920 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103921 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103922 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
nginx 103923 0.0 0.1 125248 3524 ? S 17:44 0:00 nginx: worker process
如果發現新版本的nginx實例不滿意,可以直接向舊主進程號發送HUP信號,這樣舊的主進程就會重新讀取配置文件並fork新的worker進程,再將新的主進程號殺掉(可以graceful stop),就可以還原為舊版本的nginx實例。
2. 降級
上面第4步其實就是最安全的降級方式。即:
kill -HUP `cat /var/run/nginx/nginx.pid.oldbin`
kill -QUIT `cat /var/run/nginx/nginx.pid`
但如果舊的主進程號已經被殺掉了,目前只有新版本的nginx實例在運行,那麼只需以升級的步驟進行降級即可。即:
kill -USR2 `cat /var/run/nginx/nginx.pid`
kill -QUIT `cat /var/run/nginx/nginx.pid.oldbin`
3.一鍵升級腳本
以下是升級的腳本。
#!/bin/sh
#
# Legacy action script for "service nginx upgrade"
# Source function library.
[ -f /etc/rc.d/init.d/functions ] && . /etc/rc.d/init.d/functions
if [ -f /etc/sysconfig/nginx ]; then
. /etc/sysconfig/nginx
fi
prefix=/usr/local/nginx
prog=nginx
nginx=$prefix/sbin/nginx
conffile=$prefix/conf/nginx.conf
pidfile=/var/run/nginx.pid
SLEEPSEC=${SLEEPSEC:-1}
UPGRADEWAITLOOPS=${UPGRADEWAITLOOPS:-5}
oldbinpidfile=${pidfile}.oldbin
# 配置文件語法檢查
${nginx} -t -c ${conffile} -q || return 6
echo -n $"Starting new master $prog: "
# 發送USR2信號升級nginx可執行程式
killproc -p ${pidfile} ${prog} -USR2
echo
for i in `/usr/bin/seq $UPGRADEWAITLOOPS`; do
/bin/sleep $SLEEPSEC
if [ -f ${oldbinpidfile} -a -f ${pidfile} ]; then
echo -n $"Graceful shutdown of old $prog: "
# graceful stop舊主nginx主進程
killproc -p ${oldbinpidfile} ${prog} -QUIT
echo
exit 0
fi
done
echo $"Upgrade failed!"
exit 1