LVS - Keepalived 實現 L4 高可用負載均衡器

LVS 負載均衡器

LVS 是一種開源的 L4 TCP/UDP 負載均衡器,本質是一個 Linux Kernel Module,所以稱爲 Linux Virtual Server(Linux 虛擬服務器)。LVS 的目標是使用 LB 技術和集羣技術來實現一個具有良好可伸縮性(Scalability)、可靠性(Reliability)和可管理性(Manageability)的 “Virtual Server“。

部署架構

三層部署架構:

  1. 負載調度器層:客戶端請求流量入口,需要嚴格保障該層面的高可用性,以獲取良好的可靠性。

  2. 服務器集羣層:客戶端請求應答單元,需要儘量保證該層面的無狀態性,以獲得更好的可伸縮性。

  3. 共享存儲池層:邏輯上集中的數據存儲池,用於支撐服務器集羣的無狀態性。

基本概念

Server 類型

IP 類型

軟件架構

LVS 的軟件架構比較簡單,只有 2 個模塊:

  1. ipvsadm(用戶態指令行工具):用於管理集羣服務及集羣中的 RS 節點;

  2. ipvs(內核態轉發模塊):工作在 iptables 的 INPUT 鏈上,提供 L4 流量轉發和策略執行。

用戶通過 ipvsadm 添加規則,再由 ipvs 來實現功能。

運行原理

LVS-NAT 模式

模式特徵

  1. Client REQ 的 dstIP 是 VIP,srcIP 是 CIP,dstPort 是 VPort,srcPort 是隨機分配的 CPort;

  2. DS 根據 LB Policy 選擇一個 RS;

  3. L3 層 NAT:

  1. L4 層 PAT:
  1. RS 的 Default Gateway 必須指向 DIP,這樣 dstIP 爲 CIP 才能進入 DS 轉發。

模式缺點:DS 會成爲帶寬和高性能的瓶頸,RS 彈性伸縮性有限。

LVS-TUN 模式

從上述 LVS-NAT 模式的特徵可知,其瓶頸的關鍵在於頻繁的 IP 和 Port 編輯。在改良的 LVS-TUN 模式中,DS 爲了避免對 Original Packet 的編輯而引入了 IP Tunneling 技術。因此也稱爲透明模式。

簡而言之,LVS-TUN 模式在 DS 和 RS 之間建立了 IP Tunnel,Client REQ 會在 Tunnel 中傳輸而無需修改。前提是所有的 RSs 都掛載了 VIP,這樣 RS 才能接收 REQ。也因此 RS 能夠直接將 REQ 的 srcIP(CIP)和 dstIP(VIP)反轉構成一個 RESP 並直接返回給 Client 而繞開 DS 的轉發。這使得整個流量路徑看起來就像是一個 “三角形狀 ",所以也被稱爲非對稱性網絡。

由於一般的網絡服務場景中,RESP 往往要不 REQ 的 Size 更大,所以三角流量的 LVS-TUN 模式相較於 LVS-NAT 模式集羣系統的最大吞吐量可以提高 10 倍。

模式特性

  1. Client REQ 不會被編輯,但會封裝上 OuterIP 層,實現 IP Tunnel;

  2. 不支持 PAT;

  3. RS 除了擁有常規的 RIP 之外,都會綁定同一個 VIP,一般配置到 lo 網絡接口上(作爲 VIP 接口)。RS VIP 是實現三角流量的關鍵。

  4. RS 的 Default Gateway 爲前端 Router,RESP 不經過 DS。

模式缺點

  1. 要求 RS 支持 IP Tunneling 技術,對 RS OS 有侵入性。

  2. IP Tunneling 增加了 OuterIP 封裝層,也帶來了一定的報文處理開銷。

  3. 對物理組網具有較高的設計和部署要求。

LVS-DR

LVS-DR(Direct Routing)模式的網絡流量模型與 LVS-TUN 模式類似,都是三角流量模型。但核心區別在於 LVS-TUN 是基於 L3 IP 技術實現的,而 LVS-DR 是基於 L2 技術實現的。所以,LVS-DR 沒有 IP Tunneling 的開銷,也不對 RS 具有侵入性。

