varnish缓存代理的安装

时间:2022-10-21 19:40:02

 

 

二、安装varnish

1、安装依赖关系的软件包(注:使用centos在线yum源)

[root@varnish ~]# yum -y install autoconfautomake libedit-devel libtool ncurses-devel pcre-devel pkgconfigpython-docutils python-sphinx

2、安装varnish

Varnish的官方网址为http://varnish-cache.org,可以在这里下载最新版本的软件。

下载地址:https://www.varnish-cache.org/content/varnish-cache-403

注意:Varnish网站有时会被墙。

Git 下载:gitclone https://github.com/varnish/Varnish-Cache /var/tmp/

解压,进入解压目录编译安装:

[root@varnish ~]# tar zxfvarnish-4.0.3.tar.gz

[root@varnish ~]# cdvarnish-4.0.3/

[root@varnishvarnish-4.0.3]# export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

注:

./autogen.sh 

如果从Git库下载的安装包时才需要运行,用于生成configure编译文件。

配置:

[root@varnishvarnish-4.0.3]# ./configure

注:不指定安装路径,默认是安装在/usr/local目录下

编译、安装

[root@varnishvarnish-4.0.3]# make && make install

复制 vcl 文件(在编译安装目录下),如果安装目录里没有 default.vcl 文件。

复制到安装目录的/usr/local/var/varnish/目录下(当然并无必需要求在哪个目录,因为正式启动时还得指定这个文件的目录)

[root@varnishvarnish-4.0.3]# cp etc/example.vcl  /usr/local/var/varnish/default.vcl

三、varnish实例解析:

varnish 配置基本上是编辑 VCL(Varnish Configuration Language) 文件,varnish有一套自定义 VCL 语法,启动时,会将配置文件编译为C语言,再执行。

varnish 4.0开始,每个VCL文件必须在开始行声明它的版本“vcl 4.0;

块(子程序)由大括号分隔,语句用分号结束。所有的关键字及预设子程序名都是全小写。

注释:支持 //多行时还可以使用 /*.. */

1、后端服务器地址池配置及后端服务器健康检查

varnish"后端"或者""服务器的概念。backend server提供给varnish加速的内容。实际上就是给varnish添加可供访问的web服务器,如果有多台web服务器时,可添加多个backend块。

1)后端服务器定义:

命令:backend。这个定义为最基本的反向入口定义,用于varnish连接对应的服务器,如果没有定义或定义错误则用户无法访问正常页面。

语法格式:

backend name{

    .attribute = "value";

}

说明:backend 是定义后端关键字,name 是当前后端节点的别名,多个后端节点时,name 名不能重复,否则覆盖。花括号里面定义当前节点相关的属性(键=值)。除默认节点外其它节点定义后必需有调用,否则varnish无法启动。后端是否正常可以通过std.healthy(backend)判断。

支持运算符:

=   (赋值运算)

==  (相等比较)

~    (匹配,可以使用正则表达式,或访问控制列表)

!~    (不匹配,可以使用正则表达式,或访问控制列表)

  (非)

&&   (逻辑与)

||   (逻辑或)

属性列表:

.host="xxx.xxx.xxx.xxx";     //要转向主机(即后端主机)的IP或域名,必填键/值对。
.port="8080";        //
主机连接端口号或协议名(HTTP等),默认80
.host_header='';    //
请示主机头追加内容
.connect_timeout=1s;     //
连接后端的超时时间
.first_byte_timeout=5s;    //
等待从后端返回的第一个字节时间
.between_bytes_timeout=2s;     //
每接收一个字节之间等待时间
.probe=probe_name;        //
监控后端主机的状态,指定外部监控name或者内部直接添加
.max_connections=200;    //
设置最大并发连接数,超过这个数后连接就会失败

例:(下面两个例子结果是一样的,但第二个例子中更适用于集群,可以方便批量修改)

backend web{

    .host="192.168.31.83";

    .port="80";

    .probe={          //直接追加监控块.probe是一个的参数

        .url="/";

        .timeout=2s;

    }

}

probe web_probe{   //监控必需定义在前面,否则后端调用找不到监控块。

    .url="/";

    .timeout=2s;

}

 

backend web{

    .host="192.168.31.83";

    .port="80";

    .probe=web_probe;   //调用外部共用监控块

}

 

2)监视器定义:

命令:probe 。监控可以循环访问指定的地址,通过响应时间判定服务器是否空闲或正常。这类命令非常适用于集群中某些节点服务器崩溃或负载过重,而禁止访问这台节点服务器。

语法格式:

probe name{

    .attribute = "value";

}

说明:probe 是定义监控关键字,name 是当前监控点的别名,多个监控节点时,name 名不能重复,否则覆盖。花括号里面定义当前节点相关的属性(键=值)。

没有必填属性,因为默认值就可以正常执行操作。

属性列表:

.url="/";   //指定监控入口URL地址,默认为"/"

.request="";  //指定监控请求入口地址,比 .url 优先级高。
.expected_response="200";   //
请求响应代码,默认是 200
.timeout=2s;   //
请求超时时间。
.interval=5s;    //
每次轮询请求间隔时间,默认为 5s
.initial=-1;     //
初始启动时以.window轮询次数中几次良好后续才能使用这个后端服务器节点,默认为 -1 ,则轮询完 .window 所有次数良好判定为正常。
.window=8;   //
指定多少轮询次数,用于判定服务器正常,默认是 8
.threshold=3;   //
必须多少次轮询正常才算该后端节点服务器正常,默认是 3

例:创建健康监测,定义健康检查名称为backend_healthcheck

probe backend_healthcheck {

        .url = "/";

        .timeout = 1s;

        .interval = 5s;

        .window = 5;

        .threshold = 3;

    }

在上面的例子中varnish将每5s检测后端,超时设为1s。每个检测将会发送get /的请求。如果5个检测中大于3个是成功,varnish就认为后端是健康的,反之,后端就有问题了。

 

3)集群负载均衡directors:

varnish可以定义多个后端,也可以将几个后端放在一个后端集群里面已达到负载均衡的目的。

你也可以将几个后端组成一组后端。这个组被叫做Directors。可以提高性能和弹性。

directorsvarnish负载均衡模块,使用前必需引入directors模块,directors模块主要包含:round_robinrandomhashfallback负载均衡模式。

round_robin : 循环依次逐个选择后端服务器。

random 随机选择后端服务器,可设置每个后端权重增加随机率。

hash :  通过散列随机选择对应的后端服务器且保持选择对应关系,下次则直接找对应的后端服务器。

Fallback:后备

注意:randomhash 有权重值设置,用于提高随机率。每个后端最好都配置监控器(后端服务器正常监测)以便directors自动屏蔽不正常后端而不进入均衡列队中。

这些操作需要你载入VMODvarnish module),然后在vcl_init中调用这个VMOD

import directors;                # load the directors

backend web1 {

.host = "192.168.0.10";

.port = "80";

.probe =backend_healthcheck;

}

backend web2 {

.host = "192.168.0.11";

.port = "80";

.probe = backend_healthcheck;

}

//初始化处理

sub vcl_init {            //调用vcl_init初始化子程序创建后端主机组,即directors

    new  web_cluster = directors.round_robin();//使用new关键字创建drector对象,使用round_robin算法

    web_cluster.add_backend(web1);   //添加后端服务器节点

web_cluster.add_backend(web2);

}

//开始处理请求

sub vcl_recv {                     //调用vcl_recv子程序,用于接收和处理请求

    set  req.backend_hint = web_cluster.backend();     //选取后端

}

说明:

set命令是设置变量

unset命令是删除变量

web_cluster.add_backend( backend , real);  添加后端服务器节点,backend 为后端配置别名,real为权重值,随机率计算公式:100 * (当前权重 / 总权重)

req.backend_hintvarnish的预定义变量,作用是指定请求后端节点

vcl对象需要使用new关键字创建,所有可创建对象都是内定的,使用前必需import,所有new操作只能在vcl_init子程序中。

扩展:varnish将不同的url发送到不同的后端server

import directors;                # load the directors

backend web1 {

.host = "192.168.0.10";

.port = "80";

.probe =backend_healthcheck;

}

backend web2 {

.host = "192.168.0.11";

.port = "80";

.probe = backend_healthcheck;

}

backendimg1 {

       .host = "img1.lnmmp.com";

       .port = "80";

       .probe = backend_healthcheck;

}

backendimg2 {

       .host = "img2.lnmmp.com";

       .port = "80";

       .probe = backend_healthcheck;

}

//初始化处理

sub vcl_init {            //调用vcl_init初始化子程序创建后端主机组,即directors

    new  web_cluster = directors.round_robin();//使用new关键字创建drector对象,使用round_robin算法

    web_cluster.add_backend(web1);   //添加后端服务器节点

web_cluster.add_backend(web2);

new img_cluster =directors.random();

img_cluster.add_backend(img1,2);   //添加后端服务器节点,并且设置权重值

img_cluster.add_backend(img2,5);

}

