一、LVS简介

LVS即Linux Virtual Server的缩写,意思为Linux的虚拟服务器,是一个四层负载均衡技术的开源项目。

Linux内核从2.6.10版本之后就集成了LVS,之前的内核版本可以通过打补丁的方式安装。

LVS工作需要基于内核的Netfilter,其主要工作INPUT链上。

 

二、LVS几个重要概念

(一)负载调度算法

负载调度是实现在内核中,调度是以连接为粒度。LVS实现了10种静态算法和动态算法,如下:

静态算法:

  • 轮叫调度(Round-Robin Scheduling)

  • 加权轮叫调度(Weighted Round-Robin Scheduling)

  • 源地址散列调度(Source Hashing Scheduling)

  • 目标地址散列调度(Destination Hashing Scheduling)

动态算法:

  • 最小连接调度(Least-Connection Scheduling)

  • 加权最小连接调度(Weighted Least-Connection Scheduling)

  • 最短预期延时调度(Shortest Expected Delay Scheduling)

  • 不排队调度(Never Queue Scheduling)

  • 基于局部性的最少链接(Locality-Based Least Connections Scheduling)

  • 带复制的基于局部性最少链接(Locality-Based Least Connections with Replication Scheduling)

 

下面就以上算法做一个说明:

1、轮叫调度rr(Round-Robin Scheduling)

最为简单的调度算法,实现无状态的调度,也就是轮询。

挑选算法为i = (i + 1) mod n

不适用于服务器组中性能不一致的情况

不适用于请求服务时间较大的情况

不适用于使用session的站点

注:如果权重为0,则不调度。之后的所有算法都支持该规则。

 

2、加权轮叫调度wrr(Weighted Round-Robin Scheduling)

根据不同权重,分配较多的连接到权重大的服务器上。

算法在RR轮询调度的基础上,算出所有服务器权重的最大公约数,使用服务器最大权重和这个公约数求差求得一个调度权重,然后服务器自身权重大于这个调度权重的就可以被调度。

 

3、源地址散列调度(Source Hashing Scheduling)

它是针对请求的源IP地址的负载均衡,是一种静态映射算法。将源地址IP求hash值后,映射到一台服务器。以后该目标地址访问将始终使用该服务器。直到该服务器权重为0,或者超载的情况下,将重新调度到另一台服务器。

它将来自同一IP的请求尽可能的指向同一台服务器处理,这可以实现Session绑定效果。

算法中使用一个Hash表,使用源IP地址的Hash值作为key,value指向的服务器IP地址。

 

4、目标地址散列调度(Destination Hashing Scheduling)

它是针对目标IP地址的负载均衡,是一种静态映射算法。将目标地址IP求hash值后,映射到一台服务器。以后该目标地址访问将始终使用该服务器。直到该服务器权重为0,或者超载的情况下,将重新调度到另一台服务器。

在实际应用中,源地址散列调度和目标地址散列调度可以结合使用在防火墙集群中,它们可以保证整个系统的唯一出入口。

算法中使用一个Hash表,使用目标服务器IP地址的Hash值作为key,value中的值就是指向的服务器IP地址。

 

5、最小连接调度(Least-Connection Scheduling)

把新的连接请求分配到当前连接数最小的服务器。

调度器记录各个服务器的连接数,它很好的把负载分配到负载较少的服务器上。但是如果服务器性能有差异,性能好的服务器处理完请求后,处于TIME_WAIT状态的连接还占用这资源,也还算做连接数,所以此时负载还是会调度到性能差的服务器上。

overhead = (activeconns<<8) + inactconns,这相当于(activeconns*256) + inactconns

调度到overhead最小的服务器上。

注:算法取自ip_vs_lc_dest_overhead函数。

 

6、加权最小连接调度(Weighted Least-Connection Scheduling)

它是LC的超集。使用权重值后,算法调度新建连接尽可能保证已建立的连接和其权重成正比。

overhead = activeconns*50 + inactconns

load = overhead / weight

然后比较load的大小,取load最小的

注:算法取自ip_vs_wlc_schedule函数

 

7、最短预期延时调度(Shortest Expected Delay Scheduling)

server expected overhead = activeconns + 1

