控制 nginx

控制 nginx

翻譯原文鏈接: http://nginx.org/en/docs/control.html

閱讀需要了解的前提:

  • 要對 nginx 的基本配置有比較清晰的理解;
  • 要對 nginx 的常見關鍵術語有清晰的認知,比如 全局配置文件、子配置文件,管理進程,工作進程,二進制文件或者叫可執行文件、日誌文件等;
  • 要對 nginx 可執行程序命令行選項有較熟的認知,比如 -s 是什麼含義,支持的參數可以有,-t 或者 -T 的區別是,-c 是否可以單獨使用等等;
  • 要有一定的 linux 操作系統基礎,特別是ps、 kill等 命令,常見的信號以及 nginx的配置腳本(比如 SysV init腳本以及Systemd腳本等),logrotate 程序等。(這個相較於 nginx 應用而言,屬於最基礎的東西);
  • 我們常說的幾個信號,我們要能想明白。比如 TERM, QUIT, HUP, USR1, USR2, WINCH。如果對信號理解不了,切勿在生產環境操作升級或者其他與之相關的操作(當然也不是讓你理解到源碼層面,如果要考慮實現邏輯,這個是底層代碼實現的)。

1、全局說明

nginx 可以通過信號來控制(這個是底層源碼層實現的)。默認 nginx 的管理進程(master process) 的進程id會被寫入 /usr/local/nginx/logs/nginx.pid 中。這個pid文件的名字會在配置的時候變更或者在 nginx.conf 配置文件中使用 pid 指令來修改。nginx 管理進程支持以下信號:

TERM, INT    快速關閉
QUIT         平滑關閉
HUP          變更配置,保持時區的變化(僅僅是對FreeBSD 和 Linux 系統),使用新的配置文件開啓新的工作
			 進程,並且平滑關閉舊的工作進程

USR1         重新打開日誌文件
USR2         升級一個可執行文件
WINCH        平滑關閉工作進程

用法一般爲:

kill -USR1 `cat /var/run/nginx.pid`  #這裏的pid配置文件記錄的是master process的進程id號;
或者通過 ps aux 或 ps -ef 加上管道找出nginx 管理進程的pid號,假設爲 23456
然後執行:
kill -USER1 23456

單個工作進程也能夠被信號控制,雖然一般來說不需要這樣做。nginx 的工作進程支持的信號如下:

TERM, INT    快速關閉
QUIT   		 平滑關閉
USR1         重新打開日誌文件
WINCH        調試異常終止(需要啓用 debug_points),一般生產環境不支持

2、變更配置

爲了去讓nginx去重載或重讀配置文件(即我們常說的 reload config),HUP信號應該被髮送給 nginx 的管理進程。管理進程首先會去檢查語法的有效性,然後嘗試去應用新配置,也就是說,去打開日誌文件以及打開新的監聽套接字。如果失敗了,它會回滾變更,並且會繼續使用舊配置去工作。如果成功了,它會啓動新的工作進程,並且會向舊的工作進程發送消息請求平滑關閉它們。舊的工作進程收到消息後,會關閉監聽套接字,並且繼續處理之前服務的舊的客戶端連接。當所有客戶端服務被處理完成後,舊的工作進程會被關閉。

讓我們通過示例解釋一下。假設nginx運行在FreeBSD,然後查看命令:

ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep '(nginx|PID)'

產生的輸出如下:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
33127 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)
33128 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)
33129 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)

如果 HUP 信號被髮送給 管理進程,輸出變成:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33129 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

其中一箇舊的PID爲33129的工作進程還在繼續工作。過一段時間後它也退出了:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

PS: 補充

我們在 nginx的service 服務器管理腳本中,看到reload 的配置核心段如下:

killproc -p ${pidfile} ${prog} -HUP

其中 killproc是/etc/init.d/functions 中定義的一個函數。
pidfile 是nginx pid文件,比如: /var/run/nginx.pid,裏面記錄的是運行 master process的進程id
prog 是 nginx 
HUP是一種信號,上文中有提及。

由於 functions中定義killproc比較長,這裏就不列,最終邏輯還是 調用 kill命令。比如:
kill -HUP PID_NUM

3、輪轉日誌文件

爲了去輪轉(rotate)日誌文件,它們需要首先被重命名。之後 USR1信號應該被髮送給 管理進程。管理進程收到信號後,會重新打開所有當前打開的日誌文件並且賦予運行工作進程的非特權用戶作爲它們的用戶屬主。成功重新打開後,管理進程會關閉所有打開的文件,並且向所有工作進程發送消息,讓它們重新打開文件。工作進程也會立即打開新的文件並且關閉舊的文件。因此,舊的文件可以立即被處理,比如壓縮等。

PS: 一般我們yum安裝的nginx,有一個自帶的基於 logrotate 輪詢的配置文件。我們可以根據自己的需要去二次修改處理。 可以參考 : /etc/logrotate.d/nginx 或 man logrotate