模式特性

  1. 要求 DS(DIP/MAC)和 RS(RIP/MAC)處於同一個 L2 LAN 中;

  2. 所有 RS 都配置了一個 lo VIP,同時所有的 RS 會抑制 lo 接口響應 ARP 請求(RS 和 DS 處於同一個 LAN),保證 Client REQ 經過前端 Router 之後必須先進入 DS 而不是 RS;

  3. DS 會通過改寫 L2 Frame 的 dstMAC 爲 RS-MAC,來實現將 Client REQ 轉發給 RS,並且由 RS 將 RESP 直接發送給 Client,無需經過 DS;

  4. RS 的 Default Gateway 爲前端 Router,RESP 不經過 DS;

  5. 不支持 PAT;

模式缺點:必須處於同一個 LAN,集羣規模受限。

負載均衡算法

靜態負載均衡

動態負載均衡

ipvsadm CLI

查看

查看幫助手冊

$ ipvsadm --help

查看 LVS 規則

$ ipvsadm -Ln

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
UDP  10.0.0.9:5678 rr
  -> 192.168.1.8:5678             Masq    1      0          0

添加

$ ipvsadm -A -t 192.168.1.100:80 -s rr
$ ipvsadm -a -t 192.168.1.100:80 -r 172.16.16.2:80 -m -w 1

修改

$ ipvsadm -E -t 192.168.1.100:80 -s wlc
$ ipvsadm -e -t 192.168.1.100:80 -r 172.16.16.2:80 -m -w 2

刪除

$ ipvsadm -D -t 192.168.1.100:80
$ ipvsadm -d -t 192.168.1.100:80 -r 172.16.16.2:80
$ ipvsadm -C

LVS-NAT & Keepalived 部署示例

雖然 LVS-NAT 模式存在着缺點,但由於其優秀的多場景適用性,所以大多數 LB 商業產品依舊會選擇使用此模式,例如:Cisco LocalDirector、F5 Big/IP、Alteon ACEDirector 等。

在 2 臺 DS 節點上安裝 Keepalived & LVS:

$ yum install -y keepalived ipvsadm

$ keepalived --version
Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2

$ ipvsadm --version
ipvsadm v1.27 2008/5/15 (compiled with popt and IPVS v1.2.1)

TCP 負載均衡應用場景

IP 規劃

網絡拓撲

DS1 配置

啓動 ipvsadm

$ touch /etc/sysconfig/ipvsadm
$ systemctl start ipvsadm.service

啓動 keepalived

$ cat /etc/sysconfig/keepalived
# Options for keepalived. See `keepalived --help' output and keepalived(8) and
# keepalived.conf(5) man pages for a list of all options. Here are the most
# common ones :
#
# --vrrp               -P    Only run with VRRP subsystem.
# --check              -C    Only run with Health-checker subsystem.
# --dont-release-vrrp  -V    Dont remove VRRP VIPs & VROUTEs on daemon stop.
# --dont-release-ipvs  -I    Dont remove IPVS topology on daemon stop.
# --dump-conf          -d    Dump the configuration data.
# --log-detail         -D    Detailed log messages.
# --log-facility       -S    0-7 Set local syslog facility (default=LOG_DAEMON)
#

KEEPALIVED_OPTIONS="-D -d"

$ cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   lvs_id LVS_01
}

vrrp_sync_group VG1 {
   group {
      VI_1
      VI_GATEWAY
   }
}

vrrp_instance VI_1 {
        state MASTER
        interface eno16777736
        lvs_sync_daemon_inteface eno16777736
        virtual_router_id 51
        priority 150
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass 1111
        }

        virtual_ipaddress {
                192.168.1.112
        }
}

vrrp_instance VI_GATEWAY {
        state MASTER
        interface eno33554960
        lvs_sync_daemon_inteface eno33554960
        virtual_router_id 52
        priority 150
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass example
        }
        virtual_ipaddress {
                10.0.0.105
        }
}

