Nginx反向代理+負載均衡實戰(zhàn):小白也能搭建高可用架構(gòu)
一、概述
1.1 背景介紹
2023年我?guī)Я藗€新人,讓他搭個負載均衡,結(jié)果他配了半天,流量全跑到一臺機器上,另外兩臺閑著。排查了一個小時,發(fā)現(xiàn)他把upstream里的server全寫成了localhost。這事兒讓我意識到,反向代理和負載均衡看著簡單,坑還真不少。
對于很多剛?cè)胄械耐瑢W(xué)來說,"反向代理"這詞聽著就讓人頭大。其實沒那么復(fù)雜:正向代理是代理客戶端(比如VPN),反向代理是代理服務(wù)端。用戶訪問的是Nginx,Nginx再去訪問真正的后端服務(wù),用戶根本不知道后端服務(wù)的存在。
負載均衡則是反向代理的進階玩法。當(dāng)一臺后端服務(wù)器扛不住流量的時候,就需要多臺服務(wù)器一起分擔(dān)壓力。Nginx負責(zé)把請求分發(fā)到不同的服務(wù)器上,這就是負載均衡。
1.2 技術(shù)特點
反向代理的價值:
隱藏后端服務(wù)器真實IP,增強安全性
統(tǒng)一入口,便于管理和監(jiān)控
SSL卸載,減輕后端壓力
靜態(tài)資源緩存,減少后端請求
請求過濾和限流
負載均衡的價值:
水平擴展,突破單機性能瓶頸
高可用,單點故障不影響服務(wù)
靈活調(diào)度,支持多種算法
灰度發(fā)布,逐步遷移流量
1.3 適用場景
Web應(yīng)用集群部署
API網(wǎng)關(guān)入口
微服務(wù)架構(gòu)流量分發(fā)
靜態(tài)資源與動態(tài)請求分離
多數(shù)據(jù)中心流量調(diào)度
藍綠部署和灰度發(fā)布
1.4 環(huán)境要求
| 組件 | 版本 | 說明 |
|---|---|---|
| 操作系統(tǒng) | Rocky Linux 9.4 / Ubuntu 24.04 LTS | 穩(wěn)定的生產(chǎn)環(huán)境系統(tǒng) |
| Nginx | 1.26.2 | 建議使用stable分支 |
| 后端服務(wù) | 任意HTTP服務(wù) | 示例用Tomcat 10.1.x |
| 網(wǎng)絡(luò) | 內(nèi)網(wǎng)互通 | Nginx與后端服務(wù)器網(wǎng)絡(luò)延遲<1ms |
實驗環(huán)境架構(gòu):
┌─────────────────┐
│ Client請求 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Nginx負載均衡 │
│ 192.168.1.10 │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Backend 1 │ │ Backend 2 │ │ Backend 3 │
│ 192.168.1.11│ │ 192.168.1.12│ │ 192.168.1.13│
└─────────────┘ └─────────────┘ └─────────────┘
二、詳細步驟
2.1 準(zhǔn)備工作
2.1.1 安裝Nginx
Rocky Linux 9:
# 添加Nginx官方倉庫 cat > /etc/yum.repos.d/nginx.repo <'EOF' [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true EOF # 安裝Nginx dnf install nginx -y # 啟動并設(shè)置開機自啟 systemctl start nginx systemctl?enable?nginx # 檢查版本 nginx -v
Ubuntu 24.04:
# 安裝依賴 apt update apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y # 添加Nginx官方GPG密鑰 curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null # 添加倉庫 echo"deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list # 安裝 apt update apt install nginx -y # 啟動 systemctl start nginx systemctlenablenginx
2.1.2 準(zhǔn)備后端服務(wù)
這里用一個簡單的Python HTTP服務(wù)來模擬后端,方便看到請求分發(fā)到了哪臺機器:
#!/usr/bin/env python3 # backend_server.py importhttp.server importsocketserver importsocket PORT =8080 HOSTNAME = socket.gethostname() IP = socket.gethostbyname(HOSTNAME) classMyHandler(http.server.SimpleHTTPRequestHandler): defdo_GET(self): self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() response =f"""Hello from Backend Server
Hostname:{HOSTNAME}
IP:{IP}
Port:{PORT}
Request Path:{self.path}
""" self.wfile.write(response.encode()) withsocketserver.TCPServer(("", PORT), MyHandler)ashttpd: print(f"Serving on port{PORT}") httpd.serve_forever()
在三臺后端服務(wù)器上分別啟動:
python3 backend_server.py
或者如果你有Tomcat環(huán)境,直接用Tomcat也行:
# 修改不同服務(wù)器的端口,便于區(qū)分 # server.xml中修改HTTP Connector端口 # 分別啟動三臺Tomcat
2.2 核心配置
2.2.1 最簡單的反向代理
先從最簡單的配置開始,讓Nginx把請求轉(zhuǎn)發(fā)到后端服務(wù):
# /etc/nginx/conf.d/proxy.conf
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.11:8080;
}
}
測試一下:
curl http://192.168.1.10
你會看到返回的是后端服務(wù)器的響應(yīng)。這就是最基礎(chǔ)的反向代理,就這么簡單。
2.2.2 完善的反向代理配置
實際生產(chǎn)中,光一個proxy_pass肯定不夠。下面是生產(chǎn)環(huán)境必須配置的參數(shù):
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.11:8080;
# 代理頭設(shè)置(重要!)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超時設(shè)置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 緩沖設(shè)置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
}
}
幾個關(guān)鍵配置解釋:
proxy_set_header Host $host:把客戶端請求的Host頭傳給后端。不設(shè)置這個,后端收到的Host是upstream的地址,很多應(yīng)用會出問題。
X-Real-IP和X-Forwarded-For:讓后端知道真實的客戶端IP。不設(shè)置的話,后端看到的都是Nginx的IP。
proxy_buffering:開啟緩沖后,Nginx會先把后端響應(yīng)存到緩沖區(qū),再發(fā)給客戶端。這樣即使客戶端網(wǎng)速慢,也不會一直占用后端連接。
踩坑記錄:有次我們的后端應(yīng)用一直獲取不到客戶端真實IP,日志里全是Nginx的內(nèi)網(wǎng)IP。排查半天,發(fā)現(xiàn)是忘了配X-Forwarded-For頭。后端應(yīng)用也需要配置信任代理地址才能正確解析這個頭。
2.2.3 基礎(chǔ)負載均衡
現(xiàn)在來配置負載均衡,把請求分發(fā)到多臺后端服務(wù)器:
# /etc/nginx/conf.d/loadbalance.conf
# 定義后端服務(wù)器組
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_pool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
測試負載均衡效果:
# 連續(xù)發(fā)送多個請求,觀察響應(yīng)中的服務(wù)器IP變化
foriin{1..10};do
curl -s http://192.168.1.10 | grep"IP:"
done
你會看到請求被輪流分發(fā)到三臺服務(wù)器上,這就是默認(rèn)的輪詢(Round Robin)算法。
2.2.4 負載均衡算法詳解
Nginx支持多種負載均衡算法,選擇合適的算法對系統(tǒng)性能影響很大。
1. 輪詢(Round Robin)- 默認(rèn)
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
最簡單粗暴的算法,按順序把請求分給每臺服務(wù)器。適合后端服務(wù)器配置相同、無狀態(tài)服務(wù)的場景。
2. 加權(quán)輪詢(Weighted Round Robin)
upstream backend_pool {
server 192.168.1.11:8080 weight=5; # 處理50%的請求
server 192.168.1.12:8080 weight=3; # 處理30%的請求
server 192.168.1.13:8080 weight=2; # 處理20%的請求
}
服務(wù)器配置不同時使用。比如有臺8核的機器和兩臺4核的,給8核的機器分配更多權(quán)重。
3. 最少連接數(shù)(Least Connections)
upstream backend_pool {
least_conn;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
把請求分給當(dāng)前連接數(shù)最少的服務(wù)器。適合請求處理時間不均勻的場景,比如有些請求1秒就處理完,有些要10秒。
實戰(zhàn)經(jīng)驗:我們的API網(wǎng)關(guān)一開始用輪詢,后來發(fā)現(xiàn)有些慢接口會導(dǎo)致某臺服務(wù)器連接堆積。換成least_conn后,負載變得均勻多了。
4. IP哈希(IP Hash)
upstream backend_pool {
ip_hash;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
根據(jù)客戶端IP的哈希值分配服務(wù)器,同一個IP的請求總是落到同一臺服務(wù)器。適合需要會話保持的場景。
注意:ip_hash有個坑,如果你前面還有一層代理(比如CDN),那所有請求的客戶端IP都是CDN的IP,ip_hash就失效了。這時候需要用$http_x_forwarded_for來取真實IP。
5. 一致性哈希(Hash)
upstream backend_pool {
hash $request_uri consistent;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
根據(jù)指定的key進行哈希。consistent參數(shù)啟用一致性哈希,當(dāng)服務(wù)器增減時,只有少部分請求會重新映射。
常用的hash key:
$request_uri:相同URL總是落到同一臺服務(wù)器,適合緩存場景
$arg_userid:按用戶ID分流
$cookie_sessionid:按session分流
6. 隨機(Random)
upstream backend_pool {
random two least_conn;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
隨機選擇兩臺服務(wù)器,然后從中選連接數(shù)少的那臺。這個算法在Nginx 1.15.1引入,據(jù)官方說在大規(guī)模集群中效果比least_conn更好。
2.2.5 健康檢查配置
負載均衡必須配合健康檢查,不然一臺服務(wù)器掛了,流量還往那兒發(fā),用戶體驗就很差了。
被動健康檢查(開源版Nginx):
upstream backend_pool {
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.13:8080 max_fails=3 fail_timeout=30s;
}
參數(shù)說明:
max_fails=3:連續(xù)3次請求失敗就認(rèn)為服務(wù)器掛了
fail_timeout=30s:服務(wù)器被標(biāo)記為不可用的時間,30秒后會再次嘗試
問題:被動檢查有延遲,要等真實流量失敗才能發(fā)現(xiàn)問題。而且恢復(fù)后也是立即全量切入,可能導(dǎo)致瞬間壓力過大。
主動健康檢查(Nginx Plus或第三方模塊):
如果你用的是開源版Nginx,可以安裝nginx_upstream_check_module模塊:
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
# 主動健康檢查配置
check interval=3000 rise=2 fall=3 timeout=2000 type=http;
check_http_send "GET /health HTTP/1.0
";
check_http_expect_alive http_2xx http_3xx;
}
server {
# 健康檢查狀態(tài)頁面
location /upstream_status {
check_status;
access_log off;
allow 192.168.1.0/24;
deny all;
}
}
參數(shù)說明:
interval=3000:每3秒檢查一次
rise=2:連續(xù)2次成功認(rèn)為恢復(fù)
fall=3:連續(xù)3次失敗認(rèn)為宕機
type=http:使用HTTP檢查
2.2.6 后備服務(wù)器配置
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080 backup; # 備用服務(wù)器
server 192.168.1.14:8080 down; # 標(biāo)記為下線,不接收流量
}
backup:備用服務(wù)器,只有當(dāng)所有主服務(wù)器都不可用時才啟用
down:標(biāo)記服務(wù)器為下線狀態(tài),通常用于維護
2.2.7 連接復(fù)用配置
每次代理請求都新建TCP連接太浪費了,配置keepalive可以復(fù)用連接:
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
# 每個worker保持的空閑連接數(shù)
keepalive 100;
# 單個連接最大請求數(shù)
keepalive_requests 1000;
# 連接空閑超時
keepalive_timeout 60s;
}
server {
location / {
proxy_pass http://backend_pool;
# 必須!使用HTTP/1.1和清空Connection頭
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
這兩行配置很容易漏掉:
proxy_http_version 1.1; proxy_set_header Connection "";
不加這兩行,keepalive就不生效。因為HTTP/1.0默認(rèn)是短連接,而且Nginx默認(rèn)會把Connection頭設(shè)成close。
2.3 啟動和驗證
2.3.1 配置驗證
# 檢查配置語法 nginx -t # 輸出示例 # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful
2.3.2 重載配置
# 平滑重載,不中斷服務(wù) nginx -s reload # 或者使用systemd systemctl reload nginx
2.3.3 驗證負載均衡
# 方法1:多次curl觀察響應(yīng)
foriin{1..10};do
curl -s http://192.168.1.10 | grep"IP:"
sleep 0.5
done
# 方法2:使用ab壓測
ab -n 1000 -c 100 http://192.168.1.10/
# 方法3:查看后端服務(wù)器訪問日志
tail -f /var/log/nginx/access.log
三、示例代碼和配置
3.1 完整配置示例
這是一個生產(chǎn)級的反向代理+負載均衡配置:
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
# Gzip壓縮
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript;
# 上游服務(wù)器組
upstream api_servers {
least_conn;
server 192.168.1.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.13:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.14:8080 backup;
keepalive 300;
keepalive_requests 10000;
keepalive_timeout 60s;
}
# 靜態(tài)資源服務(wù)器組
upstream static_servers {
server 192.168.1.21:80;
server 192.168.1.22:80;
keepalive 100;
}
# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 主站配置
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_session_cache shared10m;
ssl_session_timeout 1d;
ssl_protocols TLSv1.2 TLSv1.3;
# 安全頭
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# API接口
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
limit_conn conn_limit 20;
proxy_pass http://api_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 30s;
}
# 靜態(tài)資源
location /static/ {
proxy_pass http://static_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_valid 200 1d;
expires 30d;
add_header Cache-Control "public, immutable";
}
# WebSocket代理
location /ws/ {
proxy_pass http://api_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
}
# 健康檢查接口
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
}
# 內(nèi)部狀態(tài)監(jiān)控
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
access_log off;
}
}
}
3.2 實際應(yīng)用案例
案例1:電商網(wǎng)站高可用架構(gòu)
背景:日均PV 1000萬,需要支持大促期間10倍流量峰值
架構(gòu)設(shè)計:
┌─────────────────┐
│ CDN層 │
│ 靜態(tài)資源加速 │
└────────┬────────┘
│
┌────────▼────────┐
│ SLB/CLB層 │
│ 云負載均衡器 │
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Nginx 01 │ │ Nginx 02 │ │ Nginx 03 │
│ 反向代理集群 │ │ 反向代理集群 │ │ 反向代理集群 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ API集群 │ │ Web集群 │ │ 靜態(tài)資源 │
│ 商品/訂單│ │ 前端渲染 │ │ 圖片/CSS │
└─────────┘ └─────────┘ └─────────┘
Nginx配置:
# 商品服務(wù)
upstream product_service {
least_conn;
server 10.0.1.1:8080 weight=3;
server 10.0.1.2:8080 weight=3;
server 10.0.1.3:8080 weight=2;
keepalive 200;
}
# 訂單服務(wù)
upstream order_service {
least_conn;
server 10.0.2.1:8080 weight=3;
server 10.0.2.2:8080 weight=3;
server 10.0.2.3:8080 weight=2;
keepalive 200;
}
# 用戶服務(wù)
upstream user_service {
ip_hash; # 需要會話保持
server 10.0.3.1:8080;
server 10.0.3.2:8080;
server 10.0.3.3:8080;
keepalive 100;
}
server {
listen 80;
server_name api.shop.com;
# 商品接口
location /api/products {
proxy_pass http://product_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
# 訂單接口
location /api/orders {
proxy_pass http://order_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
# 用戶接口
location /api/users {
proxy_pass http://user_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
}
案例2:灰度發(fā)布配置
背景:新版本上線,先讓10%的用戶訪問新版本,驗證沒問題后逐步擴大比例
# 新舊版本服務(wù)器
upstream app_v1 {
server 10.0.1.1:8080;
server 10.0.1.2:8080;
keepalive 100;
}
upstream app_v2 {
server 10.0.2.1:8080;
server 10.0.2.2:8080;
keepalive 100;
}
# 方法1:基于權(quán)重的灰度
upstream app_canary {
server 10.0.1.1:8080 weight=9; # v1
server 10.0.1.2:8080 weight=9; # v1
server 10.0.2.1:8080 weight=1; # v2 (10%)
server 10.0.2.2:8080 weight=1; # v2 (10%)
keepalive 100;
}
# 方法2:基于Cookie的灰度
map $cookie_version $upstream_group {
default app_v1;
"v2" app_v2;
}
# 方法3:基于請求頭的灰度
map $http_x_version $upstream_group_header {
default app_v1;
"v2" app_v2;
}
# 方法4:基于用戶ID的灰度(取模)
split_clients $arg_userid $app_version {
10% app_v2;
* app_v1;
}
server {
listen 80;
server_name app.example.com;
# 使用Cookie灰度
location / {
proxy_pass http://$upstream_group;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 設(shè)置灰度Cookie的接口
location /set_version {
add_header Set-Cookie "version=$arg_v; Path=/; Max-Age=86400";
return 200 "Version set to $arg_v";
}
}
灰度發(fā)布流程:
部署新版本服務(wù)到v2集群
配置10%流量到v2
監(jiān)控v2的錯誤率、響應(yīng)時間
逐步調(diào)整權(quán)重:10% -> 30% -> 50% -> 100%
下線v1集群
案例3:WebSocket負載均衡
背景:實時聊天應(yīng)用,需要長連接支持
upstream ws_servers {
# WebSocket需要會話保持
ip_hash;
server 10.0.1.1:8080;
server 10.0.1.2:8080;
server 10.0.1.3:8080;
# WebSocket連接數(shù)較少,keepalive可以小一些
keepalive 50;
}
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://ws_servers;
proxy_http_version 1.1;
# WebSocket必須的頭
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket長連接超時設(shè)置
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# 禁用緩沖
proxy_buffering off;
}
}
踩坑記錄:WebSocket一開始配了least_conn,結(jié)果用戶聊著聊著就斷線了。排查發(fā)現(xiàn)是連接被分到了不同的服務(wù)器。WebSocket必須用ip_hash或者sticky session保證會話保持。
四、最佳實踐和注意事項
4.1 最佳實踐
1. 分層部署架構(gòu)
不要把所有服務(wù)都放在一個upstream里,按業(yè)務(wù)功能分組:
# 好的做法
upstream user_api { ... }
upstream order_api { ... }
upstream product_api { ... }
# 不好的做法
upstream all_api {
server user1:8080;
server user2:8080;
server order1:8080; # 混在一起
server product1:8080; # 難以管理
}
2. 合理的超時設(shè)置
不同類型的請求設(shè)置不同的超時:
# 短請求(列表查詢)
location /api/list {
proxy_read_timeout 10s;
}
# 長請求(報表導(dǎo)出)
location /api/export {
proxy_read_timeout 300s;
}
# 文件上傳
location /api/upload {
client_max_body_size 500m;
proxy_read_timeout 600s;
}
3. 錯誤頁面友好展示
# 自定義錯誤頁面
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
internal;
}
# 或者返回JSON
error_page 502 503 504 = @fallback;
location @fallback {
default_type application/json;
return 503 '{"code": 503, "message": "Service temporarily unavailable"}';
}
4. 監(jiān)控指標(biāo)收集
日志中記錄upstream響應(yīng)時間,便于分析:
log_format upstream_log '$remote_addr [$time_local] ' '$request $status ' 'upstream: $upstream_addr ' 'response_time: $upstream_response_time ' 'connect_time: $upstream_connect_time';
5. 平滑擴縮容
新增服務(wù)器時,先設(shè)置低權(quán)重,逐步提升:
upstream backend {
server 10.0.1.1:8080 weight=10;
server 10.0.1.2:8080 weight=10;
server 10.0.1.3:8080 weight=1; # 新機器,先低權(quán)重
}
4.2 注意事項
| 常見錯誤 | 原因分析 | 解決方案 |
|---|---|---|
| 502 Bad Gateway | 后端服務(wù)未啟動或連接被拒絕 | 檢查后端服務(wù)狀態(tài),確認(rèn)端口正確 |
| 504 Gateway Timeout | 后端響應(yīng)超時 | 增加proxy_read_timeout,或優(yōu)化后端性能 |
| upstream無法連接 | 網(wǎng)絡(luò)不通或防火墻阻止 | 檢查網(wǎng)絡(luò)連通性,開放防火墻端口 |
| keepalive不生效 | 缺少必要配置 | 添加proxy_http_version 1.1和Connection "" |
| 負載不均衡 | 算法選擇不當(dāng)或權(quán)重配置問題 | 根據(jù)場景選擇合適算法,檢查權(quán)重配置 |
| 會話丟失 | 無狀態(tài)輪詢導(dǎo)致請求分發(fā)到不同服務(wù)器 | 使用ip_hash或sticky session |
| 健康檢查延遲 | 被動檢查需要真實流量觸發(fā) | 配置主動健康檢查模塊 |
| X-Forwarded-For鏈過長 | 多層代理重復(fù)追加 | 在入口處設(shè)置X-Forwarded-For為$remote_addr |
五、故障排查和監(jiān)控
5.1 故障排查
5.1.1 常見故障診斷流程
502錯誤排查:
# 1. 檢查后端服務(wù)是否運行 curl -I http://backend_ip:port/health # 2. 檢查Nginx到后端的網(wǎng)絡(luò)連通性 telnet backend_ip port # 3. 查看Nginx錯誤日志 tail -f /var/log/nginx/error.log # 4. 檢查后端服務(wù)日志 # 看是否收到請求,是否有報錯 # 5. 檢查連接數(shù)是否達到限制 ss -s cat /proc/sys/net/core/somaxconn
504超時排查:
# 1. 測試后端接口響應(yīng)時間 time curl http://backend_ip:port/api/slow # 2. 檢查Nginx超時配置 nginx -T | grep -E"proxy_.*_timeout" # 3. 分析慢請求日志 awk'$NF > 5'/var/log/nginx/access.log # 響應(yīng)時間>5秒的請求
負載不均排查:
# 1. 統(tǒng)計各后端服務(wù)器請求量
awk'{print $NF}'/var/log/nginx/access.log | sort | uniq -c | sort -rn
# 2. 檢查upstream配置
nginx -T | grep -A 20"upstream"
# 3. 檢查是否有服務(wù)器被標(biāo)記為不可用
# 查看error.log中的upstream相關(guān)錯誤
5.1.2 調(diào)試配置
臨時開啟詳細日志排查問題:
# 臨時配置,排查完記得關(guān)掉
error_log /var/log/nginx/error.log debug;
# 或者只對特定請求開啟debug
location /api/problem {
error_log /var/log/nginx/debug.log debug;
proxy_pass http://backend;
}
5.2 性能監(jiān)控
5.2.1 內(nèi)置狀態(tài)監(jiān)控
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
獲取狀態(tài):
curl http://127.0.0.1:8080/nginx_status # 輸出示例: # Active connections: 234 # server accepts handled requests # 12847932 12847932 98234756 # Reading: 5 Writing: 67 Waiting: 162
指標(biāo)說明:
Active connections:當(dāng)前活躍連接數(shù)
accepts:已接受的連接總數(shù)
handled:已處理的連接總數(shù)(應(yīng)該等于accepts,不等說明有連接被丟棄)
requests:已處理的請求總數(shù)
Reading:正在讀取請求頭的連接數(shù)
Writing:正在發(fā)送響應(yīng)的連接數(shù)
Waiting:Keep-alive空閑連接數(shù)
5.2.2 Prometheus監(jiān)控集成
使用nginx-prometheus-exporter:
# 安裝exporter docker run -d -p 9113:9113 nginx/nginx-prometheus-exporter:latest -nginx.scrape-uri=http://nginx:8080/nginx_status
Prometheus配置:
scrape_configs: -job_name:'nginx' static_configs: -targets:['localhost:9113']
關(guān)鍵告警規(guī)則:
groups:
-name:nginx_alerts
rules:
-alert:NginxHighConnectionUsage
expr:nginx_connections_active/nginx_connections_accepted>0.8
for:5m
labels:
severity:warning
annotations:
summary:"Nginx連接使用率過高"
-alert:NginxHighErrorRate
expr:rate(nginx_http_requests_total{status=~"5.."}[5m])/rate(nginx_http_requests_total[5m])>0.01
for:5m
labels:
severity:critical
annotations:
summary:"Nginx 5xx錯誤率超過1%"
5.3 備份與恢復(fù)
5.3.1 配置備份
#!/bin/bash
# /usr/local/bin/backup_nginx.sh
BACKUP_DIR="/data/backup/nginx"
DATE=$(date +%Y%m%d_%H%M%S)
NGINX_DIR="/etc/nginx"
# 創(chuàng)建備份目錄
mkdir -p${BACKUP_DIR}
# 備份配置文件
tar -czf${BACKUP_DIR}/nginx_config_${DATE}.tar.gz${NGINX_DIR}
# 備份SSL證書
tar -czf${BACKUP_DIR}/nginx_ssl_${DATE}.tar.gz${NGINX_DIR}/ssl/
# 保留30天備份
find${BACKUP_DIR}-name"*.tar.gz"-mtime +30 -delete
echo"Backup completed:${BACKUP_DIR}/nginx_config_${DATE}.tar.gz"
5.3.2 配置恢復(fù)
#!/bin/bash # 恢復(fù)配置 BACKUP_FILE=$1 if[ -z"$BACKUP_FILE"];then echo"Usage:$0" exit1 fi # 備份當(dāng)前配置 mv /etc/nginx /etc/nginx.bak.$(date +%s) # 解壓恢復(fù) tar -xzf$BACKUP_FILE-C / # 測試配置 nginx -t if[ $? -eq 0 ];then nginx -s reload echo"Configuration restored successfully" else echo"Configuration test failed, rolling back" rm -rf /etc/nginx mv /etc/nginx.bak.* /etc/nginx fi
六、總結(jié)
6.1 技術(shù)要點回顧
反向代理和負載均衡是Nginx最核心的功能,掌握好這兩個技能,基本就能應(yīng)對大部分Web架構(gòu)需求了。
反向代理要點:
proxy_pass指令是核心
代理頭設(shè)置影響后端獲取真實信息
緩沖區(qū)配置影響內(nèi)存使用和響應(yīng)速度
超時配置要根據(jù)業(yè)務(wù)調(diào)整
負載均衡要點:
算法選擇:無狀態(tài)服務(wù)用least_conn,需要會話保持用ip_hash
健康檢查:生產(chǎn)環(huán)境必須配置max_fails和fail_timeout
連接復(fù)用:keepalive可以顯著提升性能
故障轉(zhuǎn)移:proxy_next_upstream配置自動重試
6.2 進階學(xué)習(xí)方向
Nginx Plus:商業(yè)版功能,主動健康檢查、動態(tài)配置、更好的監(jiān)控
OpenResty:整合Lua,實現(xiàn)復(fù)雜的業(yè)務(wù)邏輯
Envoy:云原生時代的新選擇,功能更強大
服務(wù)網(wǎng)格:Istio、Linkerd等,下一代流量管理方案
6.3 參考資料
Nginx官方文檔 - ngx_http_upstream_module: https://nginx.org/en/docs/http/ngx_http_upstream_module.html
Nginx官方文檔 - ngx_http_proxy_module: https://nginx.org/en/docs/http/ngx_http_proxy_module.html
Nginx負載均衡指南: https://docs.nginx.com/nginx/admin-guide/load-balancer/
附錄
A. 命令速查表
| 命令 | 說明 |
|---|---|
| nginx -t | 測試配置語法 |
| nginx -s reload | 平滑重載配置 |
| nginx -T | 輸出完整配置 |
| curl -I http://url | 查看響應(yīng)頭 |
| ss -tlnp | 查看監(jiān)聽端口 |
| ss -ant | grep ESTAB | 查看已建立的連接 |
B. 配置參數(shù)詳解
| 參數(shù) | 上下文 | 默認(rèn)值 | 說明 |
|---|---|---|---|
| proxy_pass | location | - | 代理目標(biāo)地址 |
| proxy_connect_timeout | http,server,location | 60s | 連接超時 |
| proxy_send_timeout | http,server,location | 60s | 發(fā)送超時 |
| proxy_read_timeout | http,server,location | 60s | 讀取超時 |
| proxy_next_upstream | http,server,location | error timeout | 觸發(fā)重試的條件 |
| keepalive | upstream | - | 保持的空閑連接數(shù) |
| weight | server | 1 | 服務(wù)器權(quán)重 |
| max_fails | server | 1 | 最大失敗次數(shù) |
| fail_timeout | server | 10s | 失敗超時時間 |
C. 術(shù)語表
| 術(shù)語 | 解釋 |
|---|---|
| 反向代理 | 代理服務(wù)端,客戶端不知道真實服務(wù)器地址 |
| 正向代理 | 代理客戶端,服務(wù)端不知道真實客戶端地址 |
| 負載均衡 | 將請求分發(fā)到多臺服務(wù)器 |
| Upstream | Nginx中定義后端服務(wù)器組的指令 |
| 健康檢查 | 檢測后端服務(wù)器是否可用 |
| 會話保持 | 同一用戶的請求總是發(fā)到同一臺服務(wù)器 |
| 灰度發(fā)布 | 逐步將流量切換到新版本 |
| 熔斷 | 當(dāng)后端不可用時自動停止轉(zhuǎn)發(fā)請求 |
-
服務(wù)器
+關(guān)注
關(guān)注
14文章
10251瀏覽量
91480 -
負載均衡
+關(guān)注
關(guān)注
0文章
133瀏覽量
12874 -
nginx
+關(guān)注
關(guān)注
0文章
186瀏覽量
13110
原文標(biāo)題:Nginx反向代理+負載均衡實戰(zhàn):小白也能搭建高可用架構(gòu)
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
16nginx+keepalived +zuul如何實現(xiàn)高可用及負載均衡
構(gòu)建實戰(zhàn):Nginx+IIS構(gòu)筑Web服務(wù)器集群負載均衡
聊聊Nginx作為負載均衡器它支持的算法都有哪些?
如何使用Nginx作為應(yīng)用程序的負載均衡器?
如何使用nginx反向代理功能?保姆級教程!
Nginx反向代理和負載均衡配置實戰(zhàn)
評論