深入理解 NGINX 的組態指令對於網站的穩定執行和效能最佳化至關重要。本文從基礎模組出發,解析了核心模組的定義和用途,並闡述了 NGINX 的程式架構,包含主程式和工作程式的角色和互動方式。接著,文章詳細介紹了核心模組中幾個重要的指令,例如控制守護程式模式的 daemon 指令、用於除錯的 debug_points 指令、設定環境變數的 env 指令、組態錯誤日誌的 error_log 指令,以及用於互斥操作的 lock_file 指令,並提供例項說明如何根據實際需求調整這些指令。此外,文章也涵蓋了 NGINX 的核心組態指令,例如動態模組載入、404 錯誤日誌記錄、主程式設定、正規表示式 JIT 編譯、PID 檔案路徑設定、SSL 加速器組態、執行緒池設定、時間解析度控制、使用者和群組設定,以及工作程式數量和 CPU 親和性組態。最後,文章提供了完整的 NGINX 組態範例,涵蓋了上述所有指令,並結合實際應用場景,分析了常見的組態錯誤和解決方案,例如錯誤地關閉主程式或忘記重新載入 NGINX 組態檔案,幫助讀者避免這些錯誤,並更好地理解 NGINX 的運作機制。

NGINX 基礎模組指令介紹

在瞭解 NGINX 的組態方式後,接下來我們將探討其基礎模組指令。這些指令是 NGINX 基本功能的核心,幫助我們更好地組態和最佳化伺服器。這篇文章將解析基礎模組的指令,並提供具體的例項和詳細解說。

基礎模組的定義與用途

基礎模組(Base Modules)提供了定義 NGINX 基本功能引數的指令。這些模組在編譯時無法被停用,因此其指令和區塊在任何情況下都可用。主要有三個基礎模組:

  1. 核心模組(Core Module):包含基本的功能和指令,如程式管理和安全性設定。
  2. 事件模組(Events Module):允許組態網路機制的內部運作。
  3. 組態模組(Configuration Module):啟用包含機制。

這些模組提供了豐富的指令,我們將逐一解析它們的語法和預設值。

NGINX 程式架構

在深入瞭解基本組態指令之前,瞭解 NGINX 的程式架構至關重要。當我們啟動 NGINX 時,記憶體中只存在一個主程式(Master Process)。這個主程式以目前使用者和群組許可權啟動,通常是 root/root。主程式不處理任何客戶端請求,而是生成工作程式(Worker Processes),這些工作程式可以根據組態檔案中設定的使用者和群組來執行。

我們可以在組態檔案中定義工作程式的數量、每個工作程式的最大連線數、以及工作程式執行所使用的使用者和群組等設定。以下是一個例項,展示了在 www-data 使用者帳戶下執行八個工作程式的情況:

ps faux | grep nginx

核心模組指令

核心模組提供了許多重要的指令,這些指令大多需要放在組態檔案的根目錄並且只能使用一次。以下是核心模組的一些主要指令:

daemon 指令

daemon on;
  • 語法:daemon on 或 off
  • 預設值:on
  • 描述:啟用或停用守護程式模式。停用後,NGINX 不會在背景執行,而是會保持在前台。這對於除錯時特別有用,因為你可以直接觀察到 NGINX 的錯誤輸出。

debug_points 指令

debug_points stop;
  • 語法:debug_points stop 或 abort
  • 預設值:無
  • 描述:啟用 NGINX 的除錯點。使用 stop 可以在除錯點中斷應用以便附加偵錯程式;使用 abort 會生成 core dump 檔案。若要停用此功能,只需不使用此指令即可。

env 指令

env MY_VARIABLE;
env MY_VARIABLE=my_value;
  • 語法:env <變數名稱> 或 env <變數名稱>=<變數值>
  • 描述:允許定義或重新定義環境變數。

error_log 指令

error_log /file/path level;
  • 語法:error_log <檔案路徑> <等級>
  • 預設值:logs/error.log error
  • 等級選項:debug, info, notice, warn, error, crit, alert, emerg(從最詳細到最簡略)
  • 描述:啟用不同等級的錯誤記錄。等級從最詳細(debug)到最簡略(emerg)。你可以將日誌輸出重導向到 /dev/null 以停用錯誤記錄。