virtual_server 192.168.1.112 80 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    nat_mask 255.255.255.0
    protocol TCP

    real_server 10.0.0.101 80 {
        weight 1
    }
    real_server 10.0.0.102 80 {
        weight 1
    }
}

$ systemctl start keepalived

查看 VIP 和  DIP

$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eno16777736: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:27:d5:3b brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.110/24 brd 192.168.1.255 scope global dynamic eno16777736
       valid_lft 6646sec preferred_lft 6646sec
    inet 192.168.1.112/32 scope global eno16777736
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe27:d53b/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
3: eno33554960: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:27:d5:45 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.103/24 brd 10.0.0.255 scope global eno33554960
       valid_lft forever preferred_lft forever
    inet 10.0.0.105/32 scope global eno33554960
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe27:d545/64 scope link
       valid_lft forever preferred_lft forever

查看 ipvs 規則: ipvs 的規則應該是 VIP 轉發到 RS,而不是 DIP 轉發到 RS。

$ 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.1.112:80 rr
  -> 10.0.0.101:80                Masq    1      0          0
  -> 10.0.0.102:80                Masq    1      0          0

NOTE:LVS1 上並不會真的開啓 80 端口,只是一個 VPORT。

$ netstat -lpntu | grep 80

開啓路由轉發功能(DS 要開啓路由轉發功能)

$ cat /etc/sysctl.conf
# System default settings live in /usr/lib/sysctl.d/00-system.conf.
# To override those settings, enter new settings here, or in an /etc/sysctl.d/<name>.conf file
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
net.ipv4.ip_forward = 1

[root@localhost ~]# sysctl -p
net.ipv4.ip_forward = 1

清空防火牆規則

$ iptables -F -t filter
$ iptables -F -t raw
$ iptables -F -t mangle
$ iptables -F -t nat

DS2 配置

配置 LVS2 和配置 LVS1 的步驟基本一致,但 Keepalived 的配置文件有些許變動。

$ cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   lvs_id LVS_01
}

vrrp_sync_group VG1 {
   group {
      VI_1
      VI_GATEWAY
   }
}

vrrp_instance VI_1 {
        state BACKUP
        interface eno16777736
        lvs_sync_daemon_inteface eno16777736
        virtual_router_id 51
        priority 140
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass 1111
        }

        virtual_ipaddress {
                192.168.1.112
        }
}

vrrp_instance VI_GATEWAY {
        state BACKUP
        interface eno33554960
        lvs_sync_daemon_inteface eno33554960
        virtual_router_id 52
        priority 150
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass example
        }
        virtual_ipaddress {
                10.0.0.105
        }
}

virtual_server 192.168.1.112 80 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    nat_mask 255.255.255.0
    protocol TCP

    real_server 10.0.0.101 80 {
        weight 1
    }
    real_server 10.0.0.102 80 {
        weight 1
    }
}

RS1 配置

網卡的 Gateway 指向 DIP:RS 的默認網關應該指向 DIP。

$ cat /etc/sysconfig/network-scripts/ifcfg-eno33554960
HWADDR=00:0C:29:15:40:15
TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_FAILURE_FATAL=no
NAME=eno33554960
UUID=be63d7a3-f7eb-4204-9c1d-cecb2e857d0b
ONBOOT=yes
IPADDR=10.0.0.101
GATEWAY=10.0.0.105
NETMASK=255.255.255.0
DNS1=114.114.114.114

清空防火牆規則

$ iptables -F -t filter
$ iptables -F -t raw
$ iptables -F -t mangle
$ iptables -F -t nat

安裝 TCP 80 的 httpd 服務

$ yum install -y httpd

$ cat /var/www/html/index.html
<html>
  <body>
    <h1>RS1</h1>
  </body>
</html>

$ systemctl start httpd

$ netstat -lpntu | grep 80
tcp6       0      0 :::80                   :::*                    LISTEN      18227/httpd

RS2 配置

配置 RS2 與 RS1 的步驟基本一致,只是 httpd 的 index.html 有些許改變:

$ cat /var/www/html/index.html
<html>
  <body>
    <h1>RS2</h1>
  </body>
</html>

驗證

