LAMP+haproxy+varnish实现网站访问的动静分离及静态资源缓存

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

wKiom1Yw10aDt_-tAARoZdsrL2k058.jpg

实验目标:
1.    LAMP节点提供用户动态请求访问,数据库单独有数据库节点提供;
2.    LAMP动态网站有两台服务器,提供负载均衡;
3.    静态网站服务器节点提供用户的静态资源请求访问;存在两台静态web服务器,其网站静态资源在静态服务器上存放;
4.    用户的静态请求访问后缓存在varnish服务器上,实现访问加速
5.    前端的haproxy提供反向代理功能,将用户的动态资源请求发送给后端LAMP节点,静态资源请求发往后端静态web服务器;
6.    该架构考虑还不健全,如静态内容的一致性,数据库的单点故障,只是一个不成熟的架构实现简单的动静分离及缓存服务器实现;

实现过程:

一. LAMP构建
================ ha.stu31.com : 172.16.31.10 ================
vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.31.10 ha.stu31.com ha
172.16.31.11 node1.stu31.com node1
172.16.31.12 node2.stu31.com node2
172.16.31.13 node3.stu31.com node3
172.16.31.14 node4.stu31.com node4
172.16.31.15 mysql.stu31.com mysql
172.16.31.16 node5.stu31.com node5
172.16.31.17 node6.stu31.com node6
=======
ssh-keygen -t rsa -P "" -f "/root/.ssh/id_rsa"
for i in {1..6}; do ssh-copy-id -i .ssh/id_rsa.pub nodei;doneforiin1..6;doscp/etc/hostsnodei:/etc/hosts; done
for i in {1..6}; do ssh nodei"ntpdateu202.112.10.60";doneforiin1..6;dosshnodei date; done
for i in {1..6}; do ssh nodei"mount/dev/cdrom/mnt/cdrom";doneforiin1..2;dosshnodei "yum -y install httpd php  mysql  php-mysql"; done
for i in {1..2}; do ssh nodei"servicehttpdstart";donevimindex.php<?phpphpinfo();?>///////////foriin1..2;doscpindex.phproot@nodei:/var/www/html; done


====================== windows ======================
浏览器访问 http://172.16.31.11/index.php
浏览器访问 http://172.16.31.12/index.php



============== mysql.stu31.com : 172.16.31.15 ================
mount /dev/cdrom /mnt/cdrom/
yum -y install mysql-server mysql
service mysqld start
mysql
mysql> create schema bbsdb;
mysql> grant all on bbsdb.* to 'bbsadmin'@'172.16.%.%'identified by 'oracle';
mysql> flush privileges;
mysql> quit


============== node1.stu31.com : 172.16.31.11 ================
wget http://download.comsenz.com/DiscuzX/3.2/Discuz_X3.2_SC_UTF8.zip
unzip -d /var/www/html/ Discuz_X3.2_SC_UTF8.zip
cd /var/www/html/upload
chmod -R go+w config/ data uc_*


====================== windows ======================
浏览器访问 http://172.16.31.11/upload/install
1. 检查安装环境:
2. 设置运行环境:点击选择全新安装
3. 安装数据库
填写数据库信息
数据库服务器: 172.16.31.15
数据库名: bbsdb
数据库用户名: bbsadmin
数据库密码: oracle
数据表前缀: pre_
系统邮箱: admin@stu31.com
填写管理员信息
管理员账号: admin
管理员密码: admin
重复密码: admin
管理员Email: admin@stu31.com
设置好数据库后点击安装即可


============== node2.stu31.com : 172.16.31.12 ===============

1. cd /var/www/html; scp -r upload/ readme/ utility/ node2:/var/www/html/
2. 重新解压软件包到node2,重新安装一遍,删除数据库重新安装(因为数据库中无重要内容)。



访问测试
====================== windows ======================
http://172.16.31.11/upload/forum.php
http://172.16.31.12/upload/forum.php
能正常访问证明LAMP平台构建成功



二. 静态web服务器构建

