Ubuntu 22.04 Nginx Stream 四層代理

Ubuntu 22.04 Nginx Stream Module Layer 4 Proxy Configuration

Nginx Stream 模組提供了強大的四層(Layer 4)代理功能,可以代理 TCP 和 UDP 流量。與傳統的 HTTP 反向代理不同,Stream 模組直接在傳輸層運作,不需要解析應用層協定,因此具有更高的效能和更廣泛的應用場景。本文將詳細介紹如何在 Ubuntu 22.04 上設定 Nginx Stream 四層代理。

Nginx Stream 模組介紹

什麼是 Nginx Stream 模組?

Nginx Stream 模組是 Nginx 1.9.0 版本開始引入的功能,專門用於處理 TCP 和 UDP 流量的代理轉發。與 HTTP 模組處理七層(應用層)流量不同,Stream 模組運作在四層(傳輸層),直接轉發原始的 TCP/UDP 封包。

Stream 模組的主要功能

功能說明
TCP 代理轉發 TCP 連線到後端伺服器
UDP 代理轉發 UDP 資料報到後端伺服器
負載平衡在多個後端伺服器間分配流量
SSL/TLS 終止在代理層處理加密連線
SSL 透傳直接傳遞加密流量到後端
健康檢查監控後端伺服器狀態
存取控制基於 IP 位址限制存取

Stream 模組適用場景

  • 資料庫連線代理(MySQL、PostgreSQL、Redis)
  • 郵件伺服器代理(SMTP、IMAP、POP3)
  • SSH 連線轉發
  • DNS 代理(UDP)
  • 遊戲伺服器負載平衡
  • 任何需要 TCP/UDP 代理的應用程式

四層代理與七層代理差異

OSI 模型層級比較

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────────────┐
│  Layer 7 (Application)   │  HTTP, HTTPS, FTP, SMTP         │
├─────────────────────────────────────────────────────────────┤
│  Layer 6 (Presentation)  │  SSL/TLS, Encryption            │
├─────────────────────────────────────────────────────────────┤
│  Layer 5 (Session)       │  Session Management             │
├─────────────────────────────────────────────────────────────┤
│  Layer 4 (Transport)     │  TCP, UDP ◄── Stream 模組       │
├─────────────────────────────────────────────────────────────┤
│  Layer 3 (Network)       │  IP, ICMP                       │
├─────────────────────────────────────────────────────────────┤
│  Layer 2 (Data Link)     │  Ethernet, MAC                  │
├─────────────────────────────────────────────────────────────┤
│  Layer 1 (Physical)      │  Physical Media                 │
└─────────────────────────────────────────────────────────────┘

四層代理 vs 七層代理

特性四層代理(L4)七層代理(L7)
運作層級傳輸層(TCP/UDP)應用層(HTTP/HTTPS)
效能較高,不需解析應用協定較低,需解析 HTTP 內容
功能基於 IP 和 Port 轉發可基於 URL、Header、Cookie 轉發
靈活性較低,無法檢視應用內容較高,可進行內容修改
適用場景資料庫、郵件、SSHWeb 應用、API Gateway
SSL 處理可終止或透傳通常終止後處理

選擇建議

選擇四層代理的情況:

  • 代理非 HTTP 協定(如資料庫、郵件)
  • 需要最高效能的簡單轉發
  • SSL 透傳需求
  • 不需要檢視或修改應用層內容

選擇七層代理的情況:

  • 需要基於 URL 路由
  • 需要修改 HTTP Header
  • 需要 WebSocket 支援
  • 需要應用層的快取和壓縮

Stream 模組安裝與啟用

檢查 Stream 模組是否已安裝

首先檢查 Nginx 是否已包含 Stream 模組:

1
nginx -V 2>&1 | grep -o with-stream

如果輸出 with-stream,表示模組已安裝。

安裝包含 Stream 模組的 Nginx

在 Ubuntu 22.04 上,預設的 Nginx 套件可能不包含 Stream 模組。需要安裝 nginx-fullnginx-extras

1
2
3
4
5
6
7
8
# 更新套件列表
sudo apt update

# 安裝 nginx-full(包含 Stream 模組)
sudo apt install nginx-full -y

# 或者安裝 nginx-extras(包含更多模組)
sudo apt install nginx-extras -y

驗證安裝