//根据不同的访问域名,分发至不同的后端主机组

sub vcl_recv {

if (req.http.host  ~  "(?i)^(www.)?benet.com$") {

              set  req.http.host = "www.benet.com";

              set  req.backend_hint = web_cluster.backend();  //选取后端

       } elsif (req.http.host  ~  "(?i)^images.benet.com$"){

           set  req.backend_hint = img_cluster.backend();

       }

}

说明:中的i就是忽略大小写的意思。(?i)表示开启忽略大小写,而(?-i)表示关闭忽略大小写

 

4)访问控制列表(ACL):

创建一个地址列表,用于后面的判断,可以是域名或IP集合。这个可以用于指定某些地址请求入口,防止恶意请求等。

语法格式:

acl  purgers  {

    "127.0.0.1";

"localhost";

“192.168.134.0/24”

    !"192.168.134.1";

}

说明:acl 是访问列表关键字(必需小写),name 是该列表的别名用于调用,花括号内部是地址集。

注意:如果列表中包含了无法解析的主机地址,它会匹配任何地址。

如果不想让它匹配可以在前添加一个 ! 符号,如上面 !"192.168.134.1"; 

使用ACL只需要用匹配运算符 ~ !~  如:

sub  vcl_recv {

    if (req.method == "PURGE") {    //PURGE请求的处理

        if  (client.ip  ~  purgers) {    

              return(purge);

        } else {

            return(synth(403, "Access denied."));

        }

    }

}

 

5缓存规则配置:

sub vcl_recv {

  // PURGE请求的处理

  if(req.method == "PURGE") {

    if(!client.ip ~ purgers) {

     return (synth(405, "Not Allowed."));

    }

    return (purge);

  }

 

  setreq.backend_hint = web.backend();

 

  //phpasp等动态内容访问请求直接发给后端服务器,不缓存。

  if(req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {

   return (pass);

  }

  //将非GETHEAD访问请求直接发给后端服务器,不缓存。例如POST请求。

  if(req.method != "GET" && req.method != "HEAD") {

       return (pass);

  }

  //如果varnish看到header中有'Authorization'头,它将pass请求。

  if(req.http.Authorization) {

       return (pass);

}

 

  //cookie首部的GET请求也缓存

  if(req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)"){

    unsetreq.http.cookie;

   return (hash);

  }

说明:默认情况,varnish不缓存从后端响应的http头中带有Set-Cookie的对象。如果客户端发送的请求带有Cookie headervarnish将忽略缓存,直接将请求传递到后端。

 

//为发往后端主机的请求添加X-Forward-For首部,首次访问增加X-Forwarded-For 头信息,方便后端程序获取客户端ip,而不是varnish地址

if (req.restarts == 0) {

       if (req.http.x-forwarded-for) {//如果设置过此header则要再次附加上用逗号隔开

           set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", "+ client.ip;

        } else {//如果只有一层代理的话,就无需设置了

           set req.http.X-Forwarded-For = client.ip;

        }

}

说明:X-Forwarded-For是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段

子程序:

 

子程序是一种类似C的函数,但是程序没有调用参数,子程序以 sub 关键字定义。在VCL里子程序是用于管理程序。

注意:所有VCL内置的程序都是以 vcl_ 开头,并已经预置好,在VCL文件中只要声明对应的内置子程序,都会在对应的流程中调用。

三、varnish完整配置实例

1、拓扑环境

Varnish:192.168.31.250

Web01:192.168.31.83

Web02:192.168.31.141

配置web01web02做为后端服务器(过程略)

确保varnish服务器能正常访问web01web02

Varnish缓存代理服务器配置:

2vcl文件配置内容:

[root@varnish ~]# cat  /usr/local/var/varnish/default.vcl

#使用varnish版本4的格式.

vcl 4.0;

#加载后端负载均衡模块

import directors;

#加载std模块

import std;

#创建名为backend_healthcheck的健康检查策略

probe backend_healthcheck {

   .url="/"; 

   .interval = 5s;

   .timeout = 1s;

   .window = 5;

   .threshold = 3;

}

#定义后端服务器

backend web_app_01 {

    .host= "192.168.31.83";

    .port= "80";

   .first_byte_timeout = 9s;

   .connect_timeout = 3s;

   .between_bytes_timeout = 1s;

   .probe = backend_healthcheck;

}

backend web_app_02 {

    .host= "192.168.31.141";

    .port= "80";

   .first_byte_timeout = 9s;

   .connect_timeout = 3s;

   .between_bytes_timeout = 1s;

   .probe = backend_healthcheck;

}

#定义允许清理缓存的IP

acl purgers {

   "127.0.0.1";

   "localhost";

   "192.168.31.0/24";

}

#vcl_init初始化子程序创建后端主机组

sub vcl_init {

    newweb = directors.round_robin();

   web.add_backend(web_app_01);

   web.add_backend(web_app_02);

}

#请求入口,用于接收和处理请求。这里一般用作路由处理,判断是否读取缓存和指定该请求使用哪个后端

sub vcl_recv {

    #请求指定使用web后端集群 .在集群名后加上 .backend()

set req.backend_hint =web.backend();

# 匹配清理缓存的请求

    if(req.method == "PURGE") {

       if (!client.ip ~ purgers) {

           return (synth(405, "Not Allowed."));

        }

        # 是的话就执行清理

       return (purge);

    }

    # 如果不是正常请求就直接穿透没商量

    if (req.method!= "GET" &&

     req.method != "HEAD" &&

     req.method != "PUT" &&

     req.method != "POST" &&

     req.method != "TRACE" &&

     req.method != "OPTIONS" &&

     req.method != "PATCH" &&

     req.method != "DELETE") {

     return (pipe);

    }

    # 如果不是GETHEAD就跳到pass

    if(req.method != "GET" && req.method != "HEAD") {

       return (pass);

    }

    #如果匹配动态内容访问请求就跳到pass

    if(req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {

       return (pass);

    }

    #具有身份验证的请求跳到pass

    if(req.http.Authorization) {

       return (pass);

    }

   

    if(req.http.Accept-Encoding) {

       if (req.url ~"\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$"){

           unset req.http.Accept-Encoding;       

        }elseif (req.http.Accept-Encoding ~ "gzip") {

           set req.http.Accept-Encoding = "gzip";

        }elseif (req.http.Accept-Encoding ~ "deflate") {

           set req.http.Accept-Encoding = "deflate";

        }else {

           unset req.http.Accept-Encoding;

        }

    }

 

    if(req.url ~"\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)"){

       unset req.http.cookie;

       return (hash);

}

# 把真实客户端IP传递给后端服务器后端服务器日志使用X-Forwarded-For来接收

    if(req.restarts == 0) {

       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;

        }

    }

   return (hash);

}

# hash事件(缓存事件)

sub vcl_hash {

   hash_data(req.url);

    if(req.http.host) {

       hash_data(req.http.host);

    }else {

       hash_data(server.ip);

    }

   return (lookup);

}

# 缓存命中事件

sub vcl_hit {

    if(req.method == "PURGE") {

       return (synth(200, "Purged."));

    }

   return (deliver);

}

# 缓存不命中事件

sub vcl_miss {

    if(req.method == "PURGE") {

       return (synth(404, "Purged."));

    }

   return (fetch);

}

# 返回给用户的前一个事件通常用于添加或删除header

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";

}