============== node5.stu31.com : 172.16.31.16 ===============
scp node1:/root/Discuz_X3.2_SC_UTF8.zip ./
(wget http://download.comsenz.com/DiscuzX/3.2/Discuz_X3.2_SC_UTF8.zip)
unzip -d /var/www/html/ Discuz_X3.2_SC_UTF8.zip
service httpd start


============== node6.stu31.com : 172.16.31.17 ===============
scp node1:/root/Discuz_X3.2_SC_UTF8.zip ./
(wget http://download.comsenz.com/DiscuzX/3.2/Discuz_X3.2_SC_UTF8.zip)
unzip -d /var/www/html/ Discuz_X3.2_SC_UTF8.zip
service httpd start


访问测试
====================== windows ======================
http://172.16.31.16/upload/forum.php
http://172.16.31.17/upload/forum.php
只能看到静态界面



三. Varnish缓存服务器构建

============== node3.stu31.com : 172.16.31.13 ===============

wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-3.0.6-1.el6.x86_64.rpm
wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-docs-3.0.6-1.el6.x86_64.rpm
wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-libs-3.0.6-1.el6.x86_64.rpm
yum -y install *.rpm

vim /etc/sysconfig/varnish
NFILES=131072
MEMLOCK=82000
#NPROCS="unlimited"            #加注释
DAEMON_COREFILE_LIMIT="unlimited"        #去注释
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=80              #修改
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_MIN_THREADS=1            #修改
VARNISH_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE="file,VARNISHSTORAGEFILE,{VARNISH_STORAGE_SIZE}"
VARNISH_TTL=120
DAEMON_OPTS="-a VARNISHLISTENADDRESS:{VARNISH_LISTEN_PORT} \
             -f VARNISHVCLCONF T{VARNISH_ADMIN_LISTEN_ADDRESS}:VARNISHADMINLISTENPORT t{VARNISH_TTL} \
             -w VARNISHMINTHREADS,{VARNISH_MAX_THREADS},{VARNISH_THREAD_T               -u varnish -g varnish \               -S{VARNISH_SECRET_FILE} \
             -s {VARNISH_STORAGE}"  ///////////////////////   ( grep -v ^# /etc/sysconfig/varnish |sed '/^/d' )

cp /etc/varnish/default.vcl{,.bak}
vim /etc/varnish/default.vcl
:set nu
:15,s@^#@@g    backend default {    .host = "127.0.0.1";    .port = "80";  }  #   # Below is a commented-out copy of the default VCL logic.  If you  # redefine any of these subroutines, the built-in logic will be  # appended to your code.   sub vcl_recv {       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;          }       }       if (req.request != "GET" &&         req.request != "HEAD" &&         req.request != "PUT" &&         req.request != "POST" &&         req.request != "TRACE" &&         req.request != "OPTIONS" &&         req.request != "DELETE") {           /* Non-RFC2616 or CONNECT which is weird. */           return (pipe);       }       if (req.request != "GET" && req.request != "HEAD") {           /* We only deal with GET and HEAD by default */           return (pass);       }       if (req.http.Authorization || req.http.Cookie) {           /* Not cacheable by default */           return (pass);       }       return (lookup);   }     sub vcl_pipe {          # Note that only the first request to the backend will have       # X-Forwarded-For set.  If you use X-Forwarded-For and want to       # have it set for all requests, make sure to have:       # set bereq.http.connection = "close";       # here.  It is not set by default as it might break some broken web       # applications, like IIS with NTLM authentication.       return (pipe);   }     sub vcl_pass {       return (pass);   }     sub vcl_hash {       hash_data(req.url);       if (req.http.host) {           hash_data(req.http.host);       } else {           hash_data(server.ip);       }       return (hash);   }     sub vcl_hit {       return (deliver);   }     sub vcl_miss {       return (fetch);   }    sub vcl_fetch {       if (beresp.ttl <= 0s ||           beresp.http.Set-Cookie ||           beresp.http.Vary == "*") {                  /*                   * Mark as "Hit-For-Pass" for the next 2 minutes                   */                  set beresp.ttl = 120 s;                  return (hit_for_pass);       }       return (deliver);   }     sub vcl_deliver {       return (deliver);   }     sub vcl_error {       set obj.http.Content-Type = "text/html; charset=utf-8";       set obj.http.Retry-After = "5";       synthetic {"   <?xml version="1.0" encoding="utf-8"?>   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">   <html>     <head>       <title>"} + obj.status + " " + obj.response + {"</title>     </head>     <body>       <h1>Error "} + obj.status + " " + obj.response + {"</h1>       <p>"} + obj.response + {"</p>       <h3>Guru Meditation:</h3>       <p>XID: "} + req.xid + {"</p>       <hr>       <p>Varnish cache server</p>     </body>   </html>   "};       return (deliver);   }     sub vcl_init {          return (ok);   }     sub vcl_fini {          return (ok);   }  ///////////////////    vim /etc/varnish/web.vcl  :set paste  ######定义ACL  acl purgers {                    #定义acl,实现IP地址过滤     "127.0.0.1";     "172.16.0.0"/16;  }  ######定义健康状态检测  probe dynamic {                  #设置动态网站服务器健康状态检测     .url = "/index.html";     .interval = 5s;     .timeout = 1s;      .expected_response= 200;  }           #这里设置了两个健康状态检测主要是为了区分动、静网站  probe static {                   #设置动态网站服务器健康状态检测     .url = "/index.html";       #定义检测的页面     .interval = 5s;              #探测请求的发送周期,默认为5秒     .timeout = 1s;               #每次探测请求的过期时间     .expected_response = 200;  }  ######定义后端服务器  backend app1 {                  #定义一个后端服务器     .host = "172.16.31.11";     #服务器地址     .port = "80";              #服务器监听端口     .probe = dynamic;           #健康状态检测  }  backend app2 {     .host = "172.16.31.12";      .port = "80";     .probe = dynamic;  }  backend web1 {              .host = "172.16.31.16";     .port = "80";     .probe = static;  }  backend web2 {     .host = "172.16.31.17";     .port = "80";     .probe = static;  }  ######定义后端动态服务器组,实现负载均衡效果  director apps random {          #定义一个后端服务器组,实现负载均衡效果      {          .backend = app1;       #调用前面已定义过的后端主机      .weight = 2;           #设置权重      }      {      .backend = app2;      .weight = 2;      }  }  ######定义后端静态服务器组,实现负载均衡效果  director webs random {      {         .backend = web1;         .weight = 2 ;      }      {         .backend = web2;         .weight = 2 ;      }  }  ######定义vcl_recv函数,实现请求到达并成功接收后调用此函数中定义的规则  sub vcl_recv {  ######定义动、静分离,以".php"或".php?后面跟所有文件"结尾的请求都发送到动态服务器,其他请求都发送到静态服务器     if (req.url ~ "\.php(\?\.*|)") {
   set req.backend = apps;
    }else {
   set req.backend = webs;
    }
   return(lookup);
######定义允许清除缓存的IP地址,调用的是前面定义的ACL
   if (req.request == "PURGE") {
       if (!client.ip ~ purgers) {
       error 405 "Method not allowed";
    }
       return(lookup);
    }
######重新定义http请求首部,让后端服务器可以记录请求客户端的真实IP地址
       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;
           }
        }
