這裡介紹一些 Nginx 與 PHP-FPM 相關的設定檔調整方法與技巧,最佳化網頁伺服器的效能。

最近我把網站伺服器從原本的 Ubuntu Linux 14.04 換成新的 CentOS Linux 7(LEMP 架構),PHP 版本也升級成 PHP 7,結果更換之後,網頁看似正常,但不定時會出現 MariaDB 記憶體不足的錯誤訊息:

180505 11:48:00 [ERROR] Plugin 'InnoDB' init function returned error.
180505 11:48:00 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
180505 11:48:00 [ERROR] mysqld: Out of memory (Needed 128917504 bytes)
180505 11:48:00 [ERROR] mysqld: Out of memory (Needed 96681984 bytes)

起初以為是 MariaDB 的設定問題,但是後來才發現是因為系統記憶體不足,導致 MariaDB 沒有足夠的記憶體可以使用。


但是因為我租用的 VPS 伺服器規格並沒有改變,只是把原本的 Ubuntu Linux 14.04 換成新的 CentOS Linux 7,照理說記憶體需求應該不會差異太大,經過一番檢查後,發現造成系統記憶體不足的主因是因為 PHP-FPM 的設定沒有調整好,CentOS Linux 裝好 PHP-FPM 時,預設的行程數量設定太大,導致吃光所有的記憶體,影響到 MariaDB 的運作。

PHP-FPM 行程數量設定

PHP-FPM 中有一個 process manager 功能,它負責管理 PHP-FPM 的工作行程,可以在網路流量較大時,增加 PHP-FPM 行程數量,而沒有流量時則減低 PHP-FPM 的行程數量,在安裝好 PHP-FPM 之後,建議仔細調整一下 process manager 相關的設定,這樣可以讓伺服器的效能更好一點,而且也可以避免開太多 PHP-FPM 行程,把系統記憶體耗盡。

PHP-FPM 的 process manager 設定可在 pool 設定檔中調整,在 CentOS Linux 中,PHP 5 的 PHP-FPM 設定檔案放在 /etc/php5/fpm/pool.d/www.conf,而 PHP 7 的 PHP-FPM 設定檔則是放在 /etc/opt/rh/rh-php71/php-fpm.d/www.conf。設定檔的實際位置也會跟安裝的方式有關,如果您系統上的 PHP-FPM 的設定檔位置不同,就自己尋找一下類似的路徑。

在調整 PHP-FPM 的行程管理規則時,建議可以開啟 PHP-FPM 服務的狀態監控網頁,方便觀察調整後的效果。

Process Manager 行程調配規則

process manager 有一系列的選項可以調整,首先找到 pm(process manager)參數:

pm = dynamic

pm 可用的值如下:

  • static:固定行程數量(數量為 pm.max_children)。
  • dynamic:動態行程數量(根據 pm.max_childrenpm.start_serverspm.min_spare_serverspm.max_spare_servers 動態調整)。
  • ondemand:動態行程數量(根據 pm.max_childrenpm.process_idle_timeout 動態調整)。

static 就是單純維持固定的 PHP-FPM 行程數量,若記憶體很充足的伺服器,可以考慮使用這種設定,效能會很好,不過缺點就是沒有流量時還是會佔用很多記憶體。

dynamicondemand 都會根據網路流量來動態調整 PHP-FPM 的行程數量。ondemand 就是很單純看需要多少行程就建立多少,沒用的就關閉,最節省記憶體,但是由於開關行程頻繁,所以效能較差。dynamic 可以設定一些規則,讓流量大的時候開啟多一點工作行程,而在沒有流量時,也會保留一些行程等待隨時接收新的連線,效能與耗費的記憶體都介於 staticondemand 之間,算是折衷的方案。

最大行程數量

pm.max_children 這個參數在 static 模式下就是代表開啟的行程數,而在 dynamicondemand 模式下,則是代表最大可開啟的行程數量:

pm.max_children = 50

這個值是最重要的,設太小的話會造成伺服器處理連線的速度下降,設太大的話會耗盡系統記憶體(造成當機),所以要依照自己系統上的資源來調配。