#取消显示php框架版本的header

unset resp.http.X-Powered-By;

#取消显示web软件版本、Via(来自varnish)header为了安全

    unsetresp.http.Server;

    unsetresp.http.X-Drupal-Cache;

    unsetresp.http.Via;

    unsetresp.http.Link;

unset resp.http.X-Varnish;

#显示请求经历restarts事件的次数

set resp.http.xx_restarts_count= req.restarts;

#显示该资源缓存的时间单位秒

set resp.http.xx_Age =resp.http.Age;

#显示该资源命中的次数

set resp.http.hit_count =obj.hits;

#取消显示Age 为了不和CDN冲突

unset resp.http.Age;

#返回给用户

   return (deliver);

}

# pass事件

sub vcl_pass {

   return (fetch);

}

#处理对后端返回结果的事件(设置缓存、移除cookie信息、设置header头等) fetch事件后自动调用

sub vcl_backend_response {

    #开启grace模式表示当后端全挂掉后即使缓存资源已过期(超过缓存时间) 也会把该资源返回给用户资源最大有效时间为5分钟

set beresp.grace = 5m;

#后端返回如下错误状态码则不缓存

    if(beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {

    setberesp.uncacheable = true;

}

#如请求phpjsp 则不缓存

    if(bereq.url ~ "\.(php|jsp)(\?|$)") {

      setberesp.uncacheable = true;

    }else { //自定义缓存文件的缓存时长,即TTL

       if(bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {

        set beresp.ttl = 15m;

        unset beresp.http.Set-Cookie;

       }elseif (bereq.url ~"\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {

          set beresp.ttl = 30m;

          unset beresp.http.Set-Cookie;

        } else {

           set beresp.ttl = 10m;

           unset beresp.http.Set-Cookie;

        }

      }

    #返回给用户

   return (deliver);

}

sub vcl_purge {

       return (synth(200,"success"));

}

 

sub vcl_backend_error {

    if(beresp.status == 500 ||

       beresp.status == 501 ||

       beresp.status == 502 ||

       beresp.status == 503 ||

       beresp.status == 504) {

       return (retry);

    }

}

 

sub vcl_fini {

   return (ok);

}

 

3、启动varnish

当启动varnish时有两个重要的参数你必须设置: 一个是处理http请求的tcp监听端口,另一个是处理真实请求的后端server

注:如果你使用操作系统自带的包管理工具安装的varnish,你将在下面的文件找到启动参数:

Red Hat, Centos: /etc/sysconfig/varnish


1
):'-a' 参数定义了varnish监听在哪个地址,并用该地址处理http请求,你可能想设置这个参数在众所周知的http 80端口.

例子:

-a  :80

-a  localhost:80

-a  192.168.1.100:8080

-a  '[fe80::1]:80'

-a  '0.0.0.0:8080,[::]:8081'

如果你的webservervarnish运行在同一台机器,你必须换一个监听地址.

2):'-f' VCL-file  or  '-b' backend

