Varnish配置
Varnish 的配置系統與其他軟件不同,一般的配置是使用配置指令,用於打開或關閉某個配置項。而 Varnish 使用 VCL 語言進行配置。
查看 varnish 服務的主配置文件
[root@CentOS74 ~]# cat /etc/varnish/varnish.params | grep ^[^#]
RELOAD_VCL=1 #此值爲1時可以在不重啓varnish的情況下加載vcl文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl #vcl配置文件路徑
VARNISH_LISTEN_PORT=6081 #主進程監聽端口,默認監聽在0.0.0.0
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #命令行管理接口監聽IP
VARNISH_ADMIN_LISTEN_PORT=6082 #命令行管理接口監聽端口
VARNISH_SECRET_FILE=/etc/varnish/secret #連接管理接口所需的祕鑰文件路徑
VARNISH_STORAGE="malloc,256M" #使用的緩存方式及所能使用的空間大小
VARNISH_USER=varnish #以誰的身份運行
VARNISH_GROUP=varnish #以哪個組的身份運行
DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300" #自定義運行時的選項,使用-p設定,可使用多次
-
vcl語法
語法格式:
從varnish 4.0版本開始,每一個VCL文件都必須在文件開始處說明版本號VCL4.0
//, # 單行註釋 /* ... */ 多行註釋
sub 後跟子例程,例如sub vcl_recv { ...}
不支持循環,受限於引擎的內建變量
return() 函數使用關鍵字指定下一條引擎,用於實現狀態引擎轉換
引擎需要單獨定義
請求與請求之間是無關聯的
請求之間在被處理時是被隔離的
VCL中的子程序既不帶參數,也不返回值
VCL中的子程序只能通過HTTP頭交換數據
vcl 有許多默認配置,其內容無法修改。vcl 中的配置可以分爲 Client Side 與 Backend Side
Client Side 中使用的引擎有:
vcl_recv, vcl_pass, vcl_hit, vcl_miss, vcl_pipe, vcl_purge, vcl_synth, vcl_deliver
Backend Side 中使用的引擎有:
vcl_backend_fetch, vcl_backend_response, vcl_backend_error
查看 vcl 默認配置
vcl 4.0; #開頭聲明使用的是vcl 4.0
#######################################################################
# Client side #面向客戶端側vcl的配置段
sub vcl_recv {
if (req.method == "PRI") { #如果請求方法時PRI
/* We do not support SPDY or HTTP/2.0 */
return (synth(405)); #narnish4.0不支持HTTP2.0協議,下一跳synth引擎並指定405響應碼
}
if (req.method != "GET" && #如果請求方法不是常規方法
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe); #下一跳到pipe引擎
}
if (req.method != "GET" && req.method != "HEAD") { #如果不是GET或HEAD方法
/* We only deal with GET and HEAD by default */
return (pass); #下一跳到pass引擎
}
if (req.http.Authorization || req.http.Cookie) { #如果請求中包含敏感信息
/* Not cacheable by default */
return (pass); #下一跳到pass引擎
}
return (hash); #符合上面所有if語句,則使用hash引擎
}
通過查看上面的默認 vcl 配置,可以總結出 vcl 的基本語法:
sub subroutine {
...
}
if CONDITION {
...
} else {
...
}
return(), hash_data()
-
內建函數與關鍵字
函數
regsub(str, regex, sub) 正則匹配替換
regsuball(str, regex, sub) 正則匹配全局替換
ban(boolean expression)
hash_data(input) 數據hash計算
synthetic(str) 字符串處理
關鍵字
call subroutine 調用子例程
return(action) 返回值
new 新鍵對象
set 設置變量
unset 撤銷變量
操作符
比較操作符
== , != , ~ , > , >= , < , <=
邏輯操作符
&& , || , !
-
變量
內建變量
req.* request 表示由客戶端發來的請求報文相關
req.http.*
req.http.User-Agent, req.http.Referer, ...
bereq.* 由varnish發往BE主機的httpd請求相關
bereq.http.*
beresp.* 由BE主機響應給varnish的響應報文相關
beresp.http.*
resp.* 由varnish響應給client相關;
obj.* 存儲在緩存空間中的緩存對象的屬性;只讀
常用變量
bereq.*, req.*
bereq.http.HEADERS
bereq.request, req.request 請求方法
bereq.url, req.url 請求的url
bereq.proto 請求的協議版本
bereq.backend 指明要調用的後端主機
req.http.Cookie 客戶端的請求報文中Cookie首部的值;
req.http.User-Agent ~ "chrome"
sub vcl_recv {
if (req.url ~ "(?i)^/(login|admin)") {
return(pass);
}
}
beresp.*, resp.*
beresp.http.HEADERS
beresp.status, resp.status 響應的狀態碼
reresp.proto, resp.proto 協議版本
beresp.backend.name BE主機的主機名
beresp.ttl 後端主機響應的內容的餘下的可緩存時長
sub vcl_backend_response {
if (beresp.http.cache-control !~ "s-maxage") {
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
unset beresp.http.Set-Cookie;
set beresp.ttl = 3600s;
}
}
}
obj.*
obj.hits 此對象從緩存中命中的次數
obj.ttl 對象的ttl值
server.*
server.ip varnish主機的IP
server.hostname varnish主機的Hostname
client.*
client.ip 發請求至varnish主機的客戶端IP
sub vcl_recv {
if (req.restarts == 0) {
if (req.http.X-Fowarded-For) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
}
}
網絡拓撲如下
-
緩存的修剪
Purge
purge 對請求的 url 進行清除
acl Admin {
"192.168.30.174"/32; #定義管理主機組
"192.168.30.74";
}
sub vcl_recv {
if (req.method == "PURGE") { #如果使用PURGE方法請求
if (!client.ip ~ Admin) {
return(synth(405,"Permission denied for " + client.ip)); #且對端IP屬於管理機組
}
return(purge); #則下一跳至purge引擎
}
}
sub vcl_purge {
return (synth(200,"Purged."));
}
acl 定義訪問控制列表
acl 名稱 {
"IP地址"[/子網掩碼];
...
}
在測試的過程中,發現 varnish 在緩存時,會對整個 URL 進行 hash 計算,也就是說
curl http://192.168.30.75
curl http://192.168.30.75/index.html
訪問的內容雖說一樣,但是在緩存時是兩條不同的記錄,但是
curl http://192.168.30.75
curl 192.168.30.75
卻是同一條記錄
Banning
ban 規則推薦在命令行管理接口中使用,在配置文件中定義使用反而更加死板
在命令行管理接口中使用
語法:ban <field> <operator> <arg>
ban req.http.host ~ (?i)^192.168.30.74 #清除指定目標主機的緩存
200
ban req.url ~ (?i)^/index.html #清除指定URL的緩存
200
在配置文件中使用
sub vcl_recv {
if (req.method == "BAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
return(synth(200, "Ban added"));
}
}
-
Varnish調度
varnish 作爲一個緩存服務器,同時也是一臺代理服務器。所以同樣業具有向後端主機調度的功能。當後端主機有多臺時,就需要在varnish 中定義後端主機組,並根據使用場景規定調度算法。
Round_Robin
vcl 4.0;
import directors; #在使用調度功能之前,必須事前聲明使用directors模塊
backend server1 { #定義單個後端主機
.host = "192.168.30.174";
.port = "80";
}
backend server2 {
.host = "192.168.30.69";
.port = "80";
}
sub vcl_init { #在init引擎中新鍵組,並將後端主機加載進指定組中
new webservers = directors.round_robin();
webservers.add_backend(server1);
webservers.add_backend(server2);
}
sub vcl_recv {
set req.backend_hint = webservers.backend(); #在recv引擎中調用指定的組
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 69 test2
This is 174 test3
This is 69 test4
This is 174 test5
This is 69 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10
Random
sub vcl_init { #在init引擎中新鍵組,並將後端主機加載進指定組中
new webservers = directors.random(); #使用random調度算法
webservers.add_backend(server1,2);
webservers.add_backend(server2,1);
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 174 test2
This is 174 test3
This is 174 test4
This is 174 test5
This is 174 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10
基於cookie的session sticky
sub vcl_init {
new webservers = directors.hash(); #聲明使用hash調度算法
webservers.add_backend(server1,1);
webservers.add_backend(server2,1); #基於hash的會話綁定也支持定義權重
}
sub vcl_recv {
set req.backend_hint = webservers.backend(req.http.cookie); #指定進行hash計算的目標
}
-
健康狀態檢測
probe HTTP_check {
.url = "/index.html"; #請求的方法
.interval = 2s; #檢測頻率
.timeout = 1s; #超時時間
.expected_response = 200; #期望的到的返回碼
.window = 5; #基於最近的多少次檢查來判斷其健康狀態
.threshold = 4; #最少成功次數
}
backend server1 {
.host = "192.168.30.174";
.port = "80";
.probe = HTTP_check; #調用probe方法
}
backend server2 {
.host = "192.168.30.69";
.port = "80";
.probe = HTTP_check;
}
檢測的方法:
url:檢測時要請求的URL,默認爲”/";
request:發出的具體請求;
request =
"GET /.healthtest.html HTTP/1.1" #使用的方法與協議
"Host: www.magedu.com" #請求的host
"Connection: close" #短連接
使用 backend.list 查看後端主機狀態
backend.list
200
Backend name Refs Admin Probe
default(192.168.30.174,,80) 4 probe Healthy (no probe)
server1(192.168.30.174,,80) 4 probe Healthy 5/5
server2(192.168.30.69,,80) 4 probe Sick 0/5
語法:backend.set_health <backend_expression> <state>
state:
sick:管理down
healthy:管理up
auto:probe auto
varnish> backend.set_health server1 sick
200
varnish> backend.list
200
Backend name Refs Admin Probe
default(192.168.30.174,,80) 4 probe Healthy (no probe)
server1(192.168.30.174,,80) 4 sick Healthy 5/5 #設定其狀態爲sick
server2(192.168.30.69,,80) 4 probe Sick 0/5 #檢測狀態爲sick
後端主機屬性
backend default {
.host = "192.168.30.174";
.port = "80";
.connect_timeout = 0.5s; #連接超時時間
.first_byte_timeout = 20s; #響應超時時間
.between_bytes_timeout = 5s; #傳輸間隔超時時間
.max_connections = 50; #最大併發連接書
}