LVS四层负载均衡

四种模式

1.NAT模式
2.TUN隧道模式
3.DR直接路由,推荐使用
4.FULLNAT,完全NAT

LVS负载均衡实践

环境准备
1.MySQL负载均衡
外部IP地址    内部IP地址  角色  备注
L4:
192.168.238.15    172.16.1.15 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
192.168.238.16    172.16.1.16 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
192.168.238.51    172.16.1.51 RS1(真实服务器)  MySQL
192.168.238.52    172.16.1.52 RS2(真实服务器)  MySQL

#L4+L7+WEB 大规模web负载均衡
L4:
192.168.238.15    172.16.1.15 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
192.168.238.16    172.16.1.16 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
L7:
192.168.238.5    172.16.1.5  LVS调度器(Director)    
192.168.238.6    172.16.1.6  LVS调度器(Director)    
192.168.238.7    172.16.1.7  RS1(真实服务器)  web01
192.168.238.8    172.16.1.8  RS2(真实服务器)  web02

LVS负载均衡安装

#在lb4-01、lb4-02上安装LVS
[root@lb4-01 ~]# yum install ipvsadm -y
[root@lb4-01 ~]# rpm -qa ipvsadm
ipvsadm-1.27-8.el7.x86_64

[root@lb4-01 ~]# modprobe ip_vs        #把ipvs加入到内核
[root@lb4-01 ~]# lsmod|grep ip_vs    #检查内核是否有ip_vs
ip_vs                 145458  0 
nf_conntrack          143360  1 ip_vs
libcrc32c              12644  3 xfs,ip_vs,nf_conntrack

[root@lb4-01 ~]# uname -r
3.10.0-1160.83.1.el7.x86_64
[root@lb4-01 ~]# yum install kernel-devel -y

[root@lb4-01 ~]# ln -s /usr/src/kernels/3.10.0-1160.83.1.el7.x86_64/ /usr/src/linux
[root@lb4-01 ~]# ls -l /usr/src/
lrwxrwxrwx  1 root root 45 Mar  3 10:34 linux -> /usr/src/kernels/3.10.0-1160.83.1.el7.x86_64/

特别注意:
ln命令链接路径要和uname -r输出结果内核版本对应。
如果没有/usr/src/kernels/xx路径,可通过yum install kernel-devel -y安装。

场景1:实现MySQL数据库负载均衡

###配置LVS虚拟IP(VIP)
ifconfig eth1:17 172.16.1.17/24 up        #==>简便写法
#route add -host 172.16.1.17 dev eth1    #==添加主机路由,也可不加此行。

###手工执行配置添加LVS服务并增加两台RS ipvsadm
ipvsadm -C                            #<==    -C        clear the whole table
ipvsadm --set 30 5 60                #<== --set tcp tcpfin udp        set connection timeout values
#ipvsadm -A -t 172.16.1.17:3306 -s wrr   #--add-service   -A   add virtual service with options
ipvsadm -A -t 172.16.1.17:3306 -s wrr -p 20
#dr模式
ipvsadm -a -t 172.16.1.17:3306 -r 172.16.1.51:3306 -g -w 1
ipvsadm -a -t 172.16.1.17:3306 -r 172.16.1.52:3306 -g -w 1
# ipvsadm -a|e -t|u|f service-address -r server-address [options]

[root@lb4-01 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.1.17:3306 wrr persistent 20
  -> 172.16.1.51:3306             Route   1      0          0         
  -> 172.16.1.52:3306             Route   1      0          0         
  
[root@lb4-01 ~]# ipvsadm -Ln --stats
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port               Conns   InPkts  OutPkts  InBytes OutBytes
  -> RemoteAddress:Port
TCP  172.16.1.17:3306                    0        0        0        0        0
  -> 172.16.1.51:3306                    0        0        0        0        0
  -> 172.16.1.52:3306                    0        0        0        0        0
  
[删除方法]
ipvsadm -D -t 172.16.1.17:3306
ipvsadm -d -t 172.16.1.17:3306 -r 172.16.1.51:3306 <==正确

[相关参数说明]
[root@oldboy ~]# ipvsadm -help
# --clear           -C        clear the whole table
# --add-service     -A        add virtual service with options
# --tcp-service     -t service-address   service-address is host[:port]
# --scheduler       -s scheduler         one of rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq,
#  --add-server    -a                   add real server with options
#  --real-server   -r server-address    server-addres s is host (and port)
#  --masquerading  -m                   masquerading (NAT)
#  --gatewaying    -g                   gatewaying (direct routing) (default)
#  --delete-server -d         delete real server
#  --persistent    -p [timeout]         persistent service(会话保持功能)
#  --set tcp tcpfin udp        set connection timeout values
#  --weight       -w weight            capacity of real server
#  --ipip         -i                   ipip encapsulation (tunneling)
提示:更多参数请ipvsadm -help自行查看

命令执行过程及检查配置的执行结果
ipvsadm -C
ipvsadm --set 30 5 60
ipvsadm -A -t 172.16.1.17:3306 -s wrr -p 20
ipvsadm -a -t 172.16.1.17:3306 -r 172.16.1.51:3306 -g -w 1
ipvsadm -a -t 172.16.1.17:3306 -r 172.16.1.52:3306 -g -w 1
ipvsadm -L -n --sort
ipvsadm -d -t 172.16.1.17:3306 -r 172.16.1.51:3306 #==>删除测试
ipvsadm -L -n --sort
ipvsadm -a -t 172.16.1.17:3306 -r 172.16.1.51:3306 -g -w 1
ipvsadm -L -n --sort

手工在RS端绑定lo网卡及抑制ARP

#每台real server端执行db01、db02
命令:
ifconfig lo:17 172.16.1.17 up
route add -host 172.16.1.17 dev lo
工作中写到配置文件:
vim /etc/sysconfig/network-scripts/ifcfg-lo:17

#centos7 添加方法
ip addr add 172.16.1.17/32 dev lo label lo:17
route add -host 172.16.1.17 dev lo
过程:
ifconfig lo:17 
route -n|grep 172.16.1.17
#172.16.1.17     0.0.0.0         255.255.255.255 UH        0 0          0 lo

每个集群节点上的环回接口(lo)设备上被绑定VIP地址(其广播地址是其本身,子网掩码是255.255.255.255,采取可变长掩码方式把网段划分成只含一个主机地址的目的是避免ip地址冲突)允许LVS-DR集群中的集群节点接受发向该VIP地址的数据包,这会有一个非常严重的问题发生,集群内部的真实服务器将尝试回复来自正在请求VIP客户端的ARP广播,这样所有的真实服务器都将声称自己拥有该VIP地址,这时客户端将有可能直接发送请求数据包到某台真实服务器上,从而破坏了DR集群的负载均衡策略。因此,必须要抑制所有真实服务器响应目标地址为VIP的ARP广播,而把客户端ARP广播的响应交给负载均衡调度器。

手工在RS端抑制ARP响应

#在db01、db02执行
抑制ARP响应方法如下:
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce

cat /proc/sys/net/ipv4/conf/lo/arp_ignore
cat /proc/sys/net/ipv4/conf/all/arp_ignore  
cat /proc/sys/net/ipv4/conf/all/arp_announce 
cat /proc/sys/net/ipv4/conf/lo/arp_announce    

#在web01上安装mysql测试:
yum install mariadb -y
mysql -h 172.16.1.17 -uroot -poldboy123  #172.16.1.17是VIP

####停掉了51,发现连不了
lvs没有健康检查功能,51停掉了,仍然请求发给51,需要手工清理51

[root@lb4-01 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.1.17:3306 wrr persistent 20
  -> 172.16.1.51:3306             Route   1      0          0         
  -> 172.16.1.52:3306             Route   1      0          4         
  
[root@lb4-01 ~]# ipvsadm -d -t 172.16.1.17:3306 -r 172.16.1.51:3306
[root@lb4-01 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.1.17:3306 wrr persistent 20
  -> 172.16.1.52:3306              Route   1      0          0   
重连成功,LVS的健康检查要靠keepalved配合实现

抑制ARP脚本实现脚本

开发脚本配置LVS RS真实服务器端
#!/bin/bash
# Written by oldboy
# description: Config realserver lo and apply noarp 
VIP=(
        172.16.1.17
     )
. /etc/rc.d/init.d/functions
case "$1" in
start)
        for ((i=0; i<`echo ${#VIP[*]}`; i++))
        do
           interface="lo:`echo ${VIP[$i]}|awk -F . '{print $4}'`"
           /sbin/ifconfig $interface ${VIP[$i]} broadcast ${VIP[$i]} netmask 255.255.255.255 up
        done
        echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
        echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
        echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
        echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
       action "Start LVS of RearServer.by old1boy"
        ;;
stop)
        for ((i=0; i<`echo ${#VIP[*]}`; i++))
        do
            interface="lo:`echo ${VIP[$i]}|awk -F . '{print $4}'`"
            /sbin/ifconfig $interface ${VIP[$i]} broadcast ${VIP[$i]} netmask 255.255.255.255 down
        done
        echo "close LVS Directorserver"
        if [ ${#VIP[*]} -eq 1 ];then
          echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
          echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
          echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
          echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
        fi
       action "Close LVS of RearServer.by old2boy"
        ;;
*)
        echo "Usage: $0 {start|stop}"
        exit 1
esac

工作中可以将lo网卡写到配置文件:

db01、db02上操作
cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-lo:17
[root@db01 network-scripts]# cat /etc/sysconfig/network-scripts/ifcfg-lo:17
TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="none"
DEFROUTE="yes"
NAME="lo:17"
DEVICE="lo:17"
ONBOOT="yes"
IPADDR="172.16.1.17"
PREFIX="32

arp抑制技术参数说明

arp_ignore- INTEGER
定义对目标地址为本地IP的ARP询问不同的应答模式
0 -(默认值): 回应任何网络接口上对任何本地IP地址的arp查询请求。
1 -只回答目标IP地址是来访网络接口本地地址的ARP查询请求。
2 -只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内。
3 -不回应该网络界面的arp请求,而只对设置的唯一和连接地址做出回应。
4-7 -保留未使用。
8 -不回应所有(本地地址)的arp查询。
arp_announce - INTEGER
对网络接口上,本地IP地址的发出的,ARP回应,作出相应级别的限制:  
确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口
0 -(默认) 在任意网络接口(eth0,eth1,lo)上的任何本地地址
1 -尽量避免不在该网络接口子网段的本地地址做出arp回应. 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一。如果该来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.
2 -对查询目标使用最适当的本地地址,在此模式下将忽略这个IP数据包的源地址并尝试选择能与该地址通信的本地地址,首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址。如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.限制了使用本地的vip地址作为优先的网络接口

场景2:实现LVS+keepalved L4 mysql集群高可用

配置lb4-01/02

[root@lb4-01 ~]# cat /etc/keepalived/keepalived.conf
global_defs { 
   router_id lb4-01
}
vrrp_instance VI_1 {
    state MASTER    #state BACKUP
    interface eth1
    virtual_router_id 52
    priority 150    #priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
      172.16.1.17/24 dev eth1 label eth1:17
    }
}
#MySQL port 3306
virtual_server 172.16.1.17 3306 {
    delay_loop 6              
    lb_algo wrr                
    lb_kind DR
    persistence_timeout 20     
    protocol TCP
    real_server 172.16.1.51 3306 {
     weight 1              
     TCP_CHECK {
     connect_timeout 5       
     ##nb_get_retry 3
     delay_before_retry 3
     connect_port 3306
     }
}
    real_server 172.16.1.52 3306 {
     weight 1
     TCP_CHECK {
     connect_timeout 5
     ## nb_get_retry 3
     delay_before_retry 3
     connect_port 3306
     }
    }
}

配置完重启keepalived完成
[root@lb4-01 ~]# systemctl restart keepalived
检查
[root@lb4-01 ~]# ifconfig |grep 'eth1:17'
#在web01上测试
mysql -h 172.16.1.17 -uroot -poldboy123
#停掉db01上的mariadb
[root@db01 ~]# systemctl stop mariadb
#在测试web01测试
mysql -h 172.16.1.17 -uroot -poldboy123
use test;
show tables;

场景3:实现Web4层负载及配合后端7层反向代理+web节点

环境说明
2.L4+L7+WEB 大规模web负载均衡
L4:
192.168.238.15    172.16.1.15 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
192.168.238.16    172.16.1.16 LVS调度器(Director)    对外提供服务的VIP为192.168.238.17
L7:
192.168.238.5    172.16.1.5  nginx lb01 测好了。
192.168.238.6    172.16.1.6  nginx lb02  

192.168.238.7    172.16.1.7  RS1(真实服务器)  web01
192.168.238.8    172.16.1.8  RS2(真实服务器)  web02
[root@lb4-02 keepalived]# curl -H "host:www.yunwei.com" 172.16.1.7
web01
[root@lb4-02 keepalived]# curl -H "host:www.yunwei.com" 172.16.1.8
web02

配置keepalived lb4-01/02

[root@lb4-01 ~]# vi /etc/keepalived/keepalived.conf

global_defs {
   router_id lb4-01
}
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 53
    priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
      192.168.238.17/24 dev eth0 label eth0:17
    }
}
#web config
virtual_server 192.168.238.17 80 {
    delay_loop 6              
    lb_algo wrr                
    lb_kind DR
    persistence_timeout 20     
    protocol TCP                
    real_server 192.168.238.5 80 {
        weight 1              
        TCP_CHECK {
        connect_timeout 5       
        #nb_get_retry 3
        delay_before_retry 3
        connect_port 80
        }
    }
    real_server 192.168.238.6 80 {
        weight 1
        TCP_CHECK {
        connect_timeout 5
       # nb_get_retry 3
        delay_before_retry 3
        connect_port 80
        }
    }
}
===============================================
vrrp_instance VI_2 {
    state MASTER                                 #state BACKUP
    interface eth1
    virtual_router_id 52
    priority 150                                #priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
      172.16.1.17/24 dev eth1 label eth1:17
    }
}
#MySQL config 
virtual_server 172.16.1.17 3306 {
    delay_loop 6              
    lb_algo wrr                
    lb_kind DR
    persistence_timeout 20     
    protocol TCP                

   real_server 172.16.1.51 3306 {
        weight 1              
        TCP_CHECK {
        connect_timeout 5       
        #nb_get_retry 3
        delay_before_retry 3
        connect_port 3306
        }
    }

    real_server 172.16.1.52 3306 {
        weight 1
        TCP_CHECK {
        connect_timeout 5
       # nb_get_retry 3
        delay_before_retry 3
        connect_port 3306
        }
    }
}