(server expected overhead) / weight

保证优先从权重大的服务器开始建立连接,+1是解决了起点公平,同时+1对权重小的SED值影响大,这也使得算法计算结果倾向选择权重大的服务器。此算法不使用非活动连接数。

注:算法取自ip_vs_sed_dest_overhead

 

8、不排队调度(Never Queue Scheduling)

SED算法的改进。

算法中保证,如果发现有一个空闲的服务器,就立即返回这个服务器给调度器使用。

如果存在空闲服务器,请求将不在性能最好的服务器后排队,而是被调度到空闲的服务器上。

如果不存在空闲的服务器,请求将被发送到最小期望延时的服务器上。

 

9、基于局部性的最少链接(Locality-Based Least Connections Scheduling)

它是针对请求报文的目标IP地址的负载均衡调度,目前主要用于Cache集群系统。

10、带复制的基于局部性最少链接(Locality-Based Least Connections with Replication Scheduling)

它也是针对目标IP地址的负载均衡,目前主要用于Cache集群系统。它与LBLC算法的不同之处是它要维护从一个目标IP地址到一组服务器的映射,而LBLC算法维护从一个目标IP地址到一台服务器的映射。

 

(二)LVS的实现模型

VS/NAT

基于NAT实现的虚拟服务器。

数据通过均衡器后,其数据报文中目标地址(VIP)或者端口将被映射为指定的IP地址(RIP)或端口,然后指定的IP地址或端口的服务器处理完客户请求后,返回给数据报文给均衡器,均衡器根据以前的记录,将响应报文的源地址或端口改回均衡器响应请求时的VIP或端口。

基于NAT技术,实现简单,对于客户端来说整个机器架构是透明的,这很好的隐藏了内部网络主机,同时节约了IP V4的地址。

请求报文和响应报文进出都要经过均衡器,而且均衡器还要修改报文,这势必会使得均衡器称为系统性能的瓶颈。

 

VS/DR

基于直接路由实现的虚拟服务器。

VS/NAT的好处显而易见,但也存在明显的瓶颈问题,响应能力有限。

如果能够将接收请求和响应请求的步骤分离,这样就能减轻均衡器的工作负担,让服务器多承担一点工作,这样整个集群的处理能力大大提升。    
DR模型是在均衡器上,将请求报文中的帧首部的源MAC地址指向使用调度算法挑选出的真实服务器(Real Server)的MAC地址,通过本地非路由交换网络,交由Real Server处理并直接响应请求的客户端。

为了响应客户端,Real Server需要配置VIP地址,要阻止这个VIP的ARP广播和响应,使它在网络中不可见。只有均衡器的VIP才能对外可见。    
DR模型解决了NAT模型的瓶颈问题,减轻了Director的压力,提高了虚拟服务器的并发处理能力。

由于修改的是MAC地址,所以不支持端口映射,且发送给Real Server的数据报文不能跨路由。

 

VS/TUN

基于IP隧道实现的虚拟服务器。

Director接收到客户请求之后,将根据调度方法选择合适的目标服务器,只不过这些目标服务器都不在同一网络中,可能是互联网的其他网络中的主机。使用IP隧道技术封装数据报文,也就是将报文封装在新的指向Real Server的IP报文中。这个IP报文层层路由后到达目标服务器后,目标服务器必须能够识别IP隧道封包并解开它,发现数据是发往自身具有的VIP的,就接收下来并直接响应客户端。

TUN模型,减轻了Director的压力,提高了负载均衡系统的吞吐量。

但相对于DR模型来说,其IP隧道开销较大,且广域网路由,在响应速度上可能要慢于DR模型。

所有通讯地址都要使用公网地址,这也是不小的成本。

 

VS/FULLNAT

NAT模型不太适合跨路由(笔者认为NAT可以在内网跨路由,但是内部路由配置较为复杂且不通用),这也限制其能够调度的服务器的个数,扩展能力有限。

FULLNAT将请求报文的源地址、源端口、目标地址、目标端口全部转换。是否端口映射视情况而定。

SNAT: CIP:CPORT-->DIP:DPORT

DNAT: VIP:VPORT-->RIP:RPORT