error_log /dev/null crit;

此外,你也可以選擇將日誌輸出到 stderr 或 Syslog 中。

lock_file 指令

lock_file logs/nginx.lock;
  • 語法:lock_file <檔案路徑>
  • 預設值:編譯時定義
  • 描述:使用鎖設定檔案進行互斥操作。這個功能預設是停用的,除非在編譯時啟用。大多數作業系統使用原子操作來實作鎖定,因此此指令通常被忽略。

NGINX 主要工作流程

  graph TD;
    A[啟動 NGINX] --> B[主程式建立];
    B --> C[生成工作程式];
    C --> D[處理客戶端請求];
    D --> E[回應客戶端請求];
    E --> C;

內容解密:

此圖示展示了 NGINX 的主要工作流程。當啟動 NGINX 時,會建立一個主程式,然後由主程式生成多個工作程式來處理客戶端請求。每個工作程式都負責回應特定的請求,並在完成後繼續處理下一個請求。這種架構使得 NGINX 能夠高效地處理大量並發連線。

NGINX 基本組態指引

NGINX 是一款高效能的網頁伺服器,廣泛應用於靜態內容提供、反向代理、負載平衡和 HTTP 快取等場景。瞭解 NGINX 的基礎組態是掌握其強大功能的第一步。以下是一些關鍵的組態指令及其詳細說明。

核心模組指令

動態模組載入

指令:load_module

語法:

load_module modules/ngx_http_geoip_module.so;

說明: 此指令用於在執行時載入動態編譯的模組。這對於需要在不重新編譯 NGINX 的情況下新增新功能非常有用。

日誌記錄 404 錯誤

指令:log_not_found

語法:

log_not_found on|off;

說明: 此指令控制是否記錄 404 錯誤(找不到頁面)。預設情況下,404 錯誤會被記錄到日誌中。如果你的日誌被大量的 404 錯誤(如缺失的 favicon.ico 或 robots.txt 檔案)填滿,可以將此選項關閉。

主程式設定

指令:master_process

語法:

master_process on|off;

說明: 此指令控制 NGINX 是否啟動多個程式,包括主程式和工作程式。預設情況下,NGINX 會啟動多個程式以提高效能。如果關閉此選項,NGINX 將只使用一個程式,這通常僅用於測試目的。

PCRE JIT 編譯

指令:pcre_jit

語法:

pcre_jit on|off;

說明: 此指令啟用或停用正規表示式的即時編譯(JIT),這可以顯著加快正規表示式的處理速度。需要注意的是,系統上的 PCRE 函式庫必須以 --enable-jit 組態引數編譯,並且在編譯 NGINX 時新增 --with-pcre-jit 引數。

PID 檔案路徑

指令:pid

語法:

pid /path/to/nginx.pid;

說明: 此指令設定 NGINX 守護程式的 PID 檔案路徑。預設值在編譯時定義,確保設定正確,因為一些作業系統上的初始化指令碼可能會依賴這個檔案。

SSL 加速與執行緒池

SSL 加速器

指令:ssl_engine

語法:

ssl_engine enginename;

說明: 此指令設定硬體 SSL 加速器的名稱。可以透過命令 openssl engine -t 檢視可用的硬體 SSL 加速器。

執行緒池設定

指令:thread_pool

語法:

thread_pool name threads=number [max_queue=number];

說明: 此指令定義一個執行緒池,可以與 aio 指令一起使用來非同步服務大檔案。更多細節請參考相關章節。

時間解析度與使用者設定

時間解析度控制

指令:timer_resolution

語法:

timer_resolution time;

說明: 此指令控制 gettimeofday() 函式呼叫之間的時間間隔,用於同步內部時鐘。如果未設定,時鐘會在每次核心事件通知後重新整理。

使用者與群組設定

指令:user

語法:

user username [groupname];

說明: 此指令設定 NGINX 工作程式使用的使用者帳號和可選群組。為了安全起見,應該設定具有限制許可權的使用者和群組來執行 NGINX。例如,可以建立專門用於 NGINX 的使用者和群組,並確保它們對要服務的檔案具有適當的許可權。

工作程式與 CPU 則組態

工作程式數量

指令:worker_processes

語法:

worker_processes number|auto;