lb01、lb02配置VIP绑定,和抑制ARP(脚本)

[root@lb01 conf.d]# mkdir /server/scripts -p
[root@lb01 conf.d]# cd /server/scripts/
[root@lb01 scripts]# vim ipvs.sh
#!/bin/bash
# Written by oldboy
# description: Config realserver lo and apply noarp 
VIP=(
        192.168.238.17
     )
. /etc/rc.d/init.d/functions
case "$1" in
start)
        for ((i=0; i<`echo ${#VIP[*]}`; i++))
        do
           interface="lo:`echo ${VIP[$i]}|awk -F . '{print $4}'`"
           /sbin/ifconfig $interface ${VIP[$i]} broadcast ${VIP[$i]} netmask 255.255.255.255 up
        done
        echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
        echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
        echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
        echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
           action "Start LVS of RearServer.by old1boy"
        ;;
stop)
        for ((i=0; i<`echo ${#VIP[*]}`; i++))
        do
            interface="lo:`echo ${VIP[$i]}|awk -F . '{print $4}'`"
            /sbin/ifconfig $interface ${VIP[$i]} broadcast ${VIP[$i]} netmask 255.255.255.255 down
        done
        echo "close LVS Directorserver"
        if [ ${#VIP[*]} -eq 1 ];then
          echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
          echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
          echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
          echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
        fi
           action "Close LVS of RearServer.by old2boy"
        ;;
*)
        echo "Usage: $0 {start|stop}"
        exit 1
esac

[root@lb01 scripts]# sh ipvs.sh start
Start LVS of RearServer.by old1boy                         [  确定  ]

#检查
[root@lb01 scripts]# ifconfig |grep 'lo:17'

获取lvs+nginx+web前端用户真实IP

#测试,需要把proxy_protocol给注释掉,不然访问报错。
upstream www {
        server 172.16.1.7:80;
        server 172.16.1.8:80;
}
server {
        listen 80;
        #listen 80 proxy_protocol;       #注释
        server_name www.yunwei.com;
        set_real_ip_from 172.16.1.0/24;  #添加七层负载前经过的代理IP地址
        real_ip_header proxy_protocol;   #将proxy_protocol获取的IP赋值给$remote_addr
        location / {
                proxy_pass http://www;
                proxy_set_header Host $http_host;
                proxy_set_header X-Forwarded-For $proxy_protocol_addr;
                #将proxy_protocol真实客户端的IP地址赋值给X-Forwarded-For变量携带至后端
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_headers_hash_max_size 51200;
                proxy_headers_hash_bucket_size 6400;
        }
}
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; 这个开启
客户端访问LVS是携带源IP的,LVS仅仅是转发,DR修改目的MAC,并没有改变源IP。所以客户源IP可以被nginx lb01获取到,然后通过x-forward-for传给web节点

#查看日志
[root@web01 conf.d]# tail -f /var/log/nginx/access.log
172.16.1.6 - - [03/Mar/2023:16:14:53 +0800] "GET / HTTP/1.0" 200 6 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54" "192.168.238.1"
172.16.1.6 - - [03/Mar/2023:16:14:54 +0800] "GET / HTTP/1.0" 200 6 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54" "192.168.238.1"
172.16.1.6 - - [03/Mar/2023:16:14:54 +0800] "GET / HTTP/1.0" 200 6 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54" "192.168.238.1"
172.16.1.6 - - [03/Mar/2023:16:14:55 +0800] "GET / HTTP/1.0" 200 6 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54" "192.168.238.1"

最后一位是192.168.238.1是真实IP