核心差異:
架構模式:
# 1. 依賴
yum install -y gcc make autoconf automake libtool pkgconfig \
python3-docutils libedit-devel ncurses-devel \
pcre2-devel jemalloc-devel # jemalloc 提升記憶體分配效能
# 2. 下載穩定版(Varnish 7.2 LTS / 7.4 穩定版)
wget https://varnish-cache.org/_downloads/varnish-7.4.2.tgz
tar -xzf varnish-7.4.2.tgz
cd varnish-7.4.2
# 3. 配置編譯
./configure --prefix=/usr/local/varnish \
--enable-jemalloc \
--with-pcre2
# 4. 編譯安裝
make -j$(nproc)
make install
# 5. 用戶、目錄、權限
useradd -r -s /bin/false -d /var/lib/varnish varnish
mkdir -p /var/lib/varnish /var/log/varnish
chown -R varnish:varnish /var/lib/varnish /var/log/varnish
/etc/systemd/system/varnish.service[Unit]
Description=Varnish HTTP Accelerator
After=network.target
Documentation=man:varnishd(1)
[Service]
Type=simple
User=varnish
Group=varnish
LimitNOFILE=500000
LimitMEMLOCK=infinity
# 關鍵啟動參數
ExecStart=/usr/local/varnish/sbin/varnishd \
-a :80 \ # 監聽埠
-f /usr/local/varnish/etc/varnish/default.vcl \ # VCL 配置
-s malloc,4G \ # 儲存後端:記憶體 4GB(視機器記憶體 50-70%)
-p thread_pools=4 \ # 線程池數(CPU 核心數)
-p thread_pool_min=100 \ # 每池最小線程
-p thread_pool_max=5000 \ # 每池最大線程
-p thread_pool_timeout=300 \ # 線程閒置超時
-p workspace_client=64k \ # 客戶端工作空間
-p workspace_backend=64k \ # 後端工作空間
-p http_max_hdr=128 \ # 最大頭部數
-p http_req_hdr_len=8k \ # 請求頭最大長度
-p http_resp_hdr_len=8k \ # 回應頭最大長度
-T 127.0.0.1:6082 \ # 管理介面
-S /usr/local/varnish/etc/varnish/secret # 管理認證
ExecReload=/usr/local/varnish/bin/varnishreload -q
ExecStop=/usr/local/varnish/bin/varnishd -T 127.0.0.1:6082 -S /usr/local/varnish/etc/varnish/secret stop
PrivateTmp=true
ProtectSystem=full
[Install]
WantedBy=multi-user.target
# /usr/local/varnish/etc/varnish/default.vcl
vcl 4.1;
# ===== 後端定義 =====
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 2s;
.first_byte_timeout = 10s;
.between_bytes_timeout = 5s;
.max_connections = 1000;
.probe = {
.url = "/health";
.interval = 10s;
.timeout = 3s;
.window = 5;
.threshold = 3;
}
}
# 多後端負載均衡(導演)
backend web01 { .host = "10.0.1.10"; .port = "80"; .probe = { .url = "/health"; } }
backend web02 { .host = "10.0.1.11"; .port = "80"; .probe = { .url = "/health"; } }
director web_cluster random {
{ .backend = web01; .weight = 3; }
{ .backend = web02; .weight = 2; }
}
# ===== ACL:允許 PURGE 的 IP =====
acl purge {
"localhost";
"127.0.0.1";
"10.0.0.0/8";
"192.168.0.0/16";
}
# ===== 子程序:接收請求 =====
sub vcl_recv {
# 正規化 Host
set req.http.Host = regsub(req.http.Host, ":.*$", "");
# 僅緩存 GET/HEAD
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# PURGE 請求處理
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed"));
}
return (purge);
}
# 移除干擾緩存的頭部
unset req.http.Cookie; # 靜態資源無需 Cookie
unset req.http.Authorization; # 非認證介面移除
# 保留特定 Cookie(如會話標識)需條件判斷
# 正規化 Accept-Encoding
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|webp|woff2?)$") {
unset req.http.Accept-Encoding; # 已壓縮文件不再壓縮
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
# 大文件/串流不緩存
if (req.url ~ "\.(mp4|webm|mkv|iso|tar\.gz|zip|exe)$") {
return (pass);
}
# 指定後端
if (req.http.Host ~ "^(www|static)\.example\.com$") {
set req.backend_hint = web_cluster;
} else {
set req.backend_hint = default;
}
return (hash);
}
# ===== 子程序:緩存鍵計算 =====
sub vcl_hash {
hash_data(req.url);
if (req.http.Host) {
hash_data(req.http.Host);
} else {
hash_data(server.ip);
}
# 可選:按 Cookie 分桶(個性化內容)
# if (req.http.Cookie) { hash_data(req.http.Cookie); }
return (lookup);
}
# ===== 子程序:命中處理 =====
sub vcl_hit {
if (obj.ttl >= 0s) {
return (deliver);
}
if (req.http.If-None-Match || req.http.If-Modified-Since) {
return (deliver); # 客戶端條件請求,直接交付 304
}
return (fetch); # 過期但無條件請求,回源驗證
}
# ===== 子程序:未命中/回源 =====
sub vcl_miss {
return (fetch);
}
# ===== 子程序:後端回應處理 =====
sub vcl_backend_response {
# 設定緩存 TTL(優先級:beresp.ttl > Cache-Control > 預設 120s)
if (beresp.http.Cache-Control ~ "max-age") {
# Varnish 自動解析 max-age,無需手動設定
} else {
set beresp.ttl = 1h; # 兜底預設
}
# 靜態資源長期緩存
if (bereq.url ~ "\.(css|js|jpg|jpeg|png|gif|ico|woff2?|svg|webp)$") {
set beresp.ttl = 30d;
set beresp.grace = 1h; # 過期後仍可服務 1 小時(背景更新)
unset beresp.http.Set-Cookie; # 靜態資源不下發 Cookie
}
# HTML/動態頁面短緩存
if (bereq.url ~ "\.(html|htm|json|xml)$") {
set beresp.ttl = 5m;
set beresp.grace = 10m;
}
# 移除後端干擾頭部
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
# 支援 ESI(邊緣側包含)
if (bereq.url ~ "\.html$") {
set beresp.do_esi = true;
}
return (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";
}
# 移除內部頭部
unset resp.http.Via;
unset resp.http.X-Varnish;
unset resp.http.Age; # 可選保留
return (deliver);
}
# ===== 子程序:PURGE 處理 =====
sub vcl_purge {
return (synth(200, "Purged"));
}
# ===== 子程序:錯誤頁面合成 =====
sub vcl_synth {
if (resp.status == 405 || resp.status == 403) {
set resp.http.Content-Type = "text/plain; charset=utf-8";
synthetic("Access Denied");
return (deliver);
}
if (resp.status >= 500 && resp.status < 600) {
set resp.http.Content-Type = "text/html; charset=utf-8";
synthetic({"Service Unavailable
Backend error: " + resp.reason + "
"});
return (deliver);
}
}
# 啟動時指定
varnishncsa -a -w /var/log/varnish/access.log \
-D -P /var/run/varnishncsa.pid \
-F '%{X-Forwarded-For}i %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %{Varnish:handling}x %{Varnish:time_firstbyte}x'
# systemd 啟動腳本整合
ExecStartPost=/usr/local/varnish/bin/varnishncsa -a -w /var/log/varnish/access.log -D -P /var/run/varnishncsa.pid
varnishstat:關鍵指標 MAIN.cache_hit, MAIN.cache_miss, MAIN.sess_conn, MAIN.threads, SMA.*.g_bytes (記憶體用量)。varnishtop:即時 Top URL、Client IP、Backend 等。varnishlog:詳細請求日誌(調試用),-g request -q "ReqUrl ~ '/api/'"。# 部署 varnish_exporter
wget https://github.com/jonnenauha/varnish_exporter/releases/download/v1.6.0/varnish_exporter_linux_amd64.tar.gz
./varnish_exporter --varnish.secret-file=/usr/local/varnish/etc/varnish/secret --web.listen-address=:9131
# 告警規則
- alert: VarnishHitRateLow
expr: rate(varnish_cache_hit[5m]) / (rate(varnish_cache_hit[5m]) + rate(varnish_cache_miss[5m])) < 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Varnish 命中率低於 90%"
| 問題 | 原因 | 解決 |
|---|---|---|
| 記憶體用量超限、OOM Killer 殺進程 | -s malloc,4G 超過物理記憶體、未限制 thread_pool_max | 記憶體 50-70% 給 Varnish,預留給 OS Page Cache/其他進程;限制線程池上限 |
| 命中率低 | Cookie 干擾、Vary 過度、後端無 Cache-Control、PURGE 未生效 | vcl_recv 移除無關 Cookie、vcl_hash 精準鍵、後端輸出標準頭、檢查 PURGE ACL |
| 後端健康檢查失敗、節點被踢 | 探針 URL 回應慢/錯、閾值過嚴 | 專用 /health 輕量端點、調整 .window/.threshold |
| 大文件下載緩存導致記憶體暴增 | 未在 vcl_recv pass 大文件 | 副檔名/Content-Length 判斷 return (pass) |
| HTTPS 終結效能差 | Varnish SSL 代碼路徑未優化 | 前置 Nginx 終結 SSL,Varnish 走 HTTP |
# 1. 修改 VCL
vi /usr/local/varnish/etc/varnish/default.vcl
# 2. 語法檢查
/usr/local/varnish/sbin/varnishd -C -f /usr/local/varnish/etc/varnish/default.vcl
# 3. 熱載入(不重啟進程,零連接中斷)
/usr/local/varnish/bin/varnishreload -q
# 或手動
varnishadm -T 127.0.0.1:6082 -S /usr/local/varnish/etc/varnish/secret vcl.load new1 /usr/local/varnish/etc/varnish/default.vcl
varnishadm -T 127.0.0.1:6082 -S /usr/local/varnish/etc/varnish/secret vcl.use new1
Varnish 作為記憶體級 HTTP 加速器,在熱門小對象、極致低延遲、高吞吐場景無可替代。核心在於:合理規劃記憶體(-s malloc)、精準 VCL(vcl_recv/vcl_hash/vcl_backend_response)、健康檢查與優雅降級、PURGE API 整合發布流程、Prometheus 監控告警。配合 Nginx 前置終結 SSL/WAF,構建「Nginx (Edge) → Varnish (Memory Cache) → App」三層架構,可將 95%+ 請求在微秒級完成,後端負載降低數量級。