在前两篇文章中分别介绍了LVS中常用的三种模型, 然后介绍了LVS中的功能配置.
本文在此基础之上, 基于Docker进行LVS中DR模型的实战;
LVS中DR模型实战 前言 在本次实战中, 采用CentOS:7镜像创建三个Docker容器实现:
其中一个Docker容器作为LVS负载均衡服务器, 另外两个容器作为RS.
注意: 一定要选择好CentOS的镜像
在DockerHub中有些CentOS镜像内核中不包括ip_vs, 这将导致ipvsadm命令无法使用
此外, 为了在Docker中使用ipvsadm命令, 在宿主系统中也需要安装ipvsadm, 并开启服务!(本机使用的是Ubuntu18.04.LTS)
所以需要提前在本机中安装ipvsadm, 并启动服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ lsmod | grep ip_vs $ sudo apt install ipvsadm $ sudo ipvsadm IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn $ lsmod | grep ip_vs ip_vs 151552 0 nf_conntrack 139264 6 xt_conntrack,nf_nat,xt_nat,nf_conntrack_netlink,xt_MASQUERADE,ip_vs nf_defrag_ipv6 24576 2 nf_conntrack,ip_vs libcrc32c 16384 3 nf_conntrack,nf_nat,ip_vs
本机配置完成
创建子网络 使用下述命令创建一个叫做lvs_dr的子网络:
1 docker network create --subnet=172.20.1.0/16 lvs_dr
网络规划为(在下方配置):
VIP地址: 172.20.1.100
LVS的IP地址: 172.20.1.1
两台RS的IP地址: 172.20.1.10和172.20.1.11
创建容器 然后在子网络中分别创建lvs, rs1, rs2三个子容器并启动:
1 2 3 docker run -d --privileged=true --name=lvs --net lvs_dr --ip 172.20.1.1 centos:7 /usr/sbin/init docker run -d --privileged=true --name=rs1 --net lvs_dr --ip 172.20.1.10 centos:7 /usr/sbin/init docker run -d --privileged=true --name=rs2 --net lvs_dr --ip 172.20.1.11 centos:7 /usr/sbin/init
注: --privileged=true参数非常关键, 在下面的操作中会使用真正的root权限来修改内核配置
并且在和容器进行交互时也推荐加入--privileged=true参数, 例如:
1 >docker exec -it --privileged lvs /bin/bash
注2: /usr/sbin/init用于初始化内核进程
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@39673a2ec2bf /]# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:14:01:01 inet addr:172.20.1.1 Bcast:172.20.255.255 Mask:255.255.0.0 ...... [root@a4e096143dec /]# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:14:01:0A inet addr:172.20.1.10 Bcast:172.20.255.255 Mask:255.255.0.0 ...... [root@df7a0b42700e /]# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:14:01:0B inet addr:172.20.1.11 Bcast:172.20.255.255 Mask:255.255.0.0 ......
注: 由前分析可知, DR模型中LVS和RS必须在同一个网段
配置lvs的VIP 即配置客户端请求时的IP地址;
可以使用ifconfig命令配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@39673a2ec2bf /]# ifconfig eth0:1 172.20.1.100/16 [root@39673a2ec2bf /]# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:14:01:01 inet addr:172.20.1.1 Bcast:172.20.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:69219 errors:0 dropped:0 overruns:0 frame:0 TX packets:36178 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:86198035 (82.2 MiB) TX bytes:2403434 (2.2 MiB) eth0:1 Link encap:Ethernet HWaddr 02:42:AC:14:01:01 inet addr:172.20.1.100 Bcast:172.20.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:82 errors:0 dropped:0 overruns:0 frame:0 TX packets:82 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:7747 (7.5 KiB) TX bytes:7747 (7.5 KiB)
上面的配置(临时配置 )在eth0的子接口eth0:1配置了172.20.1.100\16
172.20.1.100\16等价于172.20.1.100 netmask 255.255.0.0
需要注意的是, docker限制容器进程配置自己的网络
在Docker0.6版本之后,privileged被引入docker
使用该参数,container内的root才拥有真正的root权. 否则,container内的root只是外部的一个普通用户权限
使用privileged启动的容器,可以看到很多host上的设备,并且可以执行mount, 甚至允许你在docker容器中启动docker容器
所以在使用更改上述配置时, 使用--privileged=true获取真正的root权限:
1 docker exec -it --privileged=true lvs /bin/bash
否则在使用ifconfig修改ip时可能会报错:
1 2 3 4 5 SIOCSIFADDR: Operation not permitted SIOCSIFNETMASK: Operation not permitted SIOCSIFBROADCAST: Operation not permitted SIOCSIFFLAGS: Operation not permitted SIOCSIFFLAGS: Operation not permitted
配置LVS地址转发 使用下面的命令配置(临时配置 )LVS的地址转发功能(启用IP路由转发功能):
1 2 3 4 5 [root@39673a2ec2bf /]# echo 1 > /proc/sys/net/ipv4/ip_forward [root@39673a2ec2bf ~]# sysctl -a | grep ip_forward net.ipv4.ip_forward = 1 net.ipv4.ip_forward_update_priority = 1 net.ipv4.ip_forward_use_pmtu = 0
操作系统默认, 收到一个包, 这个包的目标地址不是本机则丢弃
注: 内核文件只能用echo来直接覆盖文件, 而不能使用类似vi来修改;
因为vi修改会创建一个临时文件, 这是不变允许的!
配置RS的响应和通告级别 需要注意的是: 必须先配置RS的响应和通告级别然后再去配置VIP
否则, 先配置的VIP会直接被广播. 就失去了对外隐藏, 对内可见的性质!
下面以rs1为例配置arp_ignore和arp_announce文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@b2f5ab1b8b7d conf]# cd /proc/sys/net/ipv4/conf/ [root@b2f5ab1b8b7d conf]# ll total 0 dr-xr-xr-x 1 root root 0 Apr 10 08:03 all dr-xr-xr-x 1 root root 0 Apr 10 08:03 default dr-xr-xr-x 1 root root 0 Apr 10 08:03 eth0 dr-xr-xr-x 1 root root 0 Apr 10 08:03 lo [root@b2f5ab1b8b7d conf]# cd eth0/ [root@b2f5ab1b8b7d eth0]# echo 1 > arp_ignore [root@b2f5ab1b8b7d eth0]# echo 2 > arp_announce [root@b2f5ab1b8b7d eth0]# cat arp_ignore 1 [root@b2f5ab1b8b7d eth0]# cat arp_announce 2
上面修改了ech0目录下的配置信息
注: 在启动容器和交互时必须增加--privileged=true参数, 否则文件将为只读, 无法修改
如果后续还要添加新的机器, 可以通过修改all目录下的配置信息
1 2 3 [root@b2f5ab1b8b7d eth0]# cd ../all/ [root@b2f5ab1b8b7d all]# echo 1 > arp_ignore [root@b2f5ab1b8b7d all]# echo 2 > arp_announce
配置RS的VIP 这里继续以rs1为例对VIP(172.20.1.100)进行配置;
使用ifconfig配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@b2f5ab1b8b7d all]# ifconfig lo:1 172.20.1.100 netmask 255.255.255.255 [root@b2f5ab1b8b7d all]# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:14:01:0A inet addr:172.20.1.10 Bcast:172.20.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:32 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:4578 (4.4 KiB) TX bytes:0 (0.0 b) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) lo:1 Link encap:Local Loopback inet addr:172.20.1.100 Mask:255.255.255.255 UP LOOPBACK RUNNING MTU:65536 Metric:1
注: 将掩码地址配置为四个255
原因是如果将掩码配置为255.255.255.0, 则可以通过eth0和lo:1两个接口做请求
如:
1 2 3 4 5 [root@b2f5ab1b8b7d all]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.20.0.1 0.0.0.0 UG 0 0 0 eth0 172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
而lo离内核更近, 所以所有包会经由lo走;
此时RS的包会自己发送给自己(环回接口)
在两个RS上启动HTTPD服务 两台RS都经过了上述配置之后, 安装httpd服务;
然后在/var/www/html目录下创建一个主页分别表示当前RS的主页
为了显示负载均衡的效果, 两个RS的内容不同. 而在实际生成环境, 应当部署相同的Server镜像
对于rs1:
1 2 3 4 [root@b2f5ab1b8b7d all]# cd /var/www/html/ [root@b2f5ab1b8b7d html]# vi index.html [root@b2f5ab1b8b7d html]# cat index.html from rs1 server...
对于rs2:
1 2 3 [root@953d5b5e1201 /]# vi /var/www/html/index.html [root@953d5b5e1201 /]# cat /var/www/html/index.html from rs2 server...
然后启动两个服务:
1 2 3 4 5 6 [root@88528e94e720 /]# systemctl start httpd [root@b2f5ab1b8b7d html]# service httpd start Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, using 172.20.1.10 for ServerName [ OK ]
注意: 这一步要求在启动容器时使用了/usr/sbin/init初始化内核
此时可以访问172.20.1.10和172.20.1.11, 会分别显示:
1 2 3 4 5 from rs1 server... from rs2 server...
但此时访问# http://172.20.1.100/会访问失败, 因为LVS还尚未配置;
配置LVS 首先安装ipvsadm:
1 [root@06f8e77f3a22 /]# yum install -y ipvsadm
然后配置监控包和负载包(分别使用-A和-a):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@4229bb8082ee /]# ipvsadm -A -t 172.20.1.100:80 -s rr [root@4229bb8082ee /]# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 172.20.1.100:80 rr [root@4229bb8082ee /]# ipvsadm -a -t 172.20.1.100:80 -r 172.20.1.10 -g [root@4229bb8082ee /]# ipvsadm -a -t 172.20.1.100:80 -r 172.20.1.11 -g [root@4229bb8082ee /]# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 172.20.1.100:80 rr -> 172.20.1.10:80 Route 1 0 0 -> 172.20.1.11:80 Route 1 0 0
配置完成后即时生效;
此时访问172.20.1.100, 则会在浏览器轮流显示:
1 2 3 from rs1 server... from rs2 server...
要使用ctrl + F5强制刷新, 否则看到的是缓存的内容!
说明配置成功!
分析 查看LVS中TCP连接状况 多次访问172.20.1.100之后, 在了lvs容器中使用netstat命令:
1 2 3 4 5 [root@4229bb8082ee /]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.11:32793 0.0.0.0:* LISTEN -
结果发现并没有对应tcp连接
这也说明了LVS工作在四层, 只做请求的转发, 而不建立三次握手连接
查看RS中的TCP连接状况 而查看RS中的TCP连接状况:
1 2 3 4 5 [root@88528e94e720 /]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1258/httpd tcp 0 0 127.0.0.11:35389 0.0.0.0:* LISTEN -
发现RS中存在TCP连接, 说明符合DR模型;
查看LVS记录 通过ipvsadm -lnc可以查看LVS中的记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ipvsadm -lnc [root@4229bb8082ee /]# ipvsadm -lnc IPVS connection entries pro expire state source virtual destination TCP 01:35 FIN_WAIT 172.20.0.1:47562 172.20.1.100:80 172.20.1.11:80 TCP 01:35 FIN_WAIT 172.20.0.1:47556 172.20.1.100:80 172.20.1.10:80 TCP 01:35 FIN_WAIT 172.20.0.1:47566 172.20.1.100:80 172.20.1.11:80 TCP 01:35 FIN_WAIT 172.20.0.1:47560 172.20.1.100:80 172.20.1.10:80 TCP 01:35 FIN_WAIT 172.20.0.1:47552 172.20.1.100:80 172.20.1.10:80 TCP 01:35 FIN_WAIT 172.20.0.1:47564 172.20.1.100:80 172.20.1.10:80 TCP 01:35 FIN_WAIT 172.20.0.1:47558 172.20.1.100:80 172.20.1.11:80 TCP 01:35 FIN_WAIT 172.20.0.1:47554 172.20.1.100:80 172.20.1.11:80 TCP 01:35 FIN_WAIT 172.20.0.1:47568 172.20.1.100:80 172.20.1.10:80 TCP 01:40 FIN_WAIT 172.20.0.1:47570 172.20.1.100:80 172.20.1.11:80
而LVS就是通过这个记录实现动态负载均衡的
附录 文章参考:
如果觉得文章写的不错, 可以关注微信公众号: Coder张小凯
内容和博客同步更新~