通过内网路由到目标服务器,目标服务器处理后将响应报文回传给Director。Director再做一次地址转换后,将数据报文发回客户端。

SNAT: RIP:RPORT-->VIP:VPORT

DNAT: DIP:DPORT-->CIP:CPORT

这种模型提高了系统的吞吐能力,使后端可以扩展更多服务器,同时允许内网拥有复杂拓扑。

但是Director的瓶颈问题没有解决。

FULLNAT已经由阿里巴巴开源,项目地址https://github.com/alibaba/LVS

 

(三)ARP相关内核参数

DR模型的实现,比较特殊,需要Real Server捆绑VIP,同时在网络中不可见,也就是说VIP要配置在Non-ARP网络设备上。

为了让VIP在网络不可见,可以采用以下方法:

arptables过滤arp协议相关的包

配置内核参数,阻止设备响应arp报文或者主动发布arp包。

 

在DR的配置中多采用第二种配置内核参数的方法。

/proc/sys/net/ipv4/conf参数

all 其参数值将应用到所有网络接口

default 用来作为缺省参数初始化一个网络接口的

eth0 这是个例子,要根据网络接口名称而定。定义某一个指定的网络接口参数。

 

下面以eth0来说明两个重要参数

arp_ignore

定义对目标地址为本机IP地址的ARP询问的不同应答方式。

0 - 任意网络接口收到请求,所有接口都应答

1 - 只在来访的网络接口上做出应答,但是要求目的IP配置在其上。

2 - 同1,进一步要求请求方的IP和目标IP必须属于同一子网。

3 - 如果目的IP的范围不是本地主机,才应答。

4-7 保留

8 不做任何应答

 

arp_announce

为网络接口上,发送ARP请求的IP包中的宣称的本地IP地址,定义不同程度的级别。

0 - 缺省,配置在任意接口上的任意本地地址。

1 - 如果可能,选择和目的地址位于同一子网内的地址。否则,使用级别2的结果。

2 - 优先使用主地址。

 

需要说明的是,如果在all下和eth0都定义相同的参数值,将取这个值的最大值。

在VS/DR模型中,需要配置这两个参数。

 

三、NAT模型实验

(一)规划

NAT实验规划

 

(二)测试页面test.php

<?php session_start(); ?>    
<html>     
<header><title><?php echo getenv('SERVER_ADDR') ?></title></header>     
<body>     
<?php     
        if (!isset( $_SESSION [ 'count' ])) {     
           $_SESSION [ 'count' ] =  0 ;     
        } else {     
           $_SESSION [ 'count' ]++;     
        }     
        printf("sessionid=%s",session_id());     
        printf("Client IP is &amp;lt;b>%s</b>:<b>%s</b>",getenv('REMOTE_ADDR'),getenv('REMOTE_PORT'));     
        printf("Server IP is <b>%s</b>:<b>%s</b>",getenv('SERVER_ADDR'),getenv('SERVER_PORT'));     
        printf("Filename=%s",getenv('SCRIPT_FILENAME'));     
        printf("Conn_Count=%d",$_SESSION [ 'count' ]);     
        phpinfo();     
?>     
</body>     
</html>

说明:

getenv('SERVER_ADDR'),getenv函数返回指定环境变量的值。

session_id()返回SessionID。

'REMOTE_ADDR'指的是取浏览器IP地址,'REMOTE_PORT'指的是取浏览器端端口。

'SERVER_ADDR'指的是取服务器IP地址,'SERVER_PORT'指的是取服务器端端口。

'SCRIPT_FILENAME'取请求的文件名称。

printf是格式化输出函数。

phpinfo函数返回当前php运行环境的所有信息。

$_SESSION [ 'count' ]定义一个会话变量count,每一次同一个会话访问就加1,以示区别。

 

(三)PHP-FPM构建

IP地址192.168.23.85

详细配置过程,请参看《LAMP的几种简单实现及drupal、WordPress、phpMyAdmin部署》。

# yum -y install gcc libxml2-devel bzip2-devel openssl-devel gd-devel    
[root@localhost php-5.6.0]# rpm -ivh ~/libmcrypt-2.5.8-9.el6.x86_64.rpm     
warning: /root/libmcrypt-2.5.8-9.el6.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEY     
Preparing...                ########################################### [100%]     
   1:libmcrypt              ########################################### [100%]     