在客戶端 curl http://VIP:VPort 會輪詢的訪問 RS1 和 RS2。

$ curl 192.168.1.112
<html>
  <body>
    <h1>RS2</h1>
  </body>
</html>

$ curl 192.168.1.112
<html>
  <body>
    <h1>RS1</h1>
  </body>
</html>

在 MR(Master Router) 查看連接表

$ ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination
TCP 01:27  TIME_WAIT   192.168.1.100:52034 192.168.1.112:80   10.0.0.101:80

在 BR(Backup Router) 查看連接表

$ ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination

可以看見因爲 Keepalived 只支持主從 HA 模式,所以 LVS 的連接表狀態並沒有同步,但是可以 Failover(故障轉移)。當關閉 MR 電源之後還可以繼續通過客戶端訪問 curl http://VIP:VPort,查看發現 VIP 漂移到了 BR,同時再次查看 BR 的連接表:

$ ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination
TCP 01:57  TIME_WAIT   192.168.1.100:52115 192.168.1.112:80   10.0.0.101:80

UDP 負載均衡應用場景

DS1/2 配置

配置 Keepalived,添加 UDP 協議負載均衡 virtual_server:

$ cat /etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
   lvs_id LVS_01
}

vrrp_sync_group VG1 {
   group {
      VI_1
      VI_GATEWAY
   }
}

vrrp_instance VI_1 {
        state MASTER
        interface eno16777736
        lvs_sync_daemon_inteface eno16777736
        virtual_router_id 51
        priority 150
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass 1111
        }

        virtual_ipaddress {
                192.168.1.112
        }
}

vrrp_instance VI_GATEWAY {
        state MASTER
        interface eno33554960
        lvs_sync_daemon_inteface eno33554960
        virtual_router_id 52
        priority 150
        advert_int 1
        authentication {
                auth_type PASS
                auth_pass example
        }
        virtual_ipaddress {
                10.0.0.105
        }
}

virtual_server 192.168.1.112 80 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    nat_mask 255.255.255.0
    protocol TCP

    real_server 10.0.0.101 80 {
        weight 1
    }
    real_server 10.0.0.102 80 {
        weight 1
    }
}

virtual_server 192.168.1.112 9999 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    nat_mask 255.255.255.0
    protocol UDP

    real_server 10.0.0.101 9999 {
        weight 1
    }
    real_server 10.0.0.102 9999 {
        weight 1
    }
}

$ systemctl restart keepalived

$ 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.1.112:80 rr
  -> 10.0.0.101:80                Masq    1      0          0
  -> 10.0.0.102:80                Masq    1      0          0
UDP  192.168.1.112:9999 rr
  -> 10.0.0.101:9999              Masq    1      0          0
  -> 10.0.0.102:9999              Masq    1      0          0

NOTE:DS1/2 的配置大同小異,只是 vrrp_instance 的角色和權重有所區別而已。

RS1/2 配置

啓用 nc udp 服務器,接收外部傳輸過來的文件:

$ yum install -y nc

$ nc -ul 9999 > file.txt

$ netstat -lpntu | grep 9999
udp        0      0 0.0.0.0:9999            0.0.0.0:*                           2618/nc
udp6       0      0 :::9999                 :::*                                2618/nc

驗證

在客戶端上準備兩個文件並執行文件傳輸:

$ nc -u -w 1 192.168.1.112 9999 < 1.txt                                                                                                                                        
$ nc -u -w 1 192.168.1.112 9999 < 2.txt

可以看見這兩個文件分別被 RS1 和 RS2 的 nc udp 服務端接收。

$ nc -ul 9999 > file.txt
Ncat: Connection refused.

$ cat file.txt
22222222222
$ nc -ul 9999 > file.txt
Ncat: Connection refused.

$ cat file.txt
11111111111

查看 ipvs 轉發表:

$ ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination
UDP 04:20  UDP         192.168.1.100:65136 192.168.1.112:9999 10.0.0.101:9999
UDP 04:19  UDP         192.168.1.100:51930 192.168.1.112:9999 10.0.0.102:9999
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/N2X1tMdQtriP7gjO2l5dhw