或者參考:

http://nginx.org/en/docs/syslog.html

或者自己定義腳本基於定時任務 cron 去實現日誌的切割輪詢處理。

4、在線升級二進制執行文件

爲了去升級服務端的可執行文件(二進制文件,比如 nginx),新的可執行文件應該首先替換舊的可執行文件。

PS: 建議先configure,後 make(要與原先的編譯一致),最後的make install 千萬先別做。可以直接 mv nginx nginx.old 或者 其他方式備份後。然後用新編譯的(路徑在編譯文件目錄下的 objs目錄下,make 之後有新的可執行文件nginx生成) 可執行文件替換舊的可執行文件。比如cp或者mv都行。

對於yum安裝的nginx,一般它服務器腳本(CentOS 6系列,我可以很確定) ,它支持傳入一個 upgrade,這是腳本中定義的一個函數,它會完成下問中,我們說的一系列過程(當然,也不能完全相信腳本,你要自己理解邏輯後才能處理),相關配置片段如下:

upgrade() {
    oldbinpidfile=${pidfile}.oldbin

    configtest -q || return
    echo -n $"Starting new master $prog: "
    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: "
            killproc -p ${oldbinpidfile} ${prog} -QUIT
            RETVAL=$?
            echo
            return
        fi
    done

    echo $"Upgrade failed!"
    RETVAL=1
}

默認變量如下:
UPGRADEWAITLOOPS=${UPGRADEWAITLOOPS:-5}  #從命名的含義來看,表示升級等待循環的次數。就是第一次向舊的管理進程發送 USR2信號後,可能有部分舊的工作進程在平滑關閉中,處理未完成的鏈接。
SLEEPSEC=${SLEEPSEC:-1}

這裏留一個疑問,我們手動處理的時候,除了向舊的管理進程發送 USR2 信號外,後面還會向 舊的管理進程發送一個 WINCH 信號,爲什麼這個腳本中沒有呢?

替換新的可執行文件後,然後向 nginx 的管理進程發送 USR2 信號 (比如 : kill -USR2 23127)。管理進程首先會去重命名 pid 文件,後綴是 .oldbin。比如: /usr/local/nginx/logs/nginx.pid.oldbin,然後使用新的可執行文件啓動管理進程,緊接着使用新的管理進程去生成新的工作進程,可以看到如下所示:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

之後,所有的工作進程(舊的和新的) 繼續去接受請求。如果 WINCH 信號發送給了第一個管理進程(就是舊的) 執行 命令: kill -WINCH 23127 ,舊的管理進程會向所有它之前創建的工作進程(即舊的工作進程)發送消息,請求它們去平滑關閉(所謂平滑關閉,就是要先處理完 收到管理進程告知要平滑關閉之前接收了沒有處理完的客戶端鏈接),如下:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33135 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)


#這裏可以看到 進程編號爲 33126的進程還沒有關閉,可能還有請求要處理。這種進程相較新的工作進程很明顯,後面有shutting down標誌。
生產中,如果你恰好遇到,長連接一直沒有關閉,而且超時時間又很長,可能就要你手動分析處理一下。一般來說,就是不建議強制kill。

過一會兒之後,只有新的工作進程將會處理請求:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

值得注意的是 舊的管理進程並沒有關閉監聽套接字,如果有需要的話,它可以用於管理重新啓動它的工作進程。如果由於一些原因,新的可執行文件並不工作或者不能良好的工作, 可以按照以下步驟處理:

  • 向舊的管理進程發送 HUP 信號,執行命令類似於 kill -HUP 23127。舊的管理進程將會啓動新的工作進程,不會去重讀配置文件。之後,新的管理進程(就是之前新執行程序啓動的管理進程) 將會被平滑關閉,並且可以向 新的進程發送 QUIT 信號退出,假設新的管理進程的進程ID號爲 23112,那麼命令爲 kill -QUIT 23112。

  • 向新的管理進程發送 TERM 信號,執行命令爲 kill -TERM 23112。新的管理進程會向它的工作進程發送消息,讓它們立即退出。它們(新的管理進程和新的工作進程) 將會立即退出。(如果新的進程由於某些原因沒有退出,我們可以考慮向它們發送 KILL 信號,強制讓他們退出。比如有個異常的進程 23119因爲特殊原因,在收到管理進程的 TERM後還沒有退出,我們可以這樣做 , kill -KILL 23119)。當新的管理進程退出後,舊的管理進程會自動啓動新的工作進程。

如果新的管理進程退出後,舊的管理進程會丟棄進程PID文件的 .oldbin 後綴,簡單說就是新的PID文件去掉後,舊的以 .oldbin 後綴命名的文件會被重新還原正常。 比如, /usr/local/nginx/logs/nginx.pid

如果升級過程是成功的,我們應該向舊的管理進程發送 QUIT 信號,執行命令爲 kill -QUIT 23127, 僅僅只有新的進程會保持,如下所示:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
36264     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章