1
2
3
4
5
6
# 確認 Nginx 版本和模組
nginx -V 2>&1 | grep -E "(version|with-stream)"

# 輸出範例:
# nginx version: nginx/1.18.0 (Ubuntu)
# --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module

建立 Stream 設定目錄

為了更好的組織設定檔,建立專用的 Stream 設定目錄:

1
2
3
4
5
# 建立 stream 設定目錄
sudo mkdir -p /etc/nginx/stream.d

# 確認目錄結構
ls -la /etc/nginx/

啟用 Stream 模組

編輯主設定檔 /etc/nginx/nginx.conf,在檔案最後加入 Stream 區塊:

1
sudo nano /etc/nginx/nginx.conf

在檔案結尾(http { } 區塊之外)加入:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Stream 模組設定
stream {
    # 日誌格式定義
    log_format stream_log '$remote_addr [$time_local] '
                          '$protocol $status $bytes_sent $bytes_received '
                          '$session_time "$upstream_addr" '
                          '"$upstream_bytes_sent" "$upstream_bytes_received" '
                          '"$upstream_connect_time"';

    # 載入 stream 設定檔
    include /etc/nginx/stream.d/*.conf;
}

測試設定並重新載入

1
2
3
4
5
6
7
8
# 測試 Nginx 設定語法
sudo nginx -t

# 重新載入設定
sudo systemctl reload nginx

# 確認 Nginx 狀態
sudo systemctl status nginx

TCP 代理基本設定

簡單 TCP 代理

以下範例展示如何設定一個簡單的 TCP 代理,將流量從本機的 3306 埠轉發到遠端 MySQL 伺服器:

建立設定檔:

1
sudo nano /etc/nginx/stream.d/mysql-proxy.conf

加入以下內容:

1
2
3
4
5
6
7
8
9
# MySQL TCP 代理
server {
    listen 3306;
    proxy_pass mysql-backend.example.com:3306;

    # 超時設定
    proxy_connect_timeout 10s;
    proxy_timeout 300s;
}

MySQL 代理完整範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# MySQL 資料庫代理設定
upstream mysql_cluster {
    server 192.168.1.10:3306 weight=5;
    server 192.168.1.11:3306 weight=5;
    server 192.168.1.12:3306 backup;
}

server {
    listen 3306;
    listen [::]:3306;

    proxy_pass mysql_cluster;

    # 連線設定
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    # 啟用 TCP keepalive
    proxy_socket_keepalive on;

    # 日誌設定
    access_log /var/log/nginx/mysql-proxy.log stream_log;
    error_log /var/log/nginx/mysql-proxy-error.log;
}

Redis 代理設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Redis 代理設定
upstream redis_servers {
    server 192.168.1.20:6379;
    server 192.168.1.21:6379;

    # 維持持久連線
    keepalive 32;
}

server {
    listen 6379;

    proxy_pass redis_servers;
    proxy_connect_timeout 5s;
    proxy_timeout 60s;

    # 連線限制
    proxy_buffer_size 16k;

    access_log /var/log/nginx/redis-proxy.log stream_log;
}

PostgreSQL 代理設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# PostgreSQL 代理設定
upstream postgresql_cluster {
    # 主資料庫(讀寫)
    server 192.168.1.30:5432 weight=10;
    # 從資料庫(讀取)
    server 192.168.1.31:5432 weight=5;
    server 192.168.1.32:5432 weight=5;
}

server {
    listen 5432;

    proxy_pass postgresql_cluster;
    proxy_connect_timeout 10s;
    proxy_timeout 600s;

    access_log /var/log/nginx/postgresql-proxy.log stream_log;
}

SSH 轉發設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# SSH 連線轉發
server {
    listen 2222;

    proxy_pass internal-server.local:22;
    proxy_connect_timeout 30s;
    proxy_timeout 3600s;  # SSH 連線可能持續很長時間

    # 啟用 TCP keepalive 防止連線中斷
    proxy_socket_keepalive on;

    access_log /var/log/nginx/ssh-proxy.log stream_log;
}

UDP 代理設定

UDP 代理基礎設定

UDP 代理需要使用 udp 參數。以下是 DNS 代理的範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# DNS UDP 代理
upstream dns_servers {
    server 8.8.8.8:53;
    server 8.8.4.4:53;
}

server {
    listen 53 udp;

    proxy_pass dns_servers;
    proxy_timeout 5s;
    proxy_responses 1;  # 期望的回應數量

    access_log /var/log/nginx/dns-proxy.log stream_log;
}

UDP 代理重要參數

參數說明
udp指定監聽 UDP 協定
proxy_responses期望從後端接收的 UDP 回應數量
proxy_timeout等待回應的超時時間
reuseport允許多個 worker 監聯同一埠

DNS 代理完整設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# DNS 代理(同時支援 UDP 和 TCP)
upstream dns_upstream {
    server 8.8.8.8:53 weight=5;
    server 8.8.4.4:53 weight=5;
    server 1.1.1.1:53 backup;
}

# UDP DNS 代理
server {
    listen 53 udp reuseport;

    proxy_pass dns_upstream;
    proxy_timeout 5s;
    proxy_responses 1;

    access_log /var/log/nginx/dns-udp.log stream_log;
}

# TCP DNS 代理(用於大型 DNS 回應)
server {
    listen 53;

    proxy_pass dns_upstream;
    proxy_timeout 5s;

    access_log /var/log/nginx/dns-tcp.log stream_log;
}

Syslog UDP 代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Syslog UDP 代理
upstream syslog_servers {
    server 192.168.1.50:514;
    server 192.168.1.51:514;
}

server {
    listen 514 udp;

    proxy_pass syslog_servers;
    proxy_timeout 1s;
    proxy_responses 0;  # Syslog 不需要回應

    access_log off;  # 避免 log 迴圈
}

遊戲伺服器 UDP 代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 遊戲伺服器 UDP 代理
upstream game_servers {
    hash $remote_addr consistent;  # 確保同一用戶連到同一伺服器
    server 192.168.1.60:27015;
    server 192.168.1.61:27015;
    server 192.168.1.62:27015;
}

server {
    listen 27015 udp reuseport;

    proxy_pass game_servers;
    proxy_timeout 30s;
    proxy_responses 1;

    # 緩衝區設定
    proxy_buffer_size 64k;

    access_log /var/log/nginx/game-server.log stream_log;
}

負載平衡設定

負載平衡演算法

Stream 模組支援多種負載平衡演算法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. Round Robin(預設)- 輪詢
upstream backend_rr {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 2. Least Connections - 最少連線
upstream backend_lc {
    least_conn;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 3. IP Hash - 基於用戶端 IP 的一致性雜湊
upstream backend_hash {
    hash $remote_addr consistent;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 4. Least Time(Nginx Plus 功能)
# upstream backend_lt {
#     least_time connect;
#     server 192.168.1.10:8080;
#     server 192.168.1.11:8080;
# }

權重設定

1
2
3
4
5
upstream weighted_backend {
    server 192.168.1.10:8080 weight=5;   # 接收 50% 的流量
    server 192.168.1.11:8080 weight=3;   # 接收 30% 的流量
    server 192.168.1.12:8080 weight=2;   # 接收 20% 的流量
}

伺服器參數

1
2
3
4
5
6
upstream backend_params {
    server 192.168.1.10:8080 weight=5 max_conns=100 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=3 max_conns=100 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 backup;   # 備援伺服器
    server 192.168.1.13:8080 down;     # 標記為離線
}
參數說明
weight伺服器權重(預設為 1)
max_conns最大同時連線數
max_fails失敗次數上限
fail_timeout失敗後暫停時間
backup備援伺服器
down標記為離線
slow_start緩慢啟動時間(Nginx Plus)

完整負載平衡範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 資料庫負載平衡設定
upstream mysql_cluster {
    least_conn;

    # 主伺服器群組
    server 192.168.1.10:3306 weight=10 max_conns=500 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:3306 weight=10 max_conns=500 max_fails=3 fail_timeout=30s;

    # 從伺服器群組
    server 192.168.1.20:3306 weight=5 max_conns=200 max_fails=3 fail_timeout=30s;
    server 192.168.1.21:3306 weight=5 max_conns=200 max_fails=3 fail_timeout=30s;

    # 備援伺服器
    server 192.168.1.30:3306 backup;
}

server {
    listen 3306;

    proxy_pass mysql_cluster;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    # 連線限制
    proxy_next_upstream on;
    proxy_next_upstream_timeout 60s;
    proxy_next_upstream_tries 3;

    access_log /var/log/nginx/mysql-lb.log stream_log;
}

SSL/TLS 終止與透傳

SSL 終止(SSL Termination)

SSL 終止是在 Nginx 代理層解密 SSL 流量,然後以純文字形式轉發到後端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# SSL 終止設定
upstream backend_servers {
    server 192.168.1.10:3306;
    server 192.168.1.11:3306;
}

server {
    listen 3307 ssl;

    # SSL 憑證
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # SSL 設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # 轉發到後端(純文字)
    proxy_pass backend_servers;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/ssl-termination.log stream_log;
}

SSL 透傳(SSL Passthrough)

SSL 透傳直接將加密流量轉發到後端,Nginx 不進行解密:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# SSL 透傳設定
upstream https_backend {
    server 192.168.1.10:443;
    server 192.168.1.11:443;
}

server {
    listen 443;

    # 直接透傳 SSL 流量
    proxy_pass https_backend;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    # 使用 SSL preread 獲取 SNI
    ssl_preread on;

    access_log /var/log/nginx/ssl-passthrough.log stream_log;
}

基於 SNI 的路由

使用 ssl_preread 模組可以根據 SNI(Server Name Indication)進行路由:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# SNI 路由映射
map $ssl_preread_server_name $backend_pool {
    default          default_backend;
    app1.example.com app1_backend;
    app2.example.com app2_backend;
    db.example.com   database_backend;
}

# 後端群組定義
upstream default_backend {
    server 192.168.1.10:443;
}

upstream app1_backend {
    server 192.168.1.20:443;
    server 192.168.1.21:443;
}

upstream app2_backend {
    server 192.168.1.30:443;
    server 192.168.1.31:443;
}

upstream database_backend {
    server 192.168.1.40:3306;
}

# SSL 路由伺服器
server {
    listen 443;

    ssl_preread on;
    proxy_pass $backend_pool;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/sni-routing.log stream_log;
}

雙向 SSL(mTLS)設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# mTLS 設定
server {
    listen 8443 ssl;

    # 伺服器憑證
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # 用戶端憑證驗證
    ssl_client_certificate /etc/nginx/ssl/ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    # SSL 協定設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    proxy_pass backend_servers;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/mtls.log stream_log;
}

轉發至 SSL 後端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 代理層連接 SSL 後端
upstream ssl_backend {
    server 192.168.1.10:3307;
}

server {
    listen 3306;

    proxy_pass ssl_backend;
    proxy_ssl on;

    # 後端 SSL 設定
    proxy_ssl_certificate /etc/nginx/ssl/client.crt;
    proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
    proxy_ssl_trusted_certificate /etc/nginx/ssl/ca.crt;
    proxy_ssl_verify on;
    proxy_ssl_verify_depth 2;
    proxy_ssl_session_reuse on;

    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/ssl-backend.log stream_log;
}

健康檢查設定

被動健康檢查

Nginx 開源版本支援被動健康檢查,根據連線失敗自動標記伺服器:

1
2
3
4
5
upstream backend_passive {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 max_fails=3 fail_timeout=30s backup;
}
參數說明
max_fails達到此失敗次數後標記伺服器為不可用
fail_timeout失敗計數視窗和伺服器不可用持續時間

自訂健康檢查腳本

雖然開源版 Nginx 不支援主動健康檢查,但可以透過外部腳本實現:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/bash
# /usr/local/bin/nginx-health-check.sh

# 設定
NGINX_CONF="/etc/nginx/stream.d/backend.conf"
SERVERS=("192.168.1.10:3306" "192.168.1.11:3306" "192.168.1.12:3306")
CHECK_INTERVAL=10
TIMEOUT=5

check_server() {
    local server=$1
    local host=$(echo $server | cut -d: -f1)
    local port=$(echo $server | cut -d: -f2)

    if nc -z -w$TIMEOUT $host $port 2>/dev/null; then
        return 0
    else
        return 1
    fi
}

update_config() {
    local server=$1
    local status=$2

    if [ "$status" == "down" ]; then
        sed -i "s/server $server;/server $server down;/" $NGINX_CONF
    else
        sed -i "s/server $server down;/server $server;/" $NGINX_CONF
    fi

    nginx -t && systemctl reload nginx
}

while true; do
    for server in "${SERVERS[@]}"; do
        if check_server $server; then
            # 伺服器健康,確保沒有 down 標記
            if grep -q "server $server down;" $NGINX_CONF; then
                echo "$(date): Server $server is back online"
                update_config $server "up"
            fi
        else
            # 伺服器不健康,標記為 down
            if ! grep -q "server $server down;" $NGINX_CONF; then
                echo "$(date): Server $server is down"
                update_config $server "down"
            fi
        fi
    done
    sleep $CHECK_INTERVAL
done

設定為系統服務:

1
sudo nano /etc/systemd/system/nginx-health-check.service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=Nginx Health Check Service
After=nginx.service

[Service]
Type=simple
ExecStart=/usr/local/bin/nginx-health-check.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

啟用服務:

1
2
3
4
sudo chmod +x /usr/local/bin/nginx-health-check.sh
sudo systemctl daemon-reload
sudo systemctl enable nginx-health-check
sudo systemctl start nginx-health-check

使用外部監控工具

搭配 Prometheus 和 Nginx Exporter 進行監控:

1
2
3
4
# 安裝 nginx-prometheus-exporter
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
tar xzf nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
sudo mv nginx-prometheus-exporter /usr/local/bin/

在 Nginx 設定中啟用 stub_status:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 在 http 區塊中加入
server {
    listen 127.0.0.1:8080;

    location /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}

實際應用場景

場景一:MySQL 讀寫分離代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# MySQL 讀寫分離設定
# 寫入流量 -> 主資料庫
upstream mysql_master {
    server 192.168.1.10:3306 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:3306 backup;
}

# 讀取流量 -> 從資料庫群組
upstream mysql_slaves {
    least_conn;
    server 192.168.1.20:3306 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.21:3306 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.22:3306 weight=5 max_fails=3 fail_timeout=30s;
}

# 寫入代理(連接埠 3306)
server {
    listen 3306;

    proxy_pass mysql_master;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/mysql-write.log stream_log;
}

# 讀取代理(連接埠 3307)
server {
    listen 3307;

    proxy_pass mysql_slaves;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/mysql-read.log stream_log;
}

場景二:Redis Cluster 代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Redis Cluster 代理設定
upstream redis_cluster {
    hash $remote_addr consistent;

    server 192.168.1.30:6379 max_fails=2 fail_timeout=10s;
    server 192.168.1.31:6379 max_fails=2 fail_timeout=10s;
    server 192.168.1.32:6379 max_fails=2 fail_timeout=10s;
    server 192.168.1.33:6379 max_fails=2 fail_timeout=10s;
    server 192.168.1.34:6379 max_fails=2 fail_timeout=10s;
    server 192.168.1.35:6379 max_fails=2 fail_timeout=10s;
}

server {
    listen 6379;

    proxy_pass redis_cluster;
    proxy_connect_timeout 5s;
    proxy_timeout 60s;

    # 提高效能
    proxy_socket_keepalive on;

    access_log /var/log/nginx/redis-cluster.log stream_log;
}

場景三:多租戶資料庫代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 基於來源 IP 的多租戶資料庫路由
map $remote_addr $tenant_backend {
    default           default_db;
    ~^10\.0\.1\.      tenant_a_db;
    ~^10\.0\.2\.      tenant_b_db;
    ~^10\.0\.3\.      tenant_c_db;
}

upstream default_db {
    server 192.168.1.100:3306;
}

upstream tenant_a_db {
    server 192.168.1.101:3306;
    server 192.168.1.102:3306;
}

upstream tenant_b_db {
    server 192.168.1.103:3306;
    server 192.168.1.104:3306;
}

upstream tenant_c_db {
    server 192.168.1.105:3306;
}

server {
    listen 3306;

    proxy_pass $tenant_backend;
    proxy_connect_timeout 10s;
    proxy_timeout 300s;

    access_log /var/log/nginx/multi-tenant.log stream_log;
}

場景四:SSH Jump Host

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# SSH Jump Host 設定
# 透過不同埠號連接不同內部伺服器

# 內部 Web 伺服器
server {
    listen 2201;
    proxy_pass 10.0.1.10:22;
    proxy_timeout 3600s;
    access_log /var/log/nginx/ssh-web.log stream_log;
}

# 內部資料庫伺服器
server {
    listen 2202;
    proxy_pass 10.0.1.20:22;
    proxy_timeout 3600s;
    access_log /var/log/nginx/ssh-db.log stream_log;
}

# 內部應用伺服器
server {
    listen 2203;
    proxy_pass 10.0.1.30:22;
    proxy_timeout 3600s;
    access_log /var/log/nginx/ssh-app.log stream_log;
}

場景五:郵件伺服器代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# SMTP 代理
upstream smtp_servers {
    server 192.168.1.50:25;
    server 192.168.1.51:25;
}

server {
    listen 25;
    proxy_pass smtp_servers;
    proxy_timeout 300s;
    access_log /var/log/nginx/smtp.log stream_log;
}

# SMTPS 代理
server {
    listen 465 ssl;

    ssl_certificate /etc/nginx/ssl/mail.crt;
    ssl_certificate_key /etc/nginx/ssl/mail.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    proxy_pass smtp_servers;
    proxy_timeout 300s;
    access_log /var/log/nginx/smtps.log stream_log;
}

# IMAP 代理
upstream imap_servers {
    server 192.168.1.50:143;
    server 192.168.1.51:143;
}

server {
    listen 143;
    proxy_pass imap_servers;
    proxy_timeout 600s;
    access_log /var/log/nginx/imap.log stream_log;
}

# IMAPS 代理
server {
    listen 993 ssl;

    ssl_certificate /etc/nginx/ssl/mail.crt;
    ssl_certificate_key /etc/nginx/ssl/mail.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    proxy_pass imap_servers;
    proxy_timeout 600s;
    access_log /var/log/nginx/imaps.log stream_log;
}

場景六:遊戲伺服器負載平衡

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 遊戲伺服器負載平衡
# 使用一致性雜湊確保玩家連接到同一伺服器

upstream game_tcp {
    hash $remote_addr consistent;
    server 192.168.1.60:27015 weight=10 max_conns=1000;
    server 192.168.1.61:27015 weight=10 max_conns=1000;
    server 192.168.1.62:27015 weight=10 max_conns=1000;
    server 192.168.1.63:27015 backup;
}

upstream game_udp {
    hash $remote_addr consistent;
    server 192.168.1.60:27015;
    server 192.168.1.61:27015;
    server 192.168.1.62:27015;
}

# TCP 連線
server {
    listen 27015;
    proxy_pass game_tcp;
    proxy_timeout 86400s;
    proxy_socket_keepalive on;
    access_log /var/log/nginx/game-tcp.log stream_log;
}

# UDP 連線
server {
    listen 27015 udp reuseport;
    proxy_pass game_udp;
    proxy_timeout 30s;
    proxy_responses 1;
    access_log /var/log/nginx/game-udp.log stream_log;
}

效能調校

Nginx Worker 設定

/etc/nginx/nginx.conf 中優化 worker 設定:

1
2
3
4
5
6
7
8
9
# 全域設定
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 65535;
    use epoll;
    multi_accept on;
}

系統核心參數調校

1
sudo nano /etc/sysctl.d/99-nginx-stream.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 網路效能調校
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# TCP 連線設定
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15

# 檔案描述符限制
fs.file-max = 2097152

套用設定:

1
sudo sysctl -p /etc/sysctl.d/99-nginx-stream.conf

檔案描述符限制

1
sudo nano /etc/security/limits.d/nginx.conf
1
2
3
4
nginx soft nofile 65535
nginx hard nofile 65535
* soft nofile 65535
* hard nofile 65535

常見問題排解

檢查設定語法

1
sudo nginx -t

查看日誌

1
2
3
4
5
6
7
8
# 錯誤日誌
sudo tail -f /var/log/nginx/error.log

# Stream 存取日誌
sudo tail -f /var/log/nginx/mysql-proxy.log

# 即時監控所有日誌
sudo tail -f /var/log/nginx/*.log

連線問題排解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 檢查 Nginx 是否正在監聽
sudo ss -tlnp | grep nginx

# 檢查防火牆規則
sudo ufw status
sudo iptables -L -n

# 測試連線
nc -zv localhost 3306

# 檢查後端服務狀態
nc -zv 192.168.1.10 3306

常見錯誤訊息

錯誤可能原因解決方法
connect() failed後端伺服器未啟動或無法連線確認後端服務正常運行
no live upstreams所有後端伺服器都已標記為不可用檢查後端健康狀態
upstream timed out連線或讀取超時調整 proxy_timeout 設定
bind() failed埠號已被占用檢查埠號使用情況

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy