LVS实战

LVS基本简介

LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(real server)上。为了避免不同机器上用户请求得到的数据不一样,需要用到了共享存储,这样保证所有用户请求的数据是一样的。

LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器。这是一个由章文嵩博士发起的一个开源项目,它的官方网站是 http://www.linuxvirtualserver.org 现在 LVS 已经是 Linux 内核标准的一部分。使用 LVS 可以达到的技术目标是:通过 LVS 达到的负载均衡技术和 Linux 操作系统实现一个高性能高可用的 Linux 服务器集群,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的性能。LVS 是一个实现负载均衡集群的开源软件项目,LVS架构从逻辑上可分为调度层、Server集群层和共享存储。

工作原理:

LVS工作于OSI四层,根据请求报文的目标IP和目标PORT将其转发至后端主机集群中的某台服务器(根据调度算法);其原理是通过ipvsadm用户空间的命令行工具定义lvs规则控制工作于内核上的netfilter INPUT钩子之上的程序代码IPVS实现调度算法。


1. 首先用户向负载均衡器调度器(Director Server)发起请求,负载均衡器将请求发往至内核空间,交给内核模块进行检测。
2. 内核模块中的PREROUTING链首先会收到用户请求,判断目标地址是否是负载均衡器的IP地址,如果是,则将数据包发往INPUT链。
3. IPVS模块是工作在INPUT链上的,当用户请求到达INPUT链上时,IPVS会将用户请求和自己已定义好的集群服务作对比,如果用户请求的就是定义的集群服务,那么IPVA会强行修改数据包里的目标IP地址和目标端口,并将新的数据包发往POSTROUTING链。
4. POSTROUTING链接收到数据包发现目标IP地址刚好是自己的后端的服务器,那么通过选路,将数据包最终发送给后端的服务器。

LVS的组成

LVS 由2部分程序组成,包括 ipvs 和 ipvsadm。

  1. ipvs(ip virtual server):一段代码工作在内核空间,叫ipvs,是真正生效实现调度的代码。
  2. ipvsadm:另外一段是工作在用户空间,叫ipvsadm,负责为ipvs内核框架编写规则,定义谁是集群服务,而谁是后端真实的服务器(Real Server)

LVS相关术语

  1. DS:Director Server。指的是前端负载均衡器节点。
  2. RS:Real Server。后端真实的工作服务器。
  3. VIP:向外部直接面向用户请求,作为用户请求的目标的IP地址。
  4. DIP:Director Server IP,主要用于和内部主机通讯的IP地址。
  5. RIP:Real Server IP,后端服务器的IP地址。
  6. CIP:Client IP,访问客户端的IP地址。

LVS的调度算法

静态方法:仅根据算法本身进行调度

  1. 轮叫调度 rr
    这种算法是最简单的,就是按依次循环的方式将请求调度到不同的服务器上,该算法最大的特点就是简单。轮询算法假设所有的服务器处理请求的能力都是一样的,调度器会将所有的请求平均分配给每个真实服务器,不管后端 RS 配置和处理能力,非常均衡地分发下去。
  2. 加权轮叫 wrr
    这种算法比 rr 的算法多了一个权重的概念,可以给 RS 设置权重,权重越高,那么分发的请求数越多,权重的取值范围 0 – 100。主要是对rr算法的一种优化和补充, LVS 会考虑每台服务器的性能,并给每台服务器添加要给权值,如果服务器A的权值为1,服务器B的权值为2,则调度到服务器B的请求会是服务器A的2倍。权值越高的服务器,处理的请求越多。
  3. 源地址散列调度算法 sh
    与目标地址散列调度算法类似,但它是根据源地址散列算法进行静态分配固定的服务器资源。
  4. 目标地址散列调度算法 dh
    该算法是根据目标 IP 地址通过散列函数将目标 IP 与服务器建立映射关系,出现服务器不可用或负载过高的情况下,发往该目标 IP 的请求会固定发给该服务器。

动态方法:根据算法及各RS当前的负载状态进行评估,算法最后体现为overhead的值。

  1. 最少链接 lc
    这个算法会根据后端 RS 的连接数来决定把请求分发给谁,比如 RS1 连接数比 RS2 连接数少,那么请求就优先发给 RS1
  2. 加权最少链接 wlc
    这个算法比 lc 多了一个权重的概念,适用于后端RS性能不一样的时候,最为通用且为默认的动态方法
  3. 基于局部性的最少连接调度算法 lblc
    这个算法是请求数据包的目标 IP 地址的一种调度算法,该算法先根据请求的目标 IP 地址寻找最近的该目标 IP 地址所有使用的服务器,如果这台服务器依然可用,并且有能力处理该请求,调度器会尽量选择相同的服务器,否则会继续选择其它可行的服务器
  4. 复杂的基于局部性最少的连接算法 lblcr
    记录的不是要给目标 IP 与一台服务器之间的连接记录,它会维护一个目标 IP 到一组服务器之间的映射关系,防止单点服务器负载过高。
  5. SED:Shortest Expection Delay
    权重大的RS如果排在后面的话,请求连接会被排在其前面权重小的机器所处理,导致排在后面权重大的RS闲置。
  6. NQ: Nerver Queue
    累死权重大的RS,SED算法的改进。

好了,说着这么多理论,毛主席说实践是检验理论的唯一标准^_^

实战-搭建一套LVS-DR模型的高性能集群,并实现以下功能:

(1)、wordpress程序通过nfs共享给各个Realserver;

(2)、后端realserver中的nginx和php分离

软件环境:
1、vmware workstation 14 PRO,
2、lnmp一键安装脚本
3、CentOS7.4+Nginx1.12+WordPress4.9.1+PHP-7.2+MariaDB10.2

拓扑图

IP分配

LVS-DR1
DIP:192.168.0.249
VIP:192.168.0.228

NGINX主机1
RIP:192.168.0.247
VIP:192.168.0.228

NGINX主机2
RIP:192.168.0.246
VIP:192.168.0.228

PHP主机1
192.168.0.233

PHP主机2
192.168.0.232

NFS主机
192.168.0.231

MariaDB主机1
192.168.0.251

配置MariaDB主机1

1
2
3
4
5
6
7
8
9
10
登录MariaDB,建立数据库。

MariaDB [(none)]> create database wordpress;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> grant all on wordpress.* to 'wp'@'192.168.%' identified by '123456';
Query OK, 0 rows affected (0.01 sec)

MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

安装配置NFS主机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#安装NFS组件
yum install nfs-utils rpcbind

#设置RPC、NFS开机启动
systemctl enable rpcbind.service
systemctl enable nfs.service

#启动nfs
systemctl start rpcbind.service
systemctl start nfs.service

#建立WordPress在NFS主机上的存放目录
mkdir /data

#下载WordPress
cd /data
wget https://wordpress.org/latest.tar.gz

#解压
tar xvf latest.tar.gz

#配置WordPress
cd wordpress
cp wp-config-sample.php wp-config.php
vim wp-config.php

#修改wp-config.php内容如下
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wp');

/** MySQL database password */
define('DB_PASSWORD', '123456');

/** MySQL hostname */
define('DB_HOST', '192.168.0.251');

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');

#如下配置nfs
[root@NFS ~]# cat /etc/exports
/data/wordpress/ 192.168.0.0/24(rw,sync,no_wdelay,no_root_squash)
之后记得重启一下nfs服务

来来来,nfs的权限设置科普一下

rw :读写;

ro :只读;

sync :同步模式,内存中数据时时写入磁盘;

async :不同步,把内存中数据定期写入磁盘中;

no_root_squash :加上这个选项后,root用户就会对共享的目录拥有至高的权限控制,就像是对本机的目录操作一样。不安全,不建议使用;

root_squash :和上面的选项对应,root用户对共享目录的权限不高,只有普通用户的权限,即限制了root;

all_squash :不管使用NFS的用户是谁,他的身份都会被限定成为一个指定的普通用户身份;

anonuid/anongid :要和root_squash 以及 all_squash一同使用,用于指定使用NFS的用户限定后的uid和gid,前提是本机的/etc/passwd中存在这个uid和gid。

在2台PHP-FPM主机及NGINX主机上挂载NFS主机上的WordPress共享目录

1
2
3
4
5
6
[root@PHP1 opt]# mkdir -pv /opt/wordpress
mkdir: created directory ‘/opt/wordpress’
mount -t nfs 192.168.0.231:/data/wordpress /opt/wordpress

也可以在主机上/etc/fstab文件中加入如下内容,实现开机自启:
192.168.0.231:/data/wordpress /opt/wordpress nfs defaults 0 0

在两台NGINX主机上编辑nginx.conf文件,支持PHP-FPM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
upstream phpserver {
server 192.168.0.233:9000 weight=1; #权重可以根据实际情况调整。
server 192.168.0.232:9000 weight=1;
}

server {
listen 80;
server_name localhost;

location / {
root /opt/wordpress;
index index.html index.htm index.php;
}


location ~ \.php$ {
root /opt/wordpress; #此路径为php机器上的路径,非nginx机器上的。
fastcgi_pass phpserver;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

在两台PHP-FPM主机上编辑PHP-FPM.conf文件

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;

[www]
listen = 192.168.0.233:9000
listen.backlog = -1
listen.allowed_clients = 192.168.0.247,192.168.0.246
;listen.owner = www
;listen.group = www
;listen.mode = 0666
user = www
group = www

pm = dynamic
pm.max_children = 33
pm.start_servers = 22
pm.min_spare_servers = 16
pm.max_spare_servers = 33
pm.max_requests = 2048
pm.process_idle_timeout = 10s
request_terminate_timeout = 120
request_slowlog_timeout = 0

pm.status_path = /php-fpm_status
slowlog = log/slow.log
rlimit_files = 51200
rlimit_core = 0

catch_workers_output = yes
;env[HOSTNAME] = PHP2
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

php文件存放的地方,要确保php配置文件中配置的user要有读权限,这里用的是www用户。

好了,现在分别在浏览器里面输入192.168.0.246 192.168.0.247 出来以下画面后,就可以开始配置lvs了

好,下面开始配置大名鼎鼎的lvs,上古神器~~

首先在LVS的机器上,查看是否支持IPVS

来一段风骚的命令

1
grep -C 10 -i "ipvs" /boot/config-$(uname -r)

参数简介:
-i:ignorecase,忽略字符的大小写;
-A #:after, 后#行
-B #:before,前#行
-C #:context,前后各#行

可以看到如下图,基本上都是默认支持滴

配置lvs的Director

1、先来系统层面的配置

#先打开系统的路由转发
[root@LVS-DR1 ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
[root@LVS-DR1 ~]# cat /proc/sys/net/ipv4/ip_forward
1
1
2
3
#系统的路由转发永久生效方法
[root@LVS-DR1 ~]#echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
[root@LVS-DR1 ~]#sysctl -p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#配置虚拟网卡
[root@LVS-DR1 ~]# ifconfig ens32:0 192.168.0.228 netmask 255.255.255.0 broadcast 192.168.0.228 up
[root@LVS-DR1 ~]# ifconfig
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.249 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::20c:29ff:fe13:618e prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:13:61:8e txqueuelen 1000 (Ethernet)
RX packets 11013 bytes 726061 (709.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 724 bytes 86827 (84.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

ens32:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.228 netmask 255.255.255.0 broadcast 192.168.0.228
ether 00:0c:29:13:61:8e txqueuelen 1000 (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1 (Local Loopback)
RX packets 72 bytes 5720 (5.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 72 bytes 5720 (5.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

2、 定义集群服务及服务上的RS

1
2
3
4
5
6
7
8
9
10
[root@LVS-DR1 ~]# ipvsadm -A -t 192.168.0.228:80 -s wlc
[root@LVS-DR1 ~]# ipvsadm -a -t 192.168.0.228:80 -r 192.168.0.247:80 -g -w 1
[root@LVS-DR1 ~]# ipvsadm -a -t 192.168.0.228:80 -r 192.168.0.246:80 -g -w 1
[root@LVS-DR1 ~]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.0.228:80 wlc
-> 192.168.0.246:80 Route 1 0 0
-> 192.168.0.247:80 Route 1 0 0

配置两台RealServer

1
2
3
4
5
6
7
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/$interface/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/$interface/arp_announce

ifconfig $interface_alias $vip broadcast $vip netmask 255.255.255.255 up
route add -host $vip dev $interface_alias

其实用脚本更科学

Director脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/bash

#定义参数,vip、rip、权重、端口、调度算法,lvs类型等
vip=192.168.0.228
rip=('192.168.0.246' '192.168.0.247')
weight=('1' '2')
port=80
scheduler=rr
ipvstype='-g'
interface_alias='ens32:0'

case $1 in
start)
#清空Linux防火墙的规则
iptables -F -t filter
#清空lvs规则
ipvsadm -C
ifconfig $interface_alias $vip broadcast $vip netmask 255.255.255.255 up
echo 1 > /proc/sys/net/ipv4/ip_forward
ipvsadm -A -t $vip:$port -s $scheduler
[ $? -eq 0 ] && echo "ipvs service $vip:$port added." || exit 2

for i in `seq 0 $[${#rip[@]}-1]`; do
ipvsadm -a -t $vip:$port -r ${rip[$i]} $ipvstype -w ${weight[$i]}
[ $? -eq 0 ] && echo "RS ${rip[$i]} added."
done

touch /var/lock/subsys/ipvs
;;

stop)
echo 0 > /proc/sys/net/ipv4/ip_forward
ipvsadm -C
ifconfig $interface_alias down
rm -f /var/lock/subsys/ipvs
echo "ipvs stopped."
;;

status)
if [ -f /var/lock/subsys/ipvs ]; then
echo "ipvs is running."
ipvsadm -L -n
else
echo "ipvs is stopped."
fi
;;

*)
echo "Usage: `basename $0` {start|stop|status}"
exit 3
;;
esac

RS的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
#
vip=192.168.0.228
interface_alias="lo:0"
interface="lo"

case $1 in
start)
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/$interface/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/$interface/arp_announce

ifconfig $interface_alias $vip broadcast $vip netmask 255.255.255.255 up
route add -host $vip dev $interface_alias
;;

stop)
echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/$interface/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/$interface/arp_announce
ifconfig $interface_alias down
;;

status)
if ifconfig lo:0 |grep $vip &> /dev/null; then
echo "ipvs is running."
else
echo "ipvs is stopped."
fi
;;

*)
echo "Usage: `basename $0` {start|stop|status}"
exit 1
esac

做完以上工作,lvs就可以正常访问了,可以看到lvs也起作用了,上图。

好,大功告成。

0%