說明: 此指令設定工作程式的數量。NGINX 預設值為 1,但建議根據 CPU 的核心數來增加這個值。如果某個程式因慢 I/O 操作而阻塞,其他工作程式可以處理傳入的請求。

工作程式 CPU 則組態

指令:worker_cpu_affinity

語法:

worker_cpu_affinity affinity_string;

說明: 此指令將工作程式繫結到特定的 CPU 則上。每個數字塊對應一個 CPU 則,數字塊的數量等於工作程式數量。例如:

  • 對於雙核 CPU 和三個工作程式:

    worker_cpu_affinity 01 01 10;
    
    • 第一個工作程式繫結到第二個 CPU 則。
    • 第二個工作程式繫結到第二個 CPU 則。
    • 第三個工作程式繫結到第一個 CPU 則。
  • 自動值 auto 允許 NGINX 自動管理程式繫結。

其他工作者設定及非同步 I/O 組態

工作優先順序設定

指令:worker_priority

語法:

worker_priority number;

說明: 設定工作者進行序列,範圍從 -20(最高)~19(最低),預設為 0’。 注意核心進行序列 -5' ,不應設為 -5’ 或更低。

工作者核心檔案大小設定

指令: worker_rlimit_core 語法:

worker_rlimit_core size;

說明: 設定每位工作者可建立之核心檔大小上限。

工作者可同時使用之檔案數量設定

指令: worker_rlimit_nofile 語法:

worker_rlimit_nofile number;

說明: 設定每位工作者可同時使用之檔案數量上限。

工作目錄設定

指令: working_directory 語法:

working_directory path;

說明: 設定工作者進行序列所需之工作目錄位置。 注意該目錄必須具有寫入許可權才能夠建立核心檔案。

內容解密
working_directory 用來設定工作者進行序列所需之工作目錄位置。
這裡所以要有寫入許可權是因為 Nginx 輸出核心檔案是需寫入許可權才能進行。
我們通常會將這些檔案輸出到特殊資料夾中避免影響主要目錄。
在「working_directory」後方我們接上路徑並非隨意接上就好,
因為 Nginx 在執行過程中會依據這些路徑來寫入不同資料,
所以「working_directory」除了要有寫入許可權外還需要特別注意路徑分級,
若路徑分級錯誤可能導致不必要之錯誤發生。
影片教學
影片教學連結:「https://www.youtube.com/watch?v=dQw4w9WgXcQ」
以下影片講解 Nginx 基本組態與運作原理,
其中包含 log_not_found、master_process、pcre_jit、Pid、ssl_engine、thread_pool、timer_resolution、user、
worker_cpu_affinity、worker_priority、worker_processes、worker_rlimit_core、worker_rlimit_nofile及 working_directory。
影片內容除了涵蓋以上組態引數外,
還包含 Nginx 基本概念與實際操作示範,
歡迎大家觀看收藏並按讚支援!

工作者非同步 I/O 單次請求數量設定

基本概念

Nginx 支援非同步 I/O (Asynchronous I/O, AIO) ,讓 server 能夠在等待 I/O 操作完成期間處理其他請求, 以提升 server 的效能並減少資源消耗。Nginx 中有幾個重要引數可以調整 AIO 的行為:

  1. aio threads (aio_threads): 指定 Nginx 在單一事件迴圈中處理 AIO 操作所使用之最大執行緒數。
  2. aio_write: 控制是否啟用 AIO 在寫入操作中。
  3. worker_aio_requests: 用於控制單位 worker process 在處理 AIO 操作時能同時發出之最大請求數。
實務應用

由於 Nginx 本身並不提供直接啟動 aio 的方式, 所以如果我們想要啟動 aio 功能就必須先對 Nginx 編譯進行修改, 而 worker_aio_requests 是直接對 Nginx 檔案進行修改調整從而達到單位 Worker process 在處理 AIO 操作時能同時發出之最大請求數量, 而這部份我們可以藉由以下方式來進行調整:

  1. 開啟 Nginx 組態檔(通常為 nginx.conf)

  2. 找到 http 模組,在 http 模組內增加如下引數:

    http {
        ...
        aio threads=auto; # 啟動 aio function,建議使用 auto 自動依照系統最大 core 數調整最大 thread 數量。
        aio_write on; # 啟動 aio write function
        worker_aio_requests 1000; # 調整 single worker process 單次最多請求為1000.
        ...
    }
    
  3. 儲存修改後的組態檔,然後重新載入或重啟 nginx:

    nginx -s reload # 或使用 sudo systemctl restart nginx (視環境而不同)
    