######除了定义的请求方法外,其他请求都到后端服务器
   if (req.request != "GET" &&
       req.request != "HEAD" &&
       req.request != "PUT" &&
       req.request != "POST" &&
       req.request != "TRACE" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
       return (pipe);
    }
   if (req.request != "GET" && req.request !="HEAD") {
       return (pass);
    }
######定义不缓存认证与Cookie信息
   if (req.http.Authorization || req.http.Cookie) {
       return (pass);
    }
######定义压缩功能
   if (req.http.Accept-Enconding) {
      if (req.url ~ "\.(jpg|jpeg|gif|bmp|png|flv|gz|tgz|tbz|mp3)"){            remove req.http.Accept-Encoding;        remove req.http.Cookie;        } else if (req.http.Accept-Encoding ~ "gzip") {        set req.http.Accept-Encoding = "gzip";        } else if (req.http.Accept-Encoding ~ "deflate") {        set req.http.Accept-Encoding = "deflate";        } else { remove req.http.Accept-Encoding;        }      }  ######定义指定格式结尾的文件去除Cookie信息     if (req.request == "GET" && req.url ~"\.(jpeg|jpg|gif|png|bmp|swf)") {
   unset req.http.cookie;
    }
######定义防盗链设置
   if (req.http.referer ~ "http://.*") {
       if (!(req.http.referer ~ "http://.*\.baidu\.com" ||req.http.referer ~ "http://.*\.google\.com.*")) {
              set req.http.host ="www.stu31.com";
         set req.url = "http://172.16.31.10/error.html";
    }
    }
}
######定义vcl_hash函数
sub vcl_hash {
    hash_data(req.url);
   if (req.http.host) {
       hash_data(req.http.host);
    }else {
       hash_data(server.ip);
    }
   return(hash);
}
######定义vcl_hit函数
sub vcl_hit {
   if (req.request == "PURGE") { #语法方法为"PURGE"
      purge;                     #清除缓存
      error 200 "Purged.";      #返回错误状态码为"200"
    }
   return(deliver);
}
######定义vcl_miss函数
sub vcl_miss {
   if (req.request == "PURGE") {
   purge;
   error 404 "Not In Cache.";
    }
   return(fetch);
}
######定义vcl_psss函数
sub vcl_pass {
   if (req.request == "PURGE") {
      error 502 "Purged On A Passed Object.";
    }
   return(pass);
}
######定义vcl_fetch函数
sub vcl_fetch {
######定义缓存,如果匹配到已定义文件结尾的缓存1天,其他则缓存1小时
   if (req.request == "GET" && req.url ~"\.(html|jpg|png|bmp|jpeg|gif|js|ico|swf|css)$") {
      set beresp.ttl = 1d;
      set beresp.http.expires = beresp.ttl;
    }else {
      set beresp.ttl = 1h;
    }
   return(deliver);
}
######定义在http首部中,如果请求命中显示"HIT",未命中则显示"MISS",通过F12可查看缓存命中状态及varnish服务器IP
sub vcl_deliver {
   if (obj.hits > 0) {
      set resp.http.X-Cache = "Hit from " + server.ip;
    }else {
      set resp.http.X-Cache = "MISS";
    }
}
//////////////////////////// end for /etc/varnish/web.vcl