[root@localhost php-5.6.0]# rpm -ivh ~/libmcrypt-devel-2.5.8-9.el6.x86_64.rpm     
warning: /root/libmcrypt-devel-2.5.8-9.el6.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEY     
Preparing...                ########################################### [100%]     
   1:libmcrypt-devel        ########################################### [100%]     
./configure --prefix=/usr/local/php54 --enable-fpm --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-libxml-dir --with-openssl --with-pcre-regex --with-zlib --with-bz2 --with-jpeg-dir --with-png-dir -with-freetype-dir --with-mcrypt --enable-sockets --with-mysql --with-mysqli --with-pdo-mysql --enable-mysqlnd --enable-mbstring --with-gd
 
[root@localhost php-5.6.0]# cp sapi/fpm/init.d.php-fpm /etc/rc.d/init.d/php-fpm    
[root@localhost php-5.6.0]# chmod +x /etc/rc.d/init.d/php-fpm     
[root@localhost php-5.6.0]# chkconfig --add php-fpm     
[root@localhost php-5.6.0]# chkconfig --list php-fpm     
php-fpm            0:off    1:off    2:on    3:on    4:on    5:on    6:off
 
[root@localhost php-5.6.0]# cp /usr/local/php54/etc/php-fpm.conf.default /usr/local/php54/etc/php-fpm.conf    
[root@localhost php-5.6.0]# vim /usr/local/php54/etc/php-fpm.conf     
pid = run/php-fpm.pid     
error_log = log/php-fpm.log
 
[www]    
listen = 9000
 
[root@localhost ~]# service php-fpm start    
Starting php-fpm  done     
[root@localhost ~]# ss -tnlp | grep 9000     
LISTEN  0 128  *:9000 *:*  users:(("php-fpm",1276,7),("php-fpm",1277,0),("php-fpm",1278,0))     
[root@localhost ~]# service php-fpm status     
php-fpm (pid 1276) is running...     
[root@localhost ~]# mkdir /web/

最后在创建的/web/目录下放置测试页面test.php

 

(四)WEB1和WEB2配置

因为最后实现的是PHP-FPM,需要支持FastCGI,因此需要2.4版本的httpd。以前的博文中都有,请查阅。

[root@localhost ~]# yum install gcc pcre-devel openssl-devel -y
 
[root@localhost ~]# tar xf apr-1.5.1.tar.bz2    
[root@localhost ~]# tar xf apr-util-1.5.3.tar.bz2     
[root@localhost ~]# tar xf httpd-2.4.10.tar.bz2     
[root@localhost ~]# cd apr-1.5.1
 
[root@localhost apr-1.5.1]# ./configure
[root@localhost apr-1.5.1]# make && make install
 
[root@localhost apr-1.5.1]# cd    
[root@localhost ~]# cd apr-util-1.5.3
 
[root@localhost apr-util-1.5.3]# ./configure --with-apr=/usr/local/apr/    
[root@localhost apr-util-1.5.3]# make && make install     
 
[root@localhost ~]# cd ~/httpd-2.4.10    
[root@localhost httpd-2.4.10]# ./configure --prefix=/usr/local/apache24 --sysconfdir=/etc/httpd24 --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-z --with-pcre --enable-mpms-shared=all --with-mpm=event --enable-modules=most
 
[root@localhost ~]# vim /etc/httpd24/httpd.conf
LoadModule proxy_module modules/mod_proxy.so   
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
 
DocumentRoot "/usr/local/apache24/htdocs"    
<Directory "/usr/local/apache24/htdocs">     
    Options None     
    AllowOverride None     
    Require all granted     
   
</Directory>
 
ProxyRequests Off    
ProxyPassMatch ^(/.*\.php)$ fcgi://192.168.23.85:9000/web$1
 
<IfModule dir_module>    
    DirectoryIndex index.php  index.html     
</IfModule>     
<IfModule mime_module>     
AddType application/x-httpd-php .php
 
