一、Keepalived介紹
Keepalived的作用是檢測服務器狀態,如果一台服務器宕機或者出現其他故障導致當前服務器不可用,keep alived就會檢測到並將故障的服務器從系統中剔除,同時使用備用服務器替代該服務器的工作,當服務器工作正常后Keepalived自動將服務器加入到服務器群中,這些工作全部自動完成,不需要人工干涉,需要人工做的只是修復故障的服務器。
Keepalived軟件起初是專為LVS負載均衡軟件設計的,用來管理並監控LVS集群系統中各個服務節點的狀態,后來又加入了可以實現高可用的VRRP功能。因此,Keepalived除了能夠管理LVS軟件外,還可以作為其他服務(例如:Nginx、Haproxy、MySQL等)的高可用解決方案軟件。
Keepalived軟件主要是通過VRRP協議實現高可用功能的。VRRP是Virtual Router RedundancyProtocol(虛擬路由器冗余協議)的縮寫,VRRP出現的目的就是為了解決靜態路由單點故障問題的,它能夠保證當個別節點宕機時,整個網絡可以不間斷地運行。
所以,Keepalived 一方面具有配置管理LVS的功能,同時還具有對LVS下面節點進行健康檢查的功能,另一方面也可實現系統網絡服務的高可用功能。
1.1 Keepalived的功能
(1)、管理LVS軟件
(2)、基於VRRP實現高可用
(3)、健康檢查,故障切換
1.2 Keepalived的工作原理
1.2.1 OSI七層結構
分層 | 功能 | 相關協議 |
---|---|---|
應用層 | 網絡服務和最終用戶的一個接口 | TFTP,HTTP,SNMP,DNS,FTP,SMTP,TELNET |
表示層 | 數據的表示、安全、壓縮 | 無協議 |
會話層 | 會話的建立、管理、中止 | 無協議 |
傳輸層 | 定義傳輸數據的協議端口號,以及流控和差錯校驗 | TCP,UDP |
網絡層 | 進行邏輯地址尋址,實現不同網絡之間的路徑選擇 | IP,ICMP,RIP,OSPF,BGP,IGMP |
數據鏈路層 | 建立邏輯連接、硬件地址尋址、差錯校驗等功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
物理層 | 建立、連接、斷開物理連接 | ISO2110,IEEE802,IEEE802.2 |
1.2.2 工作原理
Keepalived工作在TCP/IP協議的IP層、TCP層、應用層,既Layer 3/4/5;
Layer3:當Keepalived工作在這層時,它會定期向服務器群中的服務器發送ICMP包,如果發現某台服務器IP沒有激活就會報告這台服務器失效,並且將其從服務器群剔除。Layer3的是以服務器IP地址是否有效作為判斷是否存活的標准;
Layer4:當工作在這層時,主要是以TCP端口狀態來判斷服務器工作是否正常;
Layer5:當工作在這層時,主要是以用戶設定的服務運行是否正常來判斷是否存活;
Keepalived高可用主要是通過VRRP進行通信的,VRRP是通過競選機制來確定主備的,主的優先級高於備。所以正常工作時,主會優先提供服務,備處於等待階段,只有當主出現異常,備才會接管主的任務向外提供服務。
在Keepalived服務器群之間,只有作為主的服務器不斷發送VRRP廣播包,告訴備它還活着,此時備不會搶占主,只有當主不可用,既備接受不到主的VRRP廣播包,這時候備就會啟動相關的服務接管主的任務向外提供服務,以保證服務的正常使用。
二、Keepalived使用
2.1 Keepalived軟件安裝
# 安裝命令
# yum install keepalived -y
# rpm -ql keepalived
/etc/keepalived
/etc/keepalived/keepalived.conf # 主配置文件
/etc/sysconfig/keepalived
/usr/bin/genhash
/usr/lib/systemd/system/keepalived.service
/usr/libexec/keepalived
/usr/sbin/keepalived
2.2 默認配置測試
2.2.1 配置介紹
global_defs {
notification_email { # 定義郵件地址
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc # 定義發送郵件地址
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_instance VI_1 { # 定義實例
state MASTER # 狀態參數:MASTER/BACKUP只是說明
interface eth0 # 虛擬IP放置的網卡地址
virtual_router_id 51 # 設置集群ID,同一個組的ID要一致
priority 100 # 優先級設置,數值越大優先級越高
advert_int 1 # 主備通訊時間間隔
authentication { # 驗證相關
auth_type PASS
auth_pass 1111
}
virtual_ipaddress { # 虛擬IP地址
192.168.200.16
192.168.200.17
192.168.200.18
}
}
2.2.2 最終配置文件
(1)、Master配置
! Configuration File for keepalived
global_defs {
router_id lb01
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.169.200
}
}
(2)、Slave配置
! Configuration File for keepalived
global_defs {
router_id lb02
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 140
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.169.200
}
}
(3)、啟動Keepalived
# systemctl start keepalived
(4)、查看VIP是否啟動
[root@localhost keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:24:d1:b5 brd ff:ff:ff:ff:ff:ff
inet 192.168.169.131/24 brd 192.168.169.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet 192.168.169.200/32 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::fd8:8531:b2e2:c6bb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
(5)、測試
關閉master上的keepalived,查看VIP是否漂移到備。
# 關閉主master
# systemctl stop keepalived
# 查看備上的IP信息
[root@localhost keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:d9:69:08 brd ff:ff:ff:ff:ff:ff
inet 192.168.169.130/24 brd 192.168.169.255 scope global dynamic ens33
valid_lft 1464sec preferred_lft 1464sec
inet 192.168.169.200/32 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::51f1:7ad1:f554:65cc/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:88:bb:94:f6 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
2.2.3 總結
Keepalived的主備配置文件的主要區別有:
(1)、router_id不一致
(2)、state描述信息不一致
(3)、priority優先級不一致
2.3 腦裂介紹
在高可用系統中,如果兩個節點的心跳線斷開,本來兩個節點為一個整體、動作協調的一個HA系統,現在由於兩個之間的心跳線斷開導致它們分裂成了兩個單獨的個體。由於雙方互相失去了聯系,都會以為對方出了故障。這時候這兩個單獨的個體就像"腦裂人"一樣互相爭搶共享資源、爭用應用服務,這樣就會造成嚴重問題:
(1)、共享資源被瓜分,兩邊服務都起不來;
(2)、兩邊服務都起來了,同時提供服務,同時讀寫存儲,導致數據不一致甚至損壞。
2.3.1 產生腦裂的原因
一般來說,腦裂的發生,有以下幾種原因:
(1)、HA服務器之間心跳線故障,導致無法正常通信;
(2)、HA服務器上開啟了防火牆,阻擋了心跳線的信息傳輸;
(3)、HA服務器上心跳網卡配置不正確,導致心跳信息發送失敗;
(4)、其他服務器配置不當的原因。比如心跳方式不同,心跳廣播沖突,軟件BUG等;
(5)、Keepalived配置里同一 VRRP實例中如果 virtual_router_id兩端參數配置不一致也會導致裂腦問題發生。
2.3.2 常見的解決辦法
在實際環境中,我們可以從以下幾個方面來防止腦裂的問題:
(1)、同時使用串行線路或者以太網電纜連接,同時使用兩條心跳線路,如果一條壞了,另外一條還能正常提供服務;
(2)、當檢測到腦裂時強行關閉一個節點(該功能需要特殊設備支持,如Stonith,feyce),相當於備節點接受不到心跳心跳消患,通過單獨的線路發送關機命令關閉主節點的電源;
(3)、做好腦裂監控報警(用zabbix等來監控),在問題發生時能在第一時間介入仲裁,降低損失。
(4)、啟動磁盤鎖。正在服務一方鎖住共享磁盤,“裂腦”發生時,讓對方完全“搶不走”共享磁盤資源。但使用鎖磁盤也會有一個不小的問題,如果占用共享盤的一方不主動“解鎖”,另一方就永遠得不到共享磁盤。現實中假如服務節點突然死機或崩潰,就不可能執行解鎖命令。后備節點也就接管不了共享資源和應用服務。於是有人在HA中設計了“智能”鎖。即:正在服務的一方只在發現心跳線全部斷開(察覺不到對端)時才啟用磁盤鎖,平時就不上鎖了;
(5)、加入仲裁機制。例如設置網關IP,當腦裂發生時,兩個節點都各自ping以下這個網關IP,不通則表明斷點就在本端,不僅“心跳”、還兼對外“服務”的本端網絡鏈路斷了,即使啟動(或繼續)應用服務也沒有用了,那就主動放棄競爭,讓能夠ping通網關IP的一端去起服務。更保險一些,ping不通網關IP的一方干脆就自我重啟,以徹底釋放有可能還占用着的那些共享資源。
2.4 Keepalived監控nginx防止腦裂
(1)、執行腳本,用來檢測
vim check_keepalived.sh
#!/bin/bash
NGINX_SBIN=`which nginx`
NGINX_PORT=80
function check_nginx(){
NGINX_STATUS=`nmap localhost -p ${NGINX_PORT} | grep "80/tcp open" | awk '{print $2}'`
NGINX_PROCESS=`ps -ef | grep nginx|grep -v grep|wc -l`
}
check_nginx
if [ "$NGINX_STATUS" != "open" -o $NGINX_PROCESS -lt 2 ]
then
${NGINX_SBIN} -s stop
${NGINX_SBIN}
sleep 3
check_nginx
if [ "$NGINX_STATUS" != "open" -o $NGINX_PROCESS -lt 2 ];then
systemctl stop keepalived
fi
fi
(2)、添加執行權限
chmod +x check_keepalived.sh
(3)、配置keepalived
master:
! Configuration File for keepalived
global_defs {
router_id lb01
}
# 定義腳本
vrrp_script check_ng {
script "/etc/keepalived/check_keepalived.sh" # 腳本路徑
interval 2 # 執行時間間隔
weight -5 # 計算權重值,腳本結果導致的優先級變更,檢測失敗(腳本返回非0)則優先級 -5
fall 3 # 檢測連續3次失敗才算確定是真失敗。會用weight減少優先級(1-255之間)
rise 2 # 檢測2次成功就算成功。但不修改優先級
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.169.200
}
# 調用腳本
track_script {
check_ng
}
}
slave:
! Configuration File for keepalived
global_defs {
router_id lb02
}
vrrp_script check_ng {
script "/etc/keepalived/check_keepalived.sh"
interval 2
weight -5
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 147
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.169.200
}
track_script {
check_ng
}
}
2.5 Keepalived設置master故障恢復后不重新搶回VIP
(1)、master配置
! Configuration File for keepalived
global_defs {
router_id lb01
}
vrrp_script check_ng {
script "/etc/keepalived/check_keepalived.sh"
interval 2
weight -5
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP # 主上也設置為備
interface ens33
virtual_router_id 51
priority 150
advert_int 1
nopreempt # 設置為不搶奪VIP
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.169.200
}
track_script {
check_ng
}
}
(2)、slave配置
! Configuration File for keepalived
global_defs {
router_id lb02
}
vrrp_script check_ng {
script "/etc/keepalived/check_keepalived.sh"
interval 2
weight -5
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 149
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1111
virtual_ipaddress {
192.168.169.200
}
track_script {
check_ng
}
}