前言
Varnish Cache 是高效能 HTTP 反向代理快取,廣泛用於動態網站加速。本文提供從原始碼編譯安裝、VCL 配置詳解、後端負載均衡、健康檢查、日誌切割、Linux 核心參數調優到管理操作的完整實戰指南。
編譯安裝 (RedHat/CentOS 7 範例)
# 1. 安裝依賴
yum install -y gcc make autoconf automake libtool pkgconfig \
python3-docutils pcre-devel libedit-devel
# 2. 下載原始碼 (以 6.6 LTS 為例)
wget https://varnish-cache.org/_downloads/varnish-6.6.2.tgz
tar xzf varnish-6.6.2.tgz
cd varnish-6.6.2
# 3. 配置編譯
./configure --prefix=/usr/local/varnish-6.6.2
make -j$(nproc)
make install
# 4. 建立軟連結方便管理
ln -s /usr/local/varnish-6.6.2 /usr/local/varnish
ln -s /usr/local/varnish/sbin/varnishd /usr/sbin/varnishd
ln -s /usr/local/varnish/bin/* /usr/bin/
VCL 配置檔詳解 (/usr/local/varnish/etc/varnish/default.vcl)
後端伺服器定義與健康檢查
vcl 4.1;
# 後端定義
backend web01 {
.host = "192.168.1.101";
.port = "8080";
.probe = {
.url = "/healthz";
.timeout = 2s;
.interval = 5s;
.window = 5;
.threshold = 3;
.expected_response = 200;
}
.connect_timeout = 1s;
.first_byte_timeout = 5s;
.between_bytes_timeout = 2s;
}
backend web02 {
.host = "192.168.1.102";
.port = "8080";
.probe = {
.url = "/healthz";
.timeout = 2s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
# 負載均衡器:隨機權重
director app_cluster random {
.retries = 3;
{
.backend = web01;
.weight = 3; # 權重 3
}
{
.backend = web02;
.weight = 1; # 權重 1
}
}
# 另一種:輪詢
# director app_cluster round_robin { ... }
ACL 存取控制
acl purge_allowed {
"localhost";
"127.0.0.1";
"10.0.0.0/8"; # 內網管理網段
"192.168.0.0/16";
}
核心子程式:vcl_recv (請求進入點)
sub vcl_recv {
# 1. 正規化 Host header
set req.http.Host = regsub(req.http.Host, ":.*$", "");
# 2. 指定後端
if (req.http.host ~ "^(www\.)?example\.com$") {
set req.backend_hint = app_cluster;
} else {
return (synth(404, "Unknown domain"));
}
# 3. PURGE 請求驗證
if (req.method == "PURGE") {
if (!client.ip ~ purge_allowed) {
return (synth(405, "Not allowed"));
}
return (purge);
}
# 4. 移除靜態資源 Cookie (提高快取命中)
if (req.url ~ "\.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|svg|webp)(\?.*)?$") {
unset req.http.Cookie;
}
# 5. 保留原始客戶端 IP (X-Forwarded-For)
if (req.http.X-Forwarded-For) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# 6. 非 GET/HEAD 請求不快取 (POST/PUT/DELETE 等)
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# 7. 動態頁面不快取 (含 Cookie、Authorization)
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
# 8. 特定路徑不快取 (如管理後台、API)
if (req.url ~ "^/(admin|api|user)/") {
return (pass);
}
# 9. 預設查詢快取
return (hash);
}
vcl_backend_response (後端回應處理)
sub vcl_backend_response {
# 1. 後端明確禁止快取
if (beresp.http.Cache-Control ~ "(no-cache|no-store|private)" ||
beresp.http.Pragma ~ "no-cache") {
set beresp.uncacheable = true;
set beresp.ttl = 120s; # 短暫快取供優雅降級
return (deliver);
}
# 2. Set-Cookie 存在則不快取 (避免快取含 Session 的頁面)
if (beresp.http.Set-Cookie) {
set beresp.uncacheable = true;
return (deliver);
}
# 3. 靜態資源長期快取 (1 年)
if (bereq.url ~ "\.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|svg|webp|mp4|pdf)(\?.*)?$") {
set beresp.ttl = 365d;
set beresp.http.Cache-Control = "public, max-age=31536000";
# 移除可能干擾的 header
unset beresp.http.ETag;
unset beresp.http.Last-Modified;
}
# 4. HTML 頁面短期快取 (5 分鐘) - 視業務調整
if (bereq.url ~ "\.html?(\?.*)?$") {
set beresp.ttl = 300s;
set beresp.http.Cache-Control = "public, max-age=300";
}
# 5. API 回應極短快取或不快取
if (bereq.url ~ "^/api/") {
set beresp.ttl = 10s;
set beresp.http.Cache-Control = "public, max-age=10, stale-while-revalidate=60";
}
return (deliver);
}
vcl_deliver (回應客戶端前) - 加入快取命中標記
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
# 移除內部 header
unset resp.http.Via;
unset resp.http.X-Varnish;
}
其他子程式
# 管道模式 (非快取直通)
sub vcl_pipe {
return (pipe);
}
# 快取未命中 -> 取得後端
sub vcl_miss {
return (fetch);
}
# 快取命中
sub vcl_hit {
if (obj.ttl >= 0s) {
return (deliver);
}
return (miss); # 已過期
}
# 雜湊鍵值 (預設: URL + Host)
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
# 可加入其他維度,如 Cookie、User-Agent
# if (req.http.Cookie) { hash_data(req.http.Cookie); }
}
啟動參數與系統服務
建立專用使用者
groupadd -r varnish
useradd -r -g varnish -s /sbin/nologin -d /var/lib/varnish varnish
快取儲存配置
# 檔案型快取 (適合大容量、持久化)
# -s file,/var/lib/varnish/varnish_storage.bin,10G
# 記憶體型快取 (極速、重啟遺失)
# -s malloc,4G
systemd 服務檔 (/etc/systemd/system/varnish.service)
[Unit]
Description=Varnish HTTP Accelerator
After=network.target
[Service]
Type=forking
LimitNOFILE=100000
LimitMEMLOCK=infinity
ExecStartPre=/usr/local/varnish/sbin/varnishd -C -f /usr/local/varnish/etc/varnish/default.vcl
ExecStart=/usr/local/varnish/sbin/varnishd \
-a :80 \
-a :8443,PROXY \
-f /usr/local/varnish/etc/varnish/default.vcl \
-s malloc,4G \
-w 500,10000,30 \
-t 120 \
-T 127.0.0.1:6082 \
-u varnish -g varnish \
-p feature=+http2 \
-p thread_pool_min=100 \
-p thread_pool_max=5000 \
-p thread_pool_timeout=300 \
-p workspace_client=64k \
-p workspace_backend=64k
ExecReload=/usr/local/varnish/sbin/varnishd -C -f /usr/local/varnish/etc/varnish/default.vcl
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
關鍵參數說明
| 參數 | 說明 | 建議值 |
| -a | 監聽位址:埠,支援 PROXY 協定 | :80, :8443,PROXY |
| -s | 儲存後端:malloc(記憶體) 或 file(磁碟) | malloc,4G (記憶體足夠時) |
| -w | 執行緒池:min,max,timeout | 500,10000,30 |
| -t | 預設 TTL (秒) | 120 |
| -T | 管理介面 IP:埠 | 127.0.0.1:6082 |
| -p feature=+http2 | 啟用 HTTP/2 支援 | 必要 |
日誌管理與切割
varnishncsa 存取日誌
# 啟動 (背景執行)
varnishncsa -a -w /var/log/varnish/access.log -D \
-F '%{X-Forwarded-For}i %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %D %{Varnish:hitmiss}x'
# 格式說明:
# %D = 微秒耗時
# %{Varnish:hitmiss}x = HIT/MISS 標記
每日切割腳本 (/usr/local/sbin/varnish-logrotate.sh)
#!/bin/bash
LOG_DIR="/var/log/varnish"
DATE=$(date -d "yesterday" +%Y-%m-%d)
# 發送 USR1 信號讓 varnishncsa 重新開啟日誌檔
pkill -USR1 varnishncsa
# 壓縮舊日誌
mv "$LOG_DIR/access.log" "$LOG_DIR/access-$DATE.log"
gzip "$LOG_DIR/access-$DATE.log"
# 刪除 30 天前日誌
find "$LOG_DIR" -name "access-*.log.gz" -mtime +30 -delete
logrotate 配置 (/etc/logrotate.d/varnish)
/var/log/varnish/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 640 varnish varnish
sharedscripts
postrotate
/bin/kill -USR1 `cat /var/run/varnishncsa.pid 2>/dev/null` 2>/dev/null || true
endscript
}
Linux 核心網路參數調優 (/etc/sysctl.d/99-varnish.conf)
# TCP 連線優化
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0 # NAT 環境下勿開啟
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.ip_local_port_range = 10240 65535
# 記憶體緩衝
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 檔案描述符
fs.file-max = 1000000
fs.nr_open = 1000000
# 套用
sysctl --system
管理操作與監控
varnishadm 常用指令
# 連線管理介面
varnishadm -T 127.0.0.1:6082
# 查看後端健康狀態
backend.list
# 手動設定後端健康/病狀
backend.set_health web01 sick
backend.set_health web01 healthy
# 清除快取 (purge)
ban req.http.host == "example.com" && req.url ~ "^/images/"
# 即時統計
varnishstat
# 即時日誌 (篩選)
varnishlog -g request -q "ReqUrl ~ '/api/'"
varnishlog -g request -q "RespStatus >= 500"
關鍵統計指標 (varnishstat)
| 指標 | 含義 | 健康閾值 |
| MAIN.client_req | 客戶端請求總數 | 持續增長 |
| MAIN.cache_hit | 快取命中次數 | Hit Rate > 80% |
| MAIN.cache_miss | 快取未命中 | 低於 20% |
| MAIN.backend_fail | 後端連線失敗 | 應為 0 |
| MAIN.threads_created | 建立執行緒數 | 平穩 |
| MAIN.sess_dropped | 連線被拒 (滿載) | 應為 0 |
| SMA.*.g_bytes | 記憶體快取使用量 | < 80% 配額 |
常見問題排查
| 現象 | 可能原因 | 排查步驟 |
| 命中率低 | Cookie 污染、VCL 不快取規則錯誤、TTL 太短 | 檢查 vcl_recv unset Cookie、vcl_backend_response TTL 設定、varnishlog 分析 MISS 原因 |
| 後端 503 | 後端掛掉、健康檢查失敗、連線逾時 | backend.list 看健康狀態、檢查後端 /healthz、調整 probe 參數 |
| 記憶體暴增 | malloc 過大、快取物件過大、記憶體洩漏 | 限制 -s malloc 大小、設定 obj.ttl、升級 Varnish 版本 |
| 大檔案下載中斷 | between_bytes_timeout 太短 | 調大 .between_bytes_timeout (如 300s) |
總結
Varnish 優化核心在於:精準的 VCL 快取策略 (什麼快取、多久、如何失效)、健康的後端叢集 (探針、負載均衡、優雅降級)、系統層面的資源配置 (記憶體、執行緒、核心參數)、可觀測性 (日誌、統計、告警)。本文提供的配置模板可作為生產環境基準,根據實際流量與業務特性微調參數。