# vim /etc/profile.d/httpd24.sh
export PATH=/usr/local/apache24/bin:$PATH
# source /etc/profile.d/httpd24.sh
 
# apachectl start

WEB1

修改监听的在TCP的8080端口

Listen 8080

# apachectl restart

# vim /usr/local/apache24/htdocs/index.html

<html><body><h1>192.168.23.80:8080</h1></body></html>

 

WEB2

# vim /usr/local/apache24/htdocs/index.html

<html><body><h1>192.168.23.81:80</h1></body></html>

 

(五)Director

部署ipvs

# yum -y install ipvsadm

 

1、RR调度

[root@localhost ~]# ipvsadm -A -t 172.16.23.80:80 -s rr    
[root@localhost ~]# ipvsadm -a -t 172.16.23.80:80 -r 192.168.23.80:8080 -m     
[root@localhost ~]# ipvsadm -a -t 172.16.23.80:80 -r 192.168.23.81:80 -m     
[root@localhost ~]# ipvsadm -L -n     
IP Virtual Server version 1.2.1 (size=4096)     
Prot LocalAddress:Port Scheduler Flags     
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn     
TCP  172.16.23.80:80 rr     
  -> 192.168.23.80:8080           Masq    1      0          0         
  -> 192.168.23.81:80             Masq    1      0          0 

测试一下效果:

nat1rr

可以看出在两台服务器间调度

 

2、WRR调度

更换调度算法,增加权重

[root@localhost ~]# ipvsadm -E -t 172.16.23.80:80 -s wrr
 
[root@localhost log]# ipvsadm -e -t 172.16.23.80:80 -r 192.168.23.80:8080 -m -w 1    
[root@localhost log]# ipvsadm -e -t 172.16.23.80:80 -r 192.168.23.81:80 -m -w 5     
[root@localhost log]# ipvsadm -L -n     
IP Virtual Server version 1.2.1 (size=4096)     
Prot LocalAddress:Port Scheduler Flags     
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn     
TCP  172.16.23.80:80 wrr     
  -> 192.168.23.80:8080           Masq    1      0          0         
  -> 192.168.23.81:80             Masq    5      0          0 
 
[root@localhost log]# ipvsadm -L -n -c    
IPVS connection entries     
pro expire state       source             virtual            destination     
TCP 14:57  ESTABLISHED 172.16.0.100:25799 172.16.23.80:80    192.168.23.80:8080     
TCP 14:57  ESTABLISHED 172.16.0.100:25798 172.16.23.80:80    192.168.23.81:80

 

从上面可以看到,确实调度器做到了负载均衡。来测试分析一下

nat2wrr动态网页

nat2wrr动态网页1

nat2wrr动态网页2

nat2wrr动态网页3

nat2wrr动态网页4

 

从以上的图可以看出,在一定范围内做到了按照权重分配。

注意Conn_Count值的变化,测试了10次,调度192.168.23.80:8080和192.168.23.81:80的次数比值是1:5。

 

3、SH调度

[root@localhost log]# ipvsadm -E -t 172.16.23.80:80 -s sh    
[root@localhost log]# ipvsadm -L -n     
IP Virtual Server version 1.2.1 (size=4096)     
Prot LocalAddress:Port Scheduler Flags     
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn     
TCP  172.16.23.80:80 sh     
  -> 192.168.23.80:8080           Masq    1      0          0         
  -> 192.168.23.81:80             Masq    5      0          0 

nat3sh1

由上图可以看出,在一段时间内,从同一客户端发起的所有连接被定向到了同一台服务器。

 

(六)NAT模型的特点

实现简单,有较好的负载均衡效果。

基于NAT可以实现目标地址、目标端口的转换。

所有数据进出都要经过Director这个咽喉要道,Director就成整个系统的瓶颈所在。

在Director上所有数据都要进行NAT转换,这限制了Director处理连接的能力,不适合高负载的场景使用。

 

 

四、DR模型

(一)规划图

DR实验规划

说明:

上图基本上模拟了公网访问内网WEB站点,使用LVS/DR来完成响应的实现规划。

首先在网络边界处,放置防火墙,并在防火墙上做DNAT。

192.168.23.0/24这个网络所有主机的默认网关都是指向192.168.23.1