-f添加vcl文件,-b定义后端server

varnish需要知道从哪里找到这个需要缓存的http server.你可以用-b参数指定,或者帮把它放在vcl文件中,然后使用-f参数指定.

在启动的时候使用-b是一个快捷的方式.

-b 192.168.1.2:80

注意:如果你指定的是name,这个name必须能解析成一个IPv4或者IPv6的地址

如果你使用-f参数,你启动的时候可以在-f指定vcl文件。

默认的varnish使用100M的内存来缓存对象,如果你想缓存更多,可以使用-s参数

注:Varnish拥有大量的有用的命令行参数,建议查看其帮助

[root@varnish ~]# /usr/local/sbin/varnishd -h

 

启动varnish

[root@varnish ~]# /usr/local/sbin/varnishd -f/usr/local/var/varnish/default.vcl -s malloc,200M -a 0.0.0.0:80

[root@varnish ~]# netstat-anpt | grep 80

tcp     0  0 0.0.0.0:80      0.0.0.0:*      LISTEN      6173/varnishd

2)现在,varnish已经启动和运行,你可以通过varnish访问您的Web应用程序。

打开火狐浏览器

第一次访问

第二次访问

3varnish4配置手动清除缓存

varnish4通过vcl配置清楚缓存

通过vcl配置可以让客户端手动请求清楚缓存,以保证局部数据及时更新,而不用重启varnish服务器。

配置方法:

#允许清除缓存IP

acl purgers {

    "127.0.0.1";

    "localhost";

    "192.168.31.0/24";

}

sub vcl_recv {

    ……

    if (req.method == "PURGE") {

        if (!client.ip ~ purgers) {

            return (synth(405, "NotAllowed."));

        }

        return (purge);   //清除缓存

}

……

}

sub vcl_purge {

        return(synth(200,"success"));

}

打开火狐浏览器,随便进入一个缓存页面,如下图所示。

点击编辑和重发,修改请求类型为 PURGE 再点击 发送

查看返回状态,如果成功则成功清除缓存,可以按 F5 刷新页面,查看新内容。

 

 

 

 

 

 

Varnish工作流程

 

1)、varnish从客户端接收请求后,由vcl_recv状态引擎处理,不能识别的请求将会通过参数pipe交给vcl_pipe状态引擎,需要查找缓存的

请求通过lookup参数将会交给vcl_hash状态引擎,无需缓存的数据通过参数pass将会交给 vcl_pass状态引擎;

2)、vcl_hash状态引擎在接收到请求后会从缓存中查找数据,查询结果有两种,一种是hit缓存命中,另一种是miss缓存未命中;

3)、vcl_hit状态引擎将命中的缓存数据通过参数deliver交给vcl_deliver状态引擎,vcl_deliver状态引擎将数据处理后,最终返回给客户端;

4)、vcl_miss状态引擎将未命中的结果参数fetch交给vcl_fetch状态引擎,vcl_fetch状态引擎将会从数据库中查找数据;

5)、vcl_fetch状态引擎将从数据库中查询到的结果,返回给vcl_deliver状态引擎;

6)、vcl_deliver状态引擎将结果返回给master进程,最终返回给客户端;

 

本文出自 “13110271” 博客,请务必保留此出处http://13120271.blog.51cto.com/13110271/1950626