==========
兩個問題:
在繼續(xù)講keepalived之前讓我們先思考兩個問題:
1.如果我們只有一臺調(diào)度器(lvs/Nginx),且它到后端real server集群的物理連接斷了(如網(wǎng)卡、光模塊、線纜等損壞),那該怎么應對?
2.如果我們只有一臺調(diào)度器(Nginx等軟件實現(xiàn)的),但是不巧的是Nginx進程被殺死了,那該怎么應對?
顯然以上都是單點故障,遇到單點故障很棘手,我們應該避免。一般來講網(wǎng)絡中關鍵節(jié)點都應該有至少兩個,所以我們的調(diào)度器也應該至少兩臺,組成高可用(HA)集群,如下圖:
圖中有兩臺調(diào)度器,正常情況下流量僅流經(jīng)director1,也就是director1 作為MASTER,director2 作為BACKUP。我們期望的是當MASTER的 eth1或 eth3 接口掛掉后,director2 能夠承接director1的所有流量。同時,如果direcotr1為Nginx反代(包括7層和偽34層),當Nginx進程掛掉后,也能完成流量切換。如何實現(xiàn)呢?用 keepalived。
在數(shù)通網(wǎng)絡中,我們通常使用VRRP來做默認網(wǎng)關的冗余,keepalived也是靠實現(xiàn)VRRP協(xié)議來完成流量從 director1 到director2 的倒換的。如果不熟悉VRRP,建議去某度文庫搜索下數(shù)通廠商的vrrp技術白皮書。理解了VRRP的工作原理后再來看 keepalived, so easy。(務必要先搞懂VRRP協(xié)議,廠商的vrrp技術白皮書已經(jīng)寫的很詳細了,如華為。)
言歸正傳,使用keepalived,以下3種情況下會觸發(fā)倒換(也就是圖中流量從 director1 切到 director2):
場景描述:圖中eth1和eth2起vrrp,且eth1被選舉為MASTER(準確地說MASTER和BACKUP都是針對接口說的,不過我們在一定場合下也稱路由器的狀態(tài)為MASTER/BACKUP)
1. director1 的eth1接口down掉,這很容易理解,物理接口down掉了,無法發(fā)送vrrp advertisement,作為BACKUP的director2這時候就會接替原來director1的角色,成為MASTER。
2. 我們可以讓 keepalived 進程在director1監(jiān)控eth3接口的狀態(tài),如果該接口down掉,則停止在eth1接口上發(fā)送vrrp advertisement,把MASTER角色讓個director2。這通過keepalived.conf中的track_interface命令配置來實現(xiàn)。
3. 我們可以讓 keepalived 執(zhí)行某腳本,該腳本周期性地檢測諸如進程等的設備狀態(tài)并返回code,keepalived根據(jù)腳本返回的code值調(diào)整(減小)director1發(fā)出的vrrp 通告中的priority至小于director2發(fā)出的通告中的priority,把MASTER角色讓給director2。這通過keepalived.conf中的track_script命令配置來實現(xiàn)。
好,現(xiàn)在給出幾個實例,我們通過如下兩個拓撲完成實驗驗證。
Note:如中rs1,rs2,rs3是3臺不同的物理機,只不過圖中畫在了一起。
實例一:
使用拓撲一,僅僅在 director1 和 director2 的左側接口起 vrrp,VRIP為192.168.10.131,物理接口分別為 192.168.10.128 和 192.168.10.135。設置 192.168.10.128所在借口有 higher priority,以使之被選舉為master,當master接口掛掉后,backup接口開始發(fā)送 vrrp advertisement,成為master。director 1 和 director2 的配置分別如下:
補充:組播地址 && VRID 要設置成一樣才會被認為是同一個 vrrp_instance
===== director1 配置 =====
global_defs {
notification_email {
root@localhost
}
notification_email_from root@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 6A //設置 router-id
vrrp_mcast_group4 224.0.0.18 //設置組播地址,同一個vrrp_instance中的組播地址一定要相同
}
vrrp_instance OUTSIDE_VI_1 { //創(chuàng)建一個instance,名字隨意(本地有效,報文不含之),但是建議主備同名
state MASTER //預設置角色,其實沒有大用,鹿死誰手還得比拼priority等
interface eth1 //指定加入instance的接口
virtual_router_id 1 //設置VRID,同一個vrrp_instance中的VRID一定要相同
priority 100 //本機在vrrp_instance中的優(yōu)先級,準確說應該是接口的優(yōu)先級
advert_int 1 //通告每隔1s發(fā)送一次
authentication { //通告驗證使用簡單驗證,僅vrrp 2.0支持,而keepalived就是實現(xiàn)的vrrp 2.0
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //vrrp的VRIP
}
}
===== director2 配置 =====
global_defs {
notification_email {
root@localhost
}
notification_email_from root@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 6B
vrrp_mcast_group4 224.0.0.18 //同一vrrp_instance的組播地址要一致
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6 //指定加入instance的接口
virtual_router_id 1 //同一vrrp_instance的VRID要一致
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
}
配置完成后,重啟keepalived。手動關閉 director1 的eth1,ip link set eth1 down,然后在vmnet1 上抓包,發(fā)現(xiàn)director2 的eth6 開始發(fā)送vrrp advertisement,也就是說director2的eth6已經(jīng)是MASTER,切換完成。實驗完成后再恢復eth1到up狀態(tài)。
實例二:
使用拓撲一,在實驗一的基礎上,驗證當MASTER監(jiān)控的某借口down掉后,發(fā)生角色切換。也就是驗證 track_interface的作用。
===== director1 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_interface { //僅在實驗一的基礎上添加這個配置段
eth0
}
}
===== director2 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_interface { //僅在實驗一的基礎上添加這個配置段
eth2
}
}
配置完成后,重啟keepalived。手動關閉 director1 的eth0,ip link set eth0 down,然后在vmnet1 上抓包,發(fā)現(xiàn)director2 的eth6 開始發(fā)送vrrp advertisement,也就是說director2的eth6已經(jīng)是MASTER,切換完成。實驗完畢后恢復eth0到up狀態(tài)。
實例三:
使用拓撲一,在實驗一的基礎上,驗證 track_script 的作用,即周期性地執(zhí)行腳本,然后根據(jù)腳本的返回值調(diào)整weight,從而達到MASTER/BACKUP角色切換的目的。
===== director1 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_script my_test { //定義如何追蹤腳本,及根據(jù)腳本返回代碼執(zhí)行何種動作
script "[ -f /tmp/hehe ]" //檢查shell腳本表達式的值,這里雙引號內(nèi)可以是外置腳本(要有exec權限)
interval 1 //多長時間執(zhí)行一次腳本并檢查其返回值
weight -2 //如果腳本執(zhí)行失敗后的動作(weight-2,僅執(zhí)行一次);如果后續(xù)檢查發(fā)現(xiàn)腳本執(zhí)行成功則執(zhí)行反動作(把減去的2加回來)
fall 3 //返回幾次非0,才認為失敗,執(zhí)行 weight-2的動作
rise 1 //返回幾次0,才認為成功,執(zhí)行weight-2的反動作,即當前weight+2
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script { //追蹤my_test中指明的腳本,my_test要在vrrp_instance前定義好
my_test
}
}
===== director2 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_script my_test { //本次實驗這一段不加也可,但是建議主備配置一致
script "[ -f /tmp/hehe ]"
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script { //本次實驗這一段不加也可,但是建議主備配置一致
my_test
}
}
配置完成后,現(xiàn)在touch一個/tmp/hehe,重啟keepalived,在vmnet1上抓包。這時候由于 [ -f /tmp/hehe ] 返回值為0,也就是 weight 不變化,所以director1還是MASTER。這時候 rm -f /tmp/hehe,當director1 檢查腳本表達式時,發(fā)現(xiàn)3此都是非0,所以weight-2,由100變成了98,小于director2的priority99,讓出了MASTER。觀察抓到的報文,發(fā)現(xiàn)director2 確實已經(jīng)再發(fā) vrrp advertisement了。
這里只是拿 [ -f /tmp/hehe ]做個例子而已,生產(chǎn)環(huán)境中一般都是檢查某個進程是否存活,如 [ killall -0 nginx ] 若 nginx 進程存在,則返回0,weight不變。若進程已死,則weight變化,一般是減小以讓出MASTER。 當然,引用外部具有可執(zhí)行權限的腳本也是ok的。
實例四:
使用如下拓撲二,我們實現(xiàn)一個 lvs-dr的高可用集群(集群這名總是感覺高達上,其實也就是director1和director2組成一個lvs-dr組)
===== director1 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //即做VRIP,也做lvs概念中的VIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind DR //定義lvs類型為dr
persistence_timeout 0
protocol TCP
real_server 192.168.10.140 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.10.141 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.10.142 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
===== director2 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind DR //定義lvs類型為dr
persistence_timeout 0
protocol TCP
real_server 192.168.10.140 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.10.141 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.10.142 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
===== real server 的配置 =====
rs1:啟動httpd,通過 ip add add 192.168.10.131 dev lo 給real server添加VIP
rs2:啟動httpd,通過 ip add add 192.168.10.131 dev lo 給real server添加VIP
rs3:啟動httpd,通過 ip add add 192.168.10.131 dev lo 給real server添加VIP
此時的高可用就體現(xiàn)在 director1 的master接口down掉后的流量切換。
實例五:
使用拓撲一,我們實現(xiàn)一個 lvs-nat 的高可用集群。為了方便理解配置,再貼一遍圖,請對照拓撲理解配置。
先思考個問題,使用lvs-nat的話,real server把默認網(wǎng)關指向誰?在只有一臺director的時候這個問題是不存在的,但是現(xiàn)在兩臺director,怎么辦?還是用vrrp,把兩臺director右側的interface加入另一個vrrp_instance,然后讓real server把默認網(wǎng)關只想這個vrrp_instance的VRIP就可以了。這又引入了另一個問題,director上保存著nat表,所以同一條tcp流的上下行數(shù)據(jù)要經(jīng)過同一臺director(主director,也就是director1)。也就是兩個vrrp_instance的master接口要在同一臺director上,一個vrrp_instance發(fā)生了接口角色切換,另一個vrrp_instance 也要發(fā)生切換,如何實現(xiàn)?用vrrp同步組。
===== director1 配置 =====
lobal_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_sync_group VG_1 { //定義vrrp同步組,把兩個vrrp_instance加入改組。當一個vrrp_instance接口切換時,另一個同步切換
group {
INSIDE_VI_1 //加入同步組的vrrp_instance
OUTSIDE_VI_1
}
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100 //左側的vrrp_instance中,通過設置優(yōu)先級讓director1上的接口被選舉為master
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //左側vrrp_instance的VRIP
}
}
vrrp_instance INSIDE_VI_1 {
state MASTER
interface eth0
virtual_router_id 2
priority 100 //左側的vrrp_instance中,通過設置優(yōu)先級讓director1上的接口被選舉為master
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.20.135 //右側vrrp_instance的VRIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind NAT //實現(xiàn)lvs-nat
persistence_timeout 0 # during which time, request is distributed to the same real server
protocol TCP
real_server 192.168.20.130 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.20.131 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
===== director2 配置 =====
lobal_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_sync_group VG_1 {
group {
INSIDE_VI_1
OUTSIDE_VI_1
}
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99 //director2作為backup
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //左側vrrp_instance的VRIP
}
}
vrrp_instance INSIDE_VI_1 {
state BACKUP
interface eth2
virtual_router_id 2
priority 99 //director2作為backup
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.20.135 //右側vrrp_instance的VRIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind NAT
persistence_timeout 0 #dechao: during which time the request is distributed to same real server
protocol TCP
real_server 192.168.20.130 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.20.131 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
2臺director 上 echo 1 > /proc/sys/net/ipv4/ip_forward 設置 ip_forward=1
3臺real server上添加默認路由 ip route add default via 192.168.20.135,同時啟動httpd,建議給出不同頁面。這時候通過client訪問 192.168.10.131,請求就可以被輪詢調(diào)度到后端不同的real server上了。
這個實例的高可用體現(xiàn)在vrrp的同步倒換上,我們手動關閉director1上右側端口,發(fā)現(xiàn)右側的vrrp_instance角色切換,同時左側vrrp_instance的角色也發(fā)生切換。但是啟用vrrp同步后,發(fā)現(xiàn)和track_script沖突,track_script不管用了。通過日志可以看出來,文章最后會講到keepalived日志相關的東西。
實例六:
使用如下拓撲,我們實現(xiàn)一個Nginx的調(diào)度器高可用集群。director1平時作為master,當他上面的Nginx掛掉后,讓出master角色給director2,同時執(zhí)行一些動作,如發(fā)郵件給管理員。
說明:由于Nginx作反代調(diào)度器的時候,無論偽34層還是7層都可以認為是full-nat,也就是SNAT+DNAT,所以兩臺director的右側接口沒有起vrrp。
===== director1 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_script my_test {
script "killall -0 nginx"
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script {
my_test
}
notify_master "/tmp/2master.sh"
notify_backup "/tmp/2backup.sh"
}
===== director2 配置 =====
global_defs {
//節(jié)約版面,這部分內(nèi)容請自己補上
}
vrrp_script my_test {
script "killall -0 nginx"
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script {
my_test
}
notify_master "/tmp/2master.sh"
notify_backup "/tmp/2backup.sh"
}
director1 和 director2 上都運行Nginx,作為7層代理或者偽34層代理。此時director1被選舉為master。
/tmp/2master.sh 和 /tmp/2backup.sh 要有執(zhí)行權限,在本實驗中腳本內(nèi)容如下:
/tmp/2master.sh 內(nèi)容:
#!/bin/bash
echo 2master@`date` >> /tmp/keepalived.state
/tmp/2backup.sh 內(nèi)容:
#!/bin/bash
echo 2backup@`date` >> /tmp/keepalived.state
這里只是舉一個簡單的例子,角色轉換時執(zhí)行的腳本可以是任意的。
現(xiàn)在我們手動停掉director1上的Nginx,這時候director1的vrrp weight由100-2變?yōu)?8,讓出了master,我們可以抓包看到此時director2開始發(fā)送vrrp advertisement。同時可以 cat /tmp/keepalived.state ,發(fā)現(xiàn)確實角色發(fā)生了轉化。
===============
keepalived日志相關:
CentOS6上keepalived的日志默認記錄在/var/log/messages里面,不好看,通過如下方式定向到一個文件。
/etc/sysconfig/keepalived
編輯:KEEPALIVED_OPTIONS="-D -d -S 3"
/etc/rsyslog.conf
編輯:local3.* /var/log/keepalived.log
service rsyslog restart
service keepalived restart
Oh,my god。總算說完了。