图中的MAC地址,只是为了后面抓包的时候看着方便而标记在图上,实际实验中视自己网卡硬件地址而定。

有些配置都是直接使用命令即时生效的,如果需要永久生效,请写入配置文件,这些都很简单,参考文档非常多,不再赘述。

 

(二)WEB1和WEB2

注意下面几步骤都是在两台WEB服务器都做

1)调整内核参数

# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore    
# echo 2 > /proc/sys/net/ipv4/conf/all/arp_annunce     
# echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_ignore     
# echo 2 > /proc/sys/net/ipv4/conf/eth0/arp_annunce

上面配置的内核参数的意义是:

发起arp请求时只能从来访的接口上响应,内部发起的ARP请求的源地址也使用出口接口上的IP地址。

这样在LVS的时候,很好的屏蔽了内部接口上的IP地址,在外部不可见。

实际上配置all就可以了,但是怕接口上参数值有其他可能,所以显式的配置在接口上,例如eth0。如果是这样配置,干脆就直接使用后两句eth0的就可以了。

也可以写在配置文件中,永久生效。

# vim /etc/sysctl.conf    
net.ipv4.conf.all.arp_announce = 2     
net.ipv4.conf.all.arp_ignore = 1     
net.ipv4.conf.eth0.arp_announce = 2     
net.ipv4.conf.eth0.arp_ignore = 1     
# sysctl -p

 

2)配置VIP

# ip addr add 192.168.23.100/32 broadcast 192.168.23.100  dev lo    
# ip route add 192.168.23.100 dev lo

3)启动WEB服务

# apachectl start

默认页面还是VS/NAT实验中的index.html页面。

 

(三)调度服务器IP配置

使用接口eth0上配置2个IP

DIP,192.168.23.99/24,默认网关指向192.168.23.1

VIP,192.168.23.100/32

# ifconfig eth0:0 192.168.23.100 netmask 255.255.255.255 broadcast 192.168.23.100 up
 
# ipvsadm -A -t 192.168.23.100:80 -s rr    
# ipvsadm -a -t 192.168.23.100:80 -r 192.168.23.81:80 -g     
# ipvsadm -a -t 192.168.23.100:80 -r 192.168.23.80:80 -g
 
# ipvsadm -L -n    
IP Virtual Server version 1.2.1 (size=4096)     
Prot LocalAddress:Port Scheduler Flags     
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn     
TCP  192.168.23.100:80 rr     
  -> 192.168.23.80:80             Route   1      0          0         
  -> 192.168.23.81:80             Route   1      0          0

注:

这里使用rr算法,是因为后面抓包分析的方便,可以根据实际情况选择合适的算法。

Director上的VIP也可以使用24位掩码,不一定非要照着上面语句做。

一定要注意,不能把VIP放到主地址上,否则ICMP(ping)和ARP都会失败。因为这些报文中都有源地址且为VIP,Real Server收到后,认为是自己的VIP发的,Director就无法收到任何回应。

 

(四)防火墙DNAT

DNAT要指向VIP。

# iptables -t nat -A PREROUTING -d 172.16.23.80 -p tcp --dport 80 -j DNAT --to-destination 192.168.23.100
# ping 192.168.23.100
# arp -n    
Address                  HWtype  HWaddress           Flags Mask            Iface     
192.168.23.100           ether   00:0c:29:06:13:ea   C                     eth1     
192.168.23.99            ether   00:0c:29:06:13:ea   C                     eth1

看到上面VIP一定要和192.168.23.99指向同一个MAC,也就是要指向Director,否则就要追查问题。看看WEB1和WEB2配置VIP是否正确。

 

(五)测试

为了能看出区别,我们把WEB1的配置文件的参数修改如下:

DR模型不支持端口映射,所以修改 Listen 为80。

修改 ServerName www2.test.com:8888,注意这一句不会影响监听的80端口,只是影响服务器端变量。

dr-rr1

从图上看出,确实实现了rr的效果。注意端口号是8888的,不是真正监听在8888端口。它只是声称监听在8888端口而已。

 

(六)抓包分析

在防火墙的内网接口上eth1上抓包,因为这是必经之路。

抓包1