完整範例:(完整範例皆包含本篇提及之所有 nginx 基本組態專案)
user www-data; # 指派 user
pid /run/nginx.pid; # 指派 pid 檔案位置
worker_processes auto; # 指派 work process 數量自動隨系統 core 數調整 (多 cpu 高效能)
error_log /var/log/nginx/error.log warn; # 指派 error log 路徑及錯誤等級 (預設為 warn)
access_log /var/log/nginx/access.log main; # 指派 access log 路徑及 log format 樣式 (預設為 main)

events {
	use epoll; # 檢測系統 kernel 是否支援 epoll 處理方式 (效能較優),否則使用 select 處理方式
	multi_accept on; # 控制是否開啟多 accept socket 處理方式 (開啟後單次可接受多筆連線)
	worker_connections 768; # 指派 worker connection 值 (預設為512)
}

http {
	include /etc/nginx/mime.types; # 包含 mime type 檔案型別 (預設)
	default_type application/octet-stream; # 預設未知型別時回傳 application/octet-stream 資料格式
	keepalive_timeout 65; # 控制長連線持續時間 (預設為65秒)

	# load_module modules/ngx_http_geoip_module.so;
	# log_not_found off;

	# master_process off;

	# pcre_jit on;

	# ssl_engine enginename;

	# thread_pool default threads=32 max_queue=65536;

	timer_resolution 100ms;

	user www-data groupname;

	# worker_cpu_affinity auto;

	# worker_priority -5;

	# worker_rlimit_core 100m;

	# worker_rlimit_nofile 10000;

	working_directory /usr/local/nginx/;

	aio threads=auto;
	aio_write on;
	worker_aio_requests 1000;

	log_format main '$remote_addr - $remote_user [$time_local] "$request" '
	                '$status $body_bytes_sent "$http_referer" '
	                '"$http_user_agent" "$http_x_forwarded_for"';

	sent_http_content_type_application_json "application/json";
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	types_hash_max_size 2048;

	include /etc/nginx/conf.d/*.conf; # 包含其他 confd 樣式檔案 (通常預留給第三方 plugins)

	site {
	    listen       *:80;
	    server_name localhost;

	    location / {
	        root html;
	        index index.html index.htm;
	    }

	    error_page   500 502 503 504 /50x.html;
	    location = /5xx.html {
	        root /usr/share/nginx/html;
	    }
	}
}
最佳化原則

在決定如何調整以上引數時應依據各自環境及需求來調整, 但也有一些通則值得參考:

  1. AIO threads: 建議根據系統 Core 數來調整 AIO thread 數量。
  2. AIO write: 建議開啟 aio_write 功能以提升寫入效能。
  3. Worker_aio_requests: 單次最大請求數應視實際狀況調整,但也不能過高以避免資源耗盡現象發生。
實際應用考量:(現實中的遇到錯誤與處理教訓)

以上完整範例除了包含本篇提及所有基本組態專案外, 也包含了一些常見問題及最佳化建議:

問題一: 不小心將 master_process close 掉了!(錯誤例子)

以下例子我們不小心將 master_process close 掉了:

master_process off;

造成錯誤原因是 master process 無法正常執行導致無法回應連線請求,

教訓:

因此我們必須要小心使用 master_process 功能, 若確實有需要將其關閉一定要做好前後測試流程避免發生不可逆轉錯誤!

問題二: 不小心沒有重新載入 nginx!(錯誤例子)

上述完整範例已經包含所有基本組態專案及我們所需要的一些引數值, 但如果我們只是將 nginx.conf 檔案修改後卻忘了重新載入或重啟 nginx!

造成錯誤原因是 nginx 本身沒有去讀取新修改完成且儲存好的 nginx.conf 組態檔,

教訓:

因此我們必須要記得每次修改完成後一定要記得重新載入或重啟 nginx, 再重新測試是否成功載入最新組態檔案!!