1. 程式人生 > 實用技巧 >Nginx平滑升級

Nginx平滑升級

本篇部落格介紹Nginx的平滑升級,重點是理解Nginx的訊號。

訊號及作用

訊號 訊號值 作用
INT,TERM 2,15 立即終止Nginx程序
QUIT 3 等程序結束後關閉Nginx
HUP 1 根據新的配置檔案重建worker程序,即執行nginx -s reload
USR1 10 重新寫入日誌,用於日誌切割時使用
USR2 12 根據新的可執行檔案生成新的master程序,同時保持舊的nginx程序不變
WINCH 28 關閉worker程序

設定監控

為了真實模擬現場環境,在伺服器上設定監控指令碼,當狀態碼不是200或者超時3秒即判定nginx異常。升級完成後檢查result檔案是否為空,以此判斷升級是否平滑。

[root@bochs ~]# cat nginx_hot_upgrade 
#!/usr/bin/env bash

date=$(date '+%H:%M:%S')

while true;do
        status=$(curl -m 3 -s -o /dev/null -w "%{http_code}" https://www.dmesg.top:456) 
        if [[ "${status}" -ne 200 ]];then 
        echo "${date}" >> result
        echo "WRONG!" >> result
        fi
        sleep 1
done
[root@bochs ~]# bash nginx_hot_upgrade &

升級!

本次升級nginx從1.18.0升級至1.19.2

檢視現有的Nginx版本

[root@bochs ~]# nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 
built with OpenSSL 1.1.1g  21 Apr 2020
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-openssl=/mnt/nginxinstall/openssl-1.1.1g --with-zlib=/mnt/nginxinstall/zlib-1.2.11 --with-pcre=/mnt/nginxinstall/pcre-8.44

編譯新版本Nginx

#下載1.19.2版本nginx
[root@bochs ~]# curl -o /tmp/nginx-1.19.2.tar.gz http://nginx.org/download/nginx-1.19.2.tar.gz

#解壓
[root@bochs ~]# cd /tmp && tar -xzvf nginx-1.19.2.tar.gz

#編譯
[root@bochs ~]# cd nginx-1.19.2 && ./configure [取舊版本的編譯引數,即上面的configure arguments] && make 
注:由於還是指定了舊的nginx編譯選項,此步驟不能執行make install,否則會導致配置檔案被覆蓋。

載入新的Nginx可執行檔案

#備份原有的可執行檔案nginx
[root@bochs nginx-1.19.2]# mv /usr/sbin/nginx /usr/sbin/nginx-1.18.0

#拷貝新的可執行檔案
[root@bochs nginx-1.19.2]# cp objs/nginx /usr/sbin/nginx

[root@bochs nginx-1.19.2]# ls /usr/sbin/nginx*
/usr/sbin/nginx  /usr/sbin/nginx-1.18.0

開始升級

#檢視現有的nginx程序
[root@bochs nginx-1.19.2]# ps -ef | grep -v grep |grep nginx
root     22425     1  0 14:26 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22429 22425  0 14:26 ?        00:00:00 nginx: worker process

注意:這裡需要確保nginx的啟動方式是以絕對路徑方式啟動,否則會出現execve() failed while executing new binary process "nginx" (2: No such file or directory)的報錯。

#向現有的nginx主程序傳送USR2訊號
[root@bochs nginx-1.19.2]# kill -USR2 22425
[root@bochs nginx-1.19.2]# ps -ef | grep -v grep |grep nginx        
root     22425     1  0 14:26 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22429 22425  0 14:26 ?        00:00:00 nginx: worker process
root     22503 22425  0 14:27 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22508 22503  0 14:27 ?        00:00:00 nginx: worker process
#此時可以發現nginx生成了新的master程序,並且新的master程序是舊master程序的子程序

從這裡可以看出,USR2訊號的作用是保持舊nginx程序不變的情況下生成新的nginx活動程序。此時新的Nginx雖然啟動但是並沒有實際的響應請求。

#向現有的nginx主程序傳送WINCH訊號
[root@bochs nginx-1.19.2]# kill -WINCH 22425
[root@bochs nginx-1.19.2]# ps -ef | grep -v grep |grep nginx
root     22425     1  0 14:26 ?        00:00:00 nginx: master process /usr/sbin/nginx
root     22503 22425  0 14:27 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22508 22503  0 14:27 ?        00:00:02 nginx: worker process
#winch訊號關閉了舊master的worker程序,目前新的請求已經是由新的nginx在處理了

WINCH訊號的作用是關閉由舊的master程序生成的worker程序。實際響應開始由新的master程序生成的worker程序所處理。

結束

#到此nginx升級就已經結束了,唯一要做的是確認訪問正常。然後停止舊nginx的master程序
[root@bochs nginx-1.19.2]# kill -INT 22425 
[root@bochs nginx-1.19.2]# ps -ef |grep nginx
root     22503     1  0 14:27 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22508 22503  0 14:27 ?        00:00:03 nginx: worker process

由於舊的Nginx其實不再處理請求,所以這裡傳送的訊號TERM、INT及QUIT都是可以的。同時發現,新的nginx的父程序變成了1,也就是舊nginx的父程序。

#檢查result檔案,無任何輸出。平滑升級成功!
[root@bochs ~]# cat result

回滾

當升級完成如果發現與後端應用存在不相容的情形時應當立即回滾至舊版本Nginx,回滾步驟如下

[root@bochs nginx-1.19.2]# cd /usr/sbin/

#備份新版本的可執行檔案
[root@bochs sbin]# mv nginx nginx-1.19.2

#恢復舊版本可執行檔案
[root@bochs sbin]# mv nginx-1.18.0 nginx

#向nginx的master程序傳送USR2訊號
[root@bochs sbin]# ps -ef | grep nginx
root     22503     1  0 14:27 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22835 22503  0 15:43 ?        00:00:00 nginx: worker process
[root@bochs sbin]# kill -USR2 22503
[root@bochs sbin]# ps -ef | grep nginx
root     22503     1  0 14:27 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    22835 22503  0 15:43 ?        00:00:00 nginx: worker process
root     23121 22503  0 17:42 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    23126 23121  0 17:42 ?        00:00:00 nginx: worker process

#向新nginx的master程序傳送WINCH訊號和QUIT訊號
[root@bochs sbin]# kill -WINCH 22503
[root@bochs sbin]# kill -QUIT 22503

#檢視目前Nginx狀態
[root@bochs sbin]# ps -ef | grep nginx
root     23121     1  0 17:42 ?        00:00:00 nginx: master process /usr/sbin/nginx
nginx    23126 23121  0 17:42 ?        00:00:00 nginx: worker process

#檢視result檔案無任何輸出,回滾完成
[root@bochs ~]# cat result

總結

Nginx的平滑升級主要是根據不同的訊號對可執行檔案施加不同的效果。總體而言:

  • 先發送USR2訊號給master程序達到新舊版本共存的效果。
  • 傳送WINCH訊號關閉舊版本Nginx的worker程序請求由新版本的worker開始響應。
  • 傳送QUIT/TERM/INT關閉舊版本的master程序