建議可以先用 top 指令,看看單一 PHP-FPM 行程大約佔用多少記憶體,然後再估算一下整個系統可以容納多少 PHP-FPM 的行程。

初始行程數量

pm.start_servers 可設定 PHP-FPM 服務在一開始啟動時,要配置多少個行程:

pm.start_servers = 5

最小閒置行程數量

pm.min_spare_servers 可設定 PHP-FPM 最小閒置行程的數量:

pm.min_spare_servers = 5

最大閒置行程數量

pm.max_spare_servers 可設定 PHP-FPM 最大閒置行程的數量:

pm.max_spare_servers = 35

PHP-FPM 單一行程可處理連線數

pm.max_requests 可設定單一 PHP-FPM 最多可以處理多少個連線,當一個工作行程處理的連線數達到這個值的時候,就會強制關閉此行程,重新產生另一個新的行程:

pm.max_requests = 500

重新啟動 PHP-FPM 服務

修改好 PHP-FPM 設定檔之後,重新啟動 PHP-FPM 服務,讓新設定生效:

# PHP 5
sudo systemctl restart php-fpm
# PHP 7
sudo systemctl restart rh-php71-php-fpm

檢查 PHP-FPM 是否正常啟動:

# PHP 5
sudo systemctl status php-fpm
# PHP 7
sudo systemctl status rh-php71-php-fpm

Nginx 設定技巧

以下是一些 Nginx 伺服器常用的設定配置技巧。

多網站設定配置

在 CentOS Linux 中安裝好 Nginx 之後,主要的設定檔都放在 /etc/nginx/nginx.conf,建議可以仿照 Debian/Ubuntu 的管理方式,建立兩個放置個別網站設定的目錄:

/etc/nginx/sites-available/
/etc/nginx/sites-enabled/

所有網站的設定檔都放在 /etc/nginx/sites-available/ 之中,對於要啟用的網站則在 /etc/nginx/sites-enabled/ 建立連結檔。

接著在 /etc/nginx/nginx.conf 中的 http 區塊尾端加上一個 include 設定:

http {

  # [略]

  include /etc/nginx/sites-enabled/*;
}

這樣就可以方便管理多個網站的設定了。

Worker 設定

Nginx 的 worker_processes 可以設定 worker 行程的數量,通常如果 CPU 沒有其他用途的話,可以把這個值設定成 CPU 的核心數:

# 4 核心的 CPU
worker_processes 4;

或是直接設定為 auto,讓 Nginx 自動偵測可用的 CPU 核心數:

# 自動偵測 CPU 核心數
worker_processes auto;

另外 worker_connections 可設定每個 worker 可同時處理的連線數上限值:

events {
  # 同時可處理 1024 條連線
  worker_connections 1024;
}

也就是說整台 Nginx 伺服器可以同時處理的連線數上限值即為 worker_processes * worker_connections

不顯示 Nginx 版本

顯示 Nginx 的詳細版本可能會暴露出伺服器可能的弱點,建議可以將其隱藏起來:

http {
  # 不顯示 Nginx 版本
  server_tokens off;
}

禁止存取隱藏檔

通常隱藏檔都是跟系統有關的檔案,不會是一般的網頁,所以禁止網頁伺服器顯示隱藏檔可以增加系統安全性:

server {

  # [略]

  location ~ /\. {
    access_log off;
    log_not_found off;
    deny all;
  }
}

檔案資訊快取

open_file_cache 是 Nginx 的檔案資訊快取功能,適用於有大量靜態檔案的網站,它會將常用的檔案資訊放入快取(並非將檔案內容放入快取),加速靜態檔案的處理速度。

http {

  # [略]

  # 檔案資訊快取
  open_file_cache max=1000 inactive=20s;
  open_file_cache_valid 30s;
  open_file_cache_min_uses 2;
  open_file_cache_errors on;
}

其他

events 的設定中,可以加入以下設定,可讓效能更好:

events {

  # [略]

  # 使用 epoll 效能較好
  use epoll;

  # 可同時接受多條連線
  multi_accept on;
}

參考資料:If Not True Then FalsehaydenjamesServers for HackersServers for Hackers