考虑到版面大小,图片被限制了宽度,可以自行放大,下面表格逐句分析。

序号 说明
1、2 ssh相关的,不再解释。从序号3开始
3 192.168.23.1收到了从外围接口上的请求后,要把数据包发往192.168.23.100,所以广播发起ARP请求,询问谁拥有VIP 192.168.23.100
4 Director回应了ARP请求,看MAC地址,这时候后几位是06:13:ea
5 防火墙DNAT后的数据包(第一次握手)发给Director。源地址是CIP,目标地址是VIP。源MAC是192.168.23.1的接口的,目标MAC是Director的。
6 Director使用调度算法挑选出Real Server后,修改目标MAC为这个RS的MAC。这里MAC是WEB1的。
7 WEB1发起对网关192.168.23.1的ARP请求
8 网关回应
9 WEB1把第二次握手的回应发给网关
10 网关把第三次握手发给Director。
11 Director把第三次握手发给WEB1的
12 http请求终于来了。依然先到Director。
13 修改MAC转发WEB1
14 把对12回应送给网关
15 发回http报文包含网页内容给网关
16 发送ACK回应给Director
17 转发给WEB1
18 新的http连接的握手发给Director
19 Director修改为WEB2的MAC
20 WEB2广播ARP请求,询问网关192.168.23.1的MAC地址。以下的过程和WEB1相同。

以上的分析很好说明了RR调度算法,可以清楚的看到整个DR模型的工作流程。

 

 

五、动静分离的discuz的部署

在VS/DR实验的基础上,做一些扩展

DR实验规划2

 

(一)数据库服务器

使用MariaDB 5.5,使用二进制发布版本,配置简单即可使用。具体安装方法参看以前的博文。

进入管理界面后,创建数据库bbs,并授权。

> create database bbs;  
> grant all on bbs.* to bbs@'192.168.23.%' identified by 'bbs';

 

(二)部署discuz

1、PHP-FPM上部署discuz

# unzip -q Discuz_X3.2_SC_UTF8.zip   
# cp -r ~/upload/* /web/    
# chmod 777 -R /web/*

 

2、在WEB1和WEB2上部署discuz

因为discuz中有一些静态资源,而非*.php的资源都要在WEB服务器上访问,因此也要在WEB服务器上部署discuz。但是由于不是非常的了解discuz的动静资源的详细路径,最简单的方式就是把upload目录下的所有文件复制到WEB1和WEB2的站点根目录下

# unzip -q Discuz_X3.2_SC_UTF8.zip  
# cp -r ~/upload/* /web/

顺便说一句,请把WEB1上的配置文件的内容修改回来。

ServerName www2.test.com:80

 

3、安装discuz

安装过程,如下:

discuz 01

discuz 02

discuz 03

discuz 04

discuz 05

 

安装完毕后,运行后,发现论坛首页样式错乱

discuz 10

这是怎么回事呢?

其实还是动静分离的问题,在安装论坛的工程中,应该是生成了一些css和图片的新的文件或者路径,而WEB1和WEB2没有。解决的办法很简单。把php-fpm上的文件tar后,在WEB1和WEB2上重新部署就行了。

[root@www1 ~]# cd /usr/local/apache24/htdocs/   
[root@www1 htdocs]# rm -rf *    
[root@www2 ~]# cd /usr/local/apache24/htdocs/    
[root@www2 htdocs]# rm -rf *
 
[root@localhost ~]# cd /web   
[root@localhost web]# tar jcf web.tar.bz2 *    
[root@localhost web]# scp web.tar.bz2 root@192.168.23.80:/usr/local/apache24/htdocs/    
[root@localhost web]# scp web.tar.bz2 root@192.168.23.81:/usr/local/apache24/htdocs/
 
[root@www1 htdocs]# tar xf web.tar.bz2    
[root@www1 htdocs]# rm -f web.tar.bz2    
[root@www2 htdocs]# tar xf web.tar.bz2     
[root@www2 htdocs]# rm -f web.tar.bz2

再次运行,首页正常表现

discuz 11

 

(三)共享存储

1、discuz的配置

discuz 20

进入后台管理页面,选择全局/上传设置

discuz 21