/etc/rc.d/init.d/varnish  start
netstat -tnlp | grep varnish 

varnishadm -S/etc/varnish/secret -T 127.0.0.1:6082
varnish> vcl.load cache web.vcl
varnish> vcl.use cache
varnish> vcl.list
varnish> quit


============== node4.stu31.com : 172.16.31.14 ===============
scp node3:/root/*.rpm ./
yum -y install *.rpm
scp node3:/etc/sysconfig/varnish /etc/sysconfig/
scp node3:/etc/varnish/*.vcl /etc/varnish

/etc/rc.d/init.d/varnish  start
netstat -tnlp | grep varnish 

varnishadm -S/etc/varnish/secret -T 127.0.0.1:6082
varnish> vcl.load cache web.vcl
varnish> vcl.use cache        #立刻生效, 重启varnish失效
varnish> vcl.list
varnish> quit





如果我们希望后端的web服务器记录客户端访问的真实IP地址,我们需要配置httpd的配置文件中的日志格式:
node1, node2, node5, node6四个节点都要
vim /etc/httpd/conf/httpd.conf   
LogFormat "%{X-Forwarded-For}i %l %u%t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""combined
///////////
service httpd reload
echo "ok" > /var/www/html/index.html    #此页面用于varnish做健康检测





四. HAProxy反向代理构建

=============== ha.stu31.com : 172.16.31.10 ================
mount /dev/cdrom /mnt/cdrom
yum -y install haproxy

vim /etc/rsyslog.conf
# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log
local2.*                                                /var/log/haproxy.log     #添加此行
//////////////
service rsyslog restart

cd /etc/haproxy/
cp haproxy.cfg{,.bak}
vim haproxy.cfg
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

:set paste 

listen stats        #添加此块
    mode http
    bind 0.0.0.0:1080
    stats enable
    stats hide-version
    stats uri     /haproxyadmin?stats
    stats realm   Haproxy\ Statistics
    stats auth    admin:admin
    stats admin if TRUE

frontend http-in        #修改很多内容
    bind *:80
    mode http
    log global
    option httpclose
    option logasap
    option dontlognull
    capture request  header Host len 20
    capture request  header Referer len 60
    acl url_static       path_beg       -i /upload/static/image/
    acl url_static       path_end       -i .html .jpeg .gif .png .jpg
    acl url_dynamic      path_end       -i .php .css .js .jsp
    use_backend static_servers         if url_static
    use_backend dynamic_servers        if url_dynamic
    default_backend static_servers

backend static_servers
    balance roundrobin
    server staticsrv1 172.16.31.13:80 check maxconn 3000
    server staticsrv2 172.16.31.14:80 check maxconn 3000

backend dynamic_servers
    balance source
    server dynamicsrv1 172.16.31.11:80 check maxconn 3000
    server dynamicsrv2 172.16.31.12:80 check maxconn 3000
//////////////////

service haproxy start




五. 测试访问

====================== windows =====================
访问论坛
http://172.16.31.10/upload/forum.php

访问haproxy的状态页
http://172.16.31.10:1080/haproxyadmin?stats
admin
admin




window浏览器F12检查缓存命中情况

varnish上的日志
[root@node3 ~]# varnishlog

原始服务器的日志
[root@node1 ~]# tail /var/log/httpd/access_log








本文转自 zhuhc1988 51CTO博客,原文链接:http://blog.51cto.com/changeflyhigh/1707489,如需转载请自行联系原作者
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
235
分享
相关文章
【Azure Redis 缓存】Azure Redis服务开启了SSL(6380端口), PHP如何访问缓存呢?
【Azure Redis 缓存】Azure Redis服务开启了SSL(6380端口), PHP如何访问缓存呢?
网站图片缓存设置不当可能会导致哪些问题?
【10月更文挑战第18天】网站图片缓存的合理设置至关重要,需要综合考虑图片的性质、更新频率、用户体验、服务器性能等多方面因素,以避免出现上述各种问题,确保网站的正常运行和用户信息的安全。
网站的图片资源是否需要设置缓存?
【10月更文挑战第18天】网站的图片资源一般是需要设置缓存的,但要根据图片的具体特点和网站的需求,合理设置缓存时间和缓存策略,在提高网站性能和用户体验的同时,确保用户能够获取到准确、及时的图片信息。
如何利用浏览器的缓存来优化网站性能?
【10月更文挑战第23天】通过以上多种方法合理利用浏览器缓存,可以显著提高网站的性能,减少网络请求,加快资源加载速度,提升用户的访问体验。同时,要根据网站的具体情况和资源的特点,不断优化和调整缓存策略,以适应不断变化的业务需求和用户访问模式。
213 7
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
【Azure Redis 缓存】C#程序是否有对应的方式来优化并缩短由于 Redis 维护造成的不可访问的时间
【Azure Redis 缓存】C#程序是否有对应的方式来优化并缩短由于 Redis 维护造成的不可访问的时间
【Azure Redis 缓存】Azure Redis加入VNET后,在另一个区域(如中国东部二区)的VNET无法访问Redis服务(注:两个VNET已经结对,相互之间可以互ping)
【Azure Redis 缓存】Azure Redis加入VNET后,在另一个区域(如中国东部二区)的VNET无法访问Redis服务(注:两个VNET已经结对,相互之间可以互ping)
|
18天前
|
Redis--缓存击穿、缓存穿透、缓存雪崩
缓存击穿、缓存穿透和缓存雪崩是Redis使用过程中可能遇到的常见问题。理解这些问题的成因并采取相应的解决措施,可以有效提升系统的稳定性和性能。在实际应用中,应根据具体场景,选择合适的解决方案,并持续监控和优化缓存策略,以应对不断变化的业务需求。
70 29
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
Redis应用—8.相关的缓存框架
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等