也就是说,data/attachment目录要作为共享的存储的挂载点。看看这个目录下面有什么?

discuz 22

这些文件和目录都是论坛程序需要的,怎么办?还是把attachment目录下所有的内容归档tar,然后挂载nfs目录,然后使用tar解压到这个目录中。

 

2、NFS服务器配置

# yum install nfs-utils –y
 
# useradd nfs -u 600   
# service nfs start     
Starting NFS services:      [  OK  ]     
Starting NFS mountd:        [  OK  ]     
Starting NFS daemon:        [  OK  ]     
Starting RPC idmapd:        [  OK  ]     
# chkconfig nfs on     
# chkconfig --list nfs     
nfs   0:off    1:off    2:on    3:on    4:on    5:on    6:off     
# mkdir /sharedir/
 
# vim /etc/exports    
/sharedir    192.168.23.0/24(rw,all_squash,anonuid=600,anongid=600)
 
# showmount -e 192.168.23.134    
Export list for 192.168.23.134:     
/sharedir 192.168.23.0/24
 
# chmod 777 /sharedir/

为了简单起见,没有做过多的限制,如果需要压缩所有用户权限,可以参看以前的博文。

注意:

挂载nfs目录的各个服务器要安装# yum -y install nfs-utils

 

3、PHP-FPM上配置nfs目录

[root@localhost web]# cd data/attachment/   
[root@localhost attachment]# tar jcf attachment.tar.bz2 *    
[root@localhost attachment]# mv attachment.tar.bz2 /tmp    
[root@localhost attachment]# cd ..    
[root@localhost data]# mount -t nfs 192.168.23.134:/sharedir attachment/    
[root@localhost data]# cd attachment/    
[root@localhost attachment]# mv /tmp/attachment.tar.bz2 ./  

把文件和目录的归档文件,放到nfs服务器上

 

4、nfs服务器

还原文件和目录,并重新设置属主、属组。

# cd /sharedir/
# tar xf attachment.tar.bz2
# chown -R nfs:nfs /sharedir

 

5、WEB1和WEB2配置nfs目录

如法炮制,只是mount就行了。

[root@www1 attachment]# cd ..   
[root@www1 data]# mount -t nfs 192.168.23.134:/sharedir attachment/    
[root@www2 attachment]# cd ..    
[root@www2 data]# mount -t nfs 192.168.23.134:/sharedir attachment/    
[root@www2 data]# ls attachment/    
album  attachment.tar.bz2  category  common  forum  group  index.htm  portal  profile  swfupload  temp

注意attachment.tar.bz2这个文件,这个是192.168.23.134:/sharedir目录下的,说明挂载成功了。

在nfs服务器上把attachment.tar.bz2文件挪个位置# mv attachment.tar.bz2 /tmp/

 

(四)测试

discuz 31 发图片帖子

图片上传成功,提交帖子

discuz 32 看帖子

帖子浏览成功,多种方式、多次浏览帖子都没有问题。

 

至此,动静分离且使用同一数据库和NFS共享目录的网站基本搭建完成。

 

 

六、总结

本文参照LVS的源码,简单描述了LVS的调度算法。

采用LVS的NAT、DR模型分别作了简单的实验。对于NAT模型,构建了一个特殊的PHP测试页,可以较为清楚看到动静分离后调度的执行效果。对DR模型作了抓包分析,尝试从更加深入的层次理解DR模型的报文走向。

最后,安装配置discuz,看看一个真正的实用WEB程序在这种DR负载均衡下如果实现动静分离的。

本文内容较多,涉及概念非常多,可以参看笔者以前的博文。

 

本文只是简单的实验分析,所以很多服务器存在单点问题,这个可以通过扩展实现高可用。而且整个实验中, 最大的问题是均衡器并不知道后端服务器的健康状况,这需要借助其他的工具来监测联动控制,详情后述。

 

参考资料

http://zh.linuxvirtualserver.org/node/25

http://www.linuxvirtualserver.org/software/ipvs.html#kernel-2.6

http://www.linuxinsight.com/proc_sys_net_ipv4_conf.html

http://kb.linuxvirtualserver.org/wiki/Using_arp_announce/arp_ignore_to_disable_ARP