本系列會分析OpenStack 的高可用性(HA)概念和解決方案:
(2)Neutron L3 Agent HA - VRRP (虛擬路由冗余協議)
(3)Neutron L3 Agent HA - DVR (分布式虛機路由器)
(4)Pacemaker 和 OpenStack Resource Agent (RA)
(5)RabbitMQ HA
(6)MySQL HA
1. 基礎知識
1.1 虛擬路由冗余協議 - VRRP
1.1.1 概念
路由器是整個網絡的核心。一個網絡內的所有主機往往都設置一條缺省路由,這樣,主機發出的目的地址不在本網段的報文將被通過缺省路由發往路由器,從而實現了主機與外部網絡的通信。在通常只使用單路由器來承擔缺省路由的情況下,當該路由器壞掉后,本網段內所有以它為缺省路由下一跳的主機將斷掉與外部的通信。可見,在使用單路由器的情況下,如果路由器發生致命性的故障,將導致本地網絡的癱瘓,如果是骨干路由器,影響的范圍將更大,所造成的損失也是難以估計的。因此,對路由器采用熱備份是提高網絡可靠性的必然選擇。
1.1.2 VRRP 怎么解決問題

- Device A 和 B 組成一個 VRRP 組,它的虛擬 IP(VIP) 為 10.1.1.10/24。其中,通過選舉機制,A 是 Master Router,B 是 Backup Router。一個 VRRP 組內可以由多個設備,但是只有一個是 Master 設備。
- Device A 和 B 可以由自己的 IP 地址,VIP 可以和其中的某 IP 相同,也可以不同。
- 當前,Router A 作為 Master router 向局域網內的機器提供路由服務。
- 當前,Router B 作為 Backup router。它的任務是周期性地接受 A 發出的心跳。在規定的時間段內,如果都沒有收到 A 發出的心跳,則啟動一個選舉過程,重新選出 Master。
- 局域網內的機器將虛擬路由器當作默認網關,它們僅僅知道這個虛擬路由器的IP 地址 10.1.1.10,而並不知道具體的 Master 路由器的 IP 地址以及 Backup 路由器的IP 地址。它們將自己的缺省路由下一跳地址設置為10.1.1.10。於是,網絡內的主機就通過這個虛擬的路由器來與其它網絡進行通信。如果 Master 路由器壞掉,Backup 路由器將會通過選舉策略選出一個新的 Master 路由器,繼續向網絡內的主機提供路由服務。從而實現網絡內的主機不間斷地與外部網絡進行通信。
可以看出:
- VRRP 是一種路由器選擇協議,它可以把一個虛擬路由器的責任動態分配到 VRRP 組內的多個路由器中的一台。控制虛擬路由器 IP 地址的 VRRP 路由器稱為主路由器,它負責轉發數據包到這些虛擬 IP 地址。一旦主路由器不可用,這種選擇過程就提供了動態的故障轉移機制,這就虛擬允許路由器的 IP 地址可以作為終端主機的默認第一跳路由器。使用 VRRP 的好處是有更高的默認路徑的可用性而無需在每個終端主機上配置動態路由或路由發現協議。
- VRRP 包封裝在 IP 包中發送,用於 VRRP 組內設備的互相通信比如保持心跳等。它只定義了一種報文即 VRRP 報文,這是一種組播報文,由 Master 路由器定時發出來通告組內的路由器它的存在。
- VRRP 中定義了三種狀態模型,初始狀態 Initialize,活動狀態 Master 和備份狀態 Backup,其中只有活動狀態的交換機可以為到虛擬IP地址的的轉發請求提供服務。
幾個概念:
- VIP:VRRP 使用兩種類型的 VIP:一種 VIP 參與 VRRP 通告,使用配置項 virtual_ipaddress 指定;另一種 VIP 不參與 VRRP 通告,使用配置項 virtual_ipaddress_excluded 指定。
- 優先級(Priority):虛擬路由器中VRRP設備的優先級。虛擬路由器根據優先級選舉出Master設備和Backup設備。
- 搶占模式:在搶占模式下,如果 Backup 設備的優先級比當前 Master 設備的優先級高,則主動將自己切換成 Master。
- 非搶占模式:在非搶占模式下,只要 Master 設備沒有出現故障,Backup 設備即使隨后被配置了更高的優先級也不會成為Master設備。
-
VRRP協議報文:封裝在IP報文中,發送到分配給 VRRP 的 IP 組播地址。在IP報文頭中,源地址為發送報文接口的主 IP 地址(不是虛擬IP地址),目的地址是224.0.0.18,TTL是255,協議號是112。目前,VRRP協議包括兩個版本:VRRPv2和VRRPv3,VRRPv2僅適用於IPv4網路,VRRPv3適用於IPv4和IPv6兩種網絡。
- VRRP 節點三種狀態:初始狀態(Initialize)、活動狀態(Master)、備份狀態(Backup)。其中,只有處於Master狀態的設備才可以轉發那些發送到虛擬IP地址的報文。
Master 路由器的選舉過程:
VRRP根據優先級來確定虛擬路由器中每台路由器的角色(Master 路由器或 Backup 路由器)。優先級越高,則越有可能成為 Master 路由器。
初始創建的 VRRP 交換機工作在 Initialize 狀態,收到接口 Up 的消息后,若此路由器的優先級小於255,則會先切換至 Backup 狀態,待 Master_Down_Interval 定時器超時后再切換至 Master 狀態。首先切換至 Master 狀態的 VRRP 路由器通過 VRRP 通告報文的交互獲知虛擬交換機中其他成員的優先級,進行Master的選舉:
-
- 如果 VRRP 報文中 Master 路由器的優先級高於或等於自己的優先級,則 Backup 路由器保持 Backup 狀態;
- 如果 VRRP 報文中 Master 路由器的優先級低於自己的優先級,采用搶占方式的 Backup 交換機將切換至 Master 狀態,采用非搶占方式的Backup 路由器仍保持Backup狀態。
- 如果多個 VRRP 路由器同時切換到 Master 狀態,通過 VRRP 通告報文的交互進行協商后,優先級較低的 VRRP 路由器將切換成 Backup 狀態,優先級最高的 VRRP 路由器成為最終的 Master設備;優先級相同時,VRRP 路由器上 VRRP 備份組所在接口主 IP 地址較大的成為 Master 設備。
- 如果創建的VRRP 路由器為IP地址擁有者,收到接口 Up 的消息后,將會直接切換至Master狀態。
Master交換機狀態的通告:
- Master 路由器周期性地發送 VRRP 通告報文,在 VRRP 備份組中公布其配置信息(優先級等)和工作狀況。Backup 路由器通過接收到 VRRP 報文的情況來判斷Master 路由器是否工作正常。
- 當 Master 路由器主動放棄 Master 地位(如 Master 路由器退出備份組)時,會發送優先級為0的通告報文,用來使 Backup 路由器快速切換成 Master 路由器,而不用等到 Master_Down_Interval 定時器超時。
- 當 Master 路由器發生網絡故障而不能發送通告報文的時候,Backup 路由器並不能立即知道其工作狀況。等到 Master_Down_Interval 定時器超時后,才會認為 Master 路由器無法正常工作,從而將狀態切換為 Master。
目前,主流的硬件路由器設備都實現了該協議: 這篇文章 介紹了 Broadcom 路由器上 VRRP 的配置,這篇文章 介紹了一個 Cisco 路由器 VRRP 的配置例子,這篇文章 是華為的 VRRP 白皮書。
它的優勢:
- 操作簡單:它不需要改變組網情況,也不需要在主機上做任何配置,只需要在相關路由器上配置極少的幾條命令,就能實現下一跳網關的備份,並且不會給主機帶來任何負擔。和其他方法比較起來,VRRP更加能夠滿足用戶的需求。
- 簡化網絡管理:在具有多播或廣播能力的局域網(如以太網)中,借助 VRRP 能在某台設備出現故障時仍然提供高可靠的缺省鏈路,有效避免單一鏈路發生故障后網絡中斷的問題,而無需修改動態路由協議、路由發現協議等配置信息,也無需修改主機的默認網關配置。
- 適應性強:VRRP報文封裝在IP報文中,支持各種上層協議。
- 網絡開銷小:VRRP只定義了一種報文——VRRP通告報文,並且只有處於Master狀態的路由器可以發送VRRP報文。
其他參考文章:(1)(2)(3)。更多內容,參見協議文本,以及 VRRP 技術白皮書 - Huawei。
1.2 Keepalived
1.2.1 概念
Keepalived 是一個用 C 語言編寫的路由軟件,其主要目的是向 Linux 系統和基礎設施提供簡單可靠的工具來實現負載均衡(Load balancing)和高可用(HA)。其中,
- 負載均衡框架基於 Linux Virtual Server (IPVS) 內核模塊來提供網絡四層的負載均衡,它實現了一組檢查器(Checkers)來根據它們的健康狀態動態地和自適應地維護和管理被均衡的服務器池。
- 高可用(HA)是使用 VRRP 來實現的,Keepalived 是 VRRP 的一個非常好的開源實現。它是一個基於 VRRP 協議來實現的 WEB 服務高可用方案,可以利用其來避免單點故障。一個 WEB 服務至少會由兩台台物理服務器運行 Keepalived,一台為主服務器(MASTER),一台為備份服務器(BACKUP),但是對外表現為一個虛擬 IP,主服務器會發送特定的消息給備份服務器,當備份服務器收不到這個消息的時候,即主服務器宕機的時候,備份服務器就會接管虛擬 IP,繼續提供服務,從而保證了高可用性。
1.2.2 軟件架構和功能
(本部分內容來自 Keepalived 官網)
Keepalived 使用三個進程,其中 Watchdog 是控制進程,VRRP Stack and Checkers 是它的兩個子進程。
- Watchdog 通過心跳機制來確保子進程處於運行狀態。
- Checkers:負責真實服務器的健康檢測,用於負載均衡。
- VRRP Stack:實現 VRRP 協議,提供 HA。
主要模塊:
- System call : 用於啟動用戶添加的腳本。它可以被 Checkers 和 VRRP Stack 使用。它向 VRRP 框架提供了一種能力,即在 VRRP 協議狀態變化時可以運行用戶指定的腳本。
- SMTP : 使得 Keepalive 能夠使用郵件來通知管理員。
- NETLINK Interface: 用於 VRRP。Keepalive 通過它來設置或者刪除 VIP。
VRRP Stack 模塊:
VRRP Stack 是獨立於 LVS的,它可以被單獨使用。它主要提供以下功能:
- Failover:實現 VRRP協議的核心功能,提供故障切換能力。
- VRRP Instance synchronization:VRRP Group (組)之間的同步,比如保證兩個 VRRP 組直接保持同樣狀態。
- VRRP 廣播:Master 實例通知 Backup 實例。
- System call: 在 VRRP 狀態變換時調用外部的腳本或者程序。
它包括以下組件:
- Netlink:提供 VIP 操作。
- Multicast:用於發送 VRRP 協議廣播。
- SYSLOG:所有 daemon 的通知消息都會通過該組件寫入日志。
用戶可以使用配置文件 /etc/keepalived/keepalived.conf 來配置 Keepalived。本部分主要講講 keepalived 的概念,使用會在下文談到。更多內容,參見官網。
1.2.3 keepalived 切換(failover)
在非搶占模式下,只有當當前master出現故障了時,才會通過重新選舉才生新的 master。
在搶占模式下,下面三個條件任何一個條件被滿足了的情況下會發生切換:
- master 節點因為健康檢查失敗將其weight 降低至低於一個 slave 節點。
- 一個 slave 節點的weight 被配置為比當前 master 的weight 高。
- 當前master 節點不發出心跳信息,比如當前節點宕機了,進程退出了,狀態進入 FAULT 了(一旦進入 FAULT 狀態,原 master 就會釋放 VIP,停止發送心跳消息)等等。
2. Neutron Juno 版本中 VRRP 的實現
在 OpenStack HA (1)概述 一文中談到,Neutron L3 Agent 在 Juno 版本中添加了兩種 HA 機制的實現:VRRP 機制(blueprint, Spec,Wiki)和 DVR 機制。VRRP 機制就是借助實現 VRRP 協議的軟件,來保證 Neutron L3 Agent 的高可用性。這需要多方面的實現:
- 創建 HA Router 時,創建多個 router 實例部署到不同物理服務器上的 L3 Agent 中。這個實現需要修改 Neutron schedulers。
- 借助實現 VRRP 的軟件,保證多個 L3 Agent 的 HA,即其中一個是 Master,其他是 Backup。由 Master 向虛機提供路由服務。在 Master 故障時,某個 Standby 被選舉為新的 Master,接替之前的 Master。目前的 Neutron VRRP 實現使用的是 Keepalived。
- 路由器故障切換時保持已建立的連接。Neutron 會使用 conntrack 來實現該功能。
2.1 創建 HA Router
neutron server 上:
(1)類似普通的 router,首先執行 DB 操作在 DB 中創建一個 router。
(2)檢查 router 所在的 tenant 中是否存在 HA Network,如果不存在的話,則創建名稱為 “HA network tenant”+ tenant_id 的 network,以及該 network 中的名字為 “HA subnet tenant” + tenant_id, cidr 為 cfg.CONF.l3_ha_net_cidr 的 subnet。其余的參數皆為默認或者null。
s1@controller:~$ neutron net-show bc5501ed-56ee-4d32-974b-02279fb35c32 +---------------------------+----------------------------------------------------+ | Field | Value | +---------------------------+----------------------------------------------------+ | admin_state_up | True | | id | bc5501ed-56ee-4d32-974b-02279fb35c32 | | name | HA network tenant 74c8ada23a3449f888d9e19b76d13aab | | provider:network_type | gre | | provider:physical_network | | | provider:segmentation_id | 1 | | router:external | False | | shared | False | | status | ACTIVE | | subnets | 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b | | tenant_id | | +---------------------------+----------------------------------------------------+ s1@controller:~$ neutron subnet-show 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b +-------------------+------------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------------+ | allocation_pools | {"start": "169.254.192.1", "end": "169.254.255.254"} | | cidr | 169.254.192.0/18 | | dns_nameservers | | | enable_dhcp | False | | gateway_ip | | | host_routes | | | id | 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | HA subnet tenant 74c8ada23a3449f888d9e19b76d13aab | | network_id | bc5501ed-56ee-4d32-974b-02279fb35c32 | | tenant_id | | +-------------------+------------------------------------------------------+
注意,當 tenant 最后一個 HA Router 被刪除的時候,該 HA network/subnet 不會被自動刪除,這里有個 bug。看起來還沒有被 fixed,目前只能手動刪除。
(3)分配一個可用的 VRRP ID,該 ID 為 1 - 255 之間。
(4)創建與部署的 router 實例相同數量的 port 作為 HA Interfaces,每個 L3 Agent 上使用一個。
{'tenant_id': '', 'network_id': network_id, 'fixed_ips': attributes.ATTR_NOT_SPECIFIED, 'mac_address': attributes.ATTR_NOT_SPECIFIED, 'admin_state_up': True, 'device_id': router_id, 'device_owner': constants.DEVICE_OWNER_ROUTER_HA_INTF, 'name': constants.HA_PORT_NAME % tenant_id}})
比如需要部署的 router 的實例數目為2 的情況下,會分配 2 個port,分別用於各 router network namespace 中 keepalived 之間的通信:
s1@controller:~$ neutron port-list | grep 169.254.192
| 20894a79-b668-44d9-bf42-ef6bc2981960 | HA port tenant 74c8ada23a3449f888d9e19b76d13aab | fa:16:3e:09:29:d4 | {"subnet_id": "2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b", "ip_address": "169.254.192.2"} |
| a8a3a49f-c175-4ea6-9342-240ef646e99c | HA port tenant 74c8ada23a3449f888d9e19b76d13aab | fa:16:3e:84:24:7f | {"subnet_id": "2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b", "ip_address": "169.254.192.1"} |
(5)schedule router。具體參見 2.1.2 章節.
(6)向待部署 router 實例的每個 L3 Agent 發送 RPC Cast “routers_updated” 消息
每個 L3 Agent 上:
(1)循環運行的 routers_updated 方法將待處理的每個 router 加入到內部 queue 中,等待被 L3 Agent 啟動的循環線程處理
(2)處理過程可以參考我 這篇文章 的 3.1 部分
(3)在完成常規的 router 添加操作后,調用 process_ha_router_added 執行 HA 配置部分
(3.1)將 HA Port plug 到 OVS 的 br-int 上,並設置 interface 的三層屬性,比如 IP, gateway, route 等。
Bridge br-int Port "ha-20894a79-b6" tag: 7 Interface "ha-20894a79-b6" type: internal
(3.2)准備 keepalived
(3.2.1)生成一個 keepalived 實例。該實例的初始狀態為 Backup,非搶占式,VRRP 通告間隔2秒,優先級為 50。
instance = keepalived.KeepalivedInstance('BACKUP', interface_name, ri.ha_vr_id, ha_port_cidr, nopreempt=True, advert_int=self.conf.ha_vrrp_advert_int, priority=ri.ha_priority)
(3.2.2)通過上面第三步分配的 VRRP ID,定位到 VRRP Group;然后將 3.2.1 中的 keepalived instance 加入到 該 group 。
(3.2.3)將 group 和 instance 保存到 keepalived config 中。
(4)調用 _add_keepalived_notifiers 方法來添加 neutron 需要的 keepalived notifiers。具體見 2.1.3 章節。
(5)調用 process_router 方法繼續處理該 route。對於HA Router,會首次啟動新的或者重啟已有的 keepalived 進程。
(5.1)生成 keepalived 配置。配置文件的位置可以由配置項 vrrp_confs 指定,其默認值為 “$state_path/vrrp”。還可以使用配置項 ha_vrrp_auth_type (默認 'PASS') 和 ha_vrrp_auth_password (默認 none)指定 VRRP 組播的認證方法,以及 使用配置項 ha_vrrp_advert_int (默認值 2) 指定 VRRP 廣播發送的時間間隔。
vrrp_sync_group VG_1 { # VRRP ID 為 1
group {
VR_1
}
notify_master "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh" notify_backup "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh" notify_fault "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_fault.sh" } vrrp_instance VR_1 { state BACKUP interface ha-20894a79-b6 # 實例綁定的網卡,因為在配置虛擬 IP 的時候必須是在已有的網卡上添加的 virtual_router_id 1 # VRRP ID,每個 tenant HA Router 一個 ID priority 50 # 該 HA Router instance 的 優先級,根據這個選舉 Master nopreempt # 使用非搶占模式 advert_int 2 # VRRP 廣播發送間隔,單位秒 track_interface { # 跟蹤接口,設置額外的監控 ha-20894a79-b6 } virtual_ipaddress { # 該 VRRP 的 VIP,格式為 HA Network CIDR 最后一位被 VID 替換,該例子中,CIDR 為 “169.254.0.1/24”, VID 為 1 169.254.0.1/24 dev ha-20894a79-b6 } }
keepalived 各種配置說明:
配置 | 說明 | neutron 用法 |
notify master/backup/fault | 表示當該 VRRP 節點切換到 master/backup/fault 狀態時,要執行的腳本 | 見本文 2.1.3 章節 |
state | state 指定 instance(Initial) 的初始狀態,就是說在配置好后,這台服務器的初始狀態就是這里指定的,但這里指定的不算,還是得要通過競選通過優先級來確定,里如果這里設置為master,但如若他的優先級不及另外一台,那么這台在發送通告時,會發送自己的優先級,另外一台發現優先級不如自己的高,那么他會就回搶占為master | neutron 將所有 keeplived 實例的初始狀態都設置為 BACKUP,而且都使用默認優先級 50。這樣實際上,一般來說,哪個 L3 Agent 先起來,它的 keepalived 實例就是 Master 了。 |
interface | VRRP 實例綁定的網卡,在此網卡上添加 VIP 作為 secondary IP | HA Interface |
dont track primary | 忽略 VRRP 的 interface 錯誤 | 不使用 |
track interface | 跟蹤接口,設置額外的監控,里面任意一塊網卡出現問題,都會進入故障(FAULT)狀態,例如,用nginx做均衡器的時候,內網必須正常工作,如果內網出問題了,這個均衡器也就無法運作了,所以必須對內外網同時做健康檢查 | neutron 只跟蹤 HA Interface,其實它還是可以跟蹤 network namespace 的其他 interface,比如 qr,qg 等,是吧? |
mcast src ip | 發送多播數據包時的源IP地址,這里注意了,這里實際上就是在那個地址上發送VRRP通告,這個非常重要,一定要選擇穩定的網卡端口來發送,這里相當於heartbeat的心跳端口,如果沒有設置那么就用默認的綁定的網卡的IP,也就是interface指定的IP地址 | 不設置,默認使用 HA Interface IP |
garp master delay | 在切換到master狀態后,延遲進行免費的ARP(gratuitous ARP)請求 | 未指定 |
virtual router id | 這里設置VRID,這里非常重要,相同的 VRID 為一個組,他將決定多播的MAC地址 | 創建 HA Router時從 neutron DB 分配,從 1 開始 |
priority 100 | 設置本節點的優先級,優先級高的為master | 默認為 50 |
advert int | 檢查間隔,默認為1秒 | 配置項 ha_vrrp_advert_int (默認值 2) |
virtual ipaddress | 這里設置的就是VIP,也就是虛擬 IP 地址,他隨着 state 的變化而增加或者刪除,當 state 為master的時候就添加,當 state 為 backup 的時候刪除,這里主要是有優先級來決定的,和 state 設置的值沒有多大關系,這里可以設置多個 IP 地址。這篇文章 談到其數目限制為 20,超過該數目的 IP 可以放到 virtual_ipaddress_excluded。 | VIP。格式為 HA Network CIDR 最后一位被 VID 替換, |
virtual routes | 原理和 virtual ipaddress 一樣,只不過這里是增加和刪除路由 | 不使用 |
authentication auth type auth pass |
認證方式,類型,密碼 | 配置項 ha_vrrp_auth_type (默認 'PASS') 和 ha_vrrp_auth_password (默認 none) |
nopreempt | 設置不搶占,這里只能設置在state為backup的節點上,而且這個節點的優先級必須別另外的高 | 設置,使用非搶占模式。這樣,只要 Master 沒壞,即使 Backup 的優先級更高,也不會觸發選舉 |
preempt delay | 搶占延遲 | 不使用 |
virtual_ipaddress_excluded | 需要 keepalived 維護但是不會放在VRRP包中傳輸的IP。也就是說當某個router被選舉為master的時候其會的將qg口設置對應的IP。而在變為 backup 時,會將相應的 IP 刪除。 | VIP。Neutron 將 浮動 IP,internal/external port IP 等保存在這里。這樣,當一個 node 變為 master 的時候,這些 IP 地址會生效;當一個 node 變為 backup 的時候,這些 IP 地址會被刪除。 |
virtual_routes | 在某個router設置為 master 的時候其會的設置對應的 route。相反的,如果一個router變成了backup,那么上面這些操作會反過來做一遍。 | 每個 external port 對應一條路由,在 VRRP 狀態變化時增加或者刪除 |
(資料來源。Neutron 所使用的 keepalived config 模板在 這里)
(5.2)在 route network namespace 運行命令 'keepalived -P -f config_path -p pid_file -r pid_file-vrrp' 啟動 keepalived 進程。
root 31510 1 0 10:42 ? 00:00:00 keepalived -P -f /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf -p /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid -r /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid-vrrp root 31512 31510 0 10:42 ? 00:00:00 keepalived -P -f /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf -p /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid -r /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid-vrrp
根據上面基礎知識部分的介紹,第一個進程(進程號 31510)是 Watchdog 進程,第二個進程(進程號 31512)是 VRRP Stack 進程。
(6) Neutron 中,keepalived 進程啟動后,初始狀態都是 BACKUP。在它指定時間沒收到 Master 的廣播后,它自己切換至 Master 狀態,然后通過 VRRP 通告報文的交互獲知虛擬路由器組中其他成員的優先級,進行 Master 的選舉:
- 如果 VRRP 報文中 Master 路由器的優先級高於或等於自己的優先級,則 Backup 交換機保持 Backup 狀態。顯然,Neutron 中的 keepalived 都是同樣的優先級,它們會各自保持自己的 BACKUP 狀態。
- 如果 VRRP 報文中 Master 路由器的優先級低於自己的優先級,因為 neutron 中 keepalived 采用非搶占模式,Backup交換機仍保持 Backup 狀態。
可見,neutron 中,哪個 L3 Agent namespace 中的 keepalived 進程先啟動並先發出 VRRP 通告,則它會成為 Master,直到它 down 掉才會由別的節點接替。在狀態變化時,會調用指定的腳本。詳細見 2.1.3 部分。狀態變化完成后,會達到下面這種效果(Juno 中還沒有實現 conntrack):
(7)啟動后狀態轉換過程
Aug 1 18:42:32 network Keepalived[31509]: Starting Keepalived v1.2.7 (08/14,2013) #進程啟動 Aug 1 18:42:32 network Keepalived[31510]: Starting VRRP child process, pid=31512 # VRRP Stack 進程啟動 Aug 1 18:42:32 network Keepalived_vrrp[31512]: Interface queue is empty Aug 1 18:42:32 network Keepalived_vrrp[31512]: Registering Kernel netlink reflector Aug 1 18:42:32 network Keepalived_vrrp[31512]: Registering Kernel netlink command channel Aug 1 18:42:32 network Keepalived_vrrp[31512]: Registering gratuitous ARP shared channel Aug 1 18:42:32 network Keepalived_vrrp[31512]: Initializing ipvs 2.6 Aug 1 18:42:32 network Keepalived_vrrp[31512]: Opening file '/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf'. #導入配置文件 Aug 1 18:42:32 network Keepalived_vrrp[31512]: Configuration is using : 64796 Bytes Aug 1 18:42:32 network Keepalived_vrrp[31512]: Using LinkWatch kernel netlink reflector... Aug 1 18:42:32 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Entering BACKUP STATE #根據初始配置,首先進入 BACKUP 狀態 Aug 1 18:42:32 network Keepalived_vrrp[31512]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh #腳本調用 Aug 1 18:42:39 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Transition to MASTER STATE #第一次選舉后進入 Master 狀態 Aug 1 18:42:39 network Keepalived_vrrp[31512]: VRRP_Group(VG_1) Syncing instances to MASTER state Aug 1 18:42:39 network Keepalived_vrrp[31512]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh #腳本調用 Aug 1 18:42:41 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Entering MASTER STATE
(8)VRRP 心跳
下面的 log 能看出來,Master 每隔兩秒鍾,向 組播地址 224.0.0.18/01:00:5e:00:00:12 發出長度為 54 bytes 的一個心跳:
ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb tcpdump -envi ha-20894a79-b6 -vvv
11:18:10.969378 fa:16:3e:09:29:d4 > 01:00:5e:00:00:12, ethertype IPv4 (0x0800), length 54: (tos 0xc0, ttl 255, id 1066, offset 0, flags [none], proto VRRP (112), length 40) 169.254.192.2 > 224.0.0.18: vrrp 169.254.192.2 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20, addrs: 169.254.0.1 11:18:12.971101 fa:16:3e:09:29:d4 > 01:00:5e:00:00:12, ethertype IPv4 (0x0800), length 54: (tos 0xc0, ttl 255, id 1067, offset 0, flags [none], proto VRRP (112), length 40) 169.254.192.2 > 224.0.0.18: vrrp 169.254.192.2 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20, addrs: 169.254.0.1
2.2 HA Router 調度(scheduling)
Neutron L3 Agent 的調度包括三個層面:
- 普通(Legacy)L3 Agent 調度:在存在多個 L3 Agent 的時候,通過隨機或者最小 router 數算法找到一個 L3 Agent,將該 Router 部署在上面。
- 分布式(DVR)L3 Agent 調度:將 Router 分布到所有計算節點和網絡節點。
- 高可用(HA) L3 Agent 調度:見下文
關於 HA L3 Agent 的調度,這部分的實現代碼在 這里。新的代碼修改了已有的 scheduler 算法,使得它們可以支持 HA Router,但是目前不能同時支持 DVR 和 HA Router。主要代碼在 /n eutron/scheduler/l3_agent_scheduler.py。該實現在 Neutron server 配置文件中添加了如下的配置項:
neutron.conf: l3_ha = True #由管理做的全局配置,創建的所有 router 均為 HA Router max_l3_agents_per_router = 4 #每個 HA router 最多被部署到若干個 L3 Agent 上 min_l3_agents_per_router = 2 #每個 HA Router 最少被部署到若干個 L3 Agent 上
該文件:
(1)檢查可用的 L3 Agent 數目(candidates)是否超過 conf.min_l3_agents_per_router 。
(2)確定部署的 router 數目,取 min(candidates,conf.max_l3_agents_per_router )。
(3)實現兩個 scheduler 類:ChanceScheduler - 隨機地在可用的 L3 Agent 上部署 Router;LeastRoutersScheduler - 將 router 部署到所管理的 router 數目最少的 L3 Agent 上。它們根據不同的算法選出待部署 router 實例的 L3 Agent。
(4)bind_router 函數會執行 db 操作,將 router 和 scheduler 選出的 L3 Agent 綁定在一起。比如:
2015-08-01 10:42:21.488 19430 DEBUG neutron.scheduler.l3_agent_scheduler [req-827c863f-cb01-4650-a26d-0f176ef84026 None] HA Router 04f6e792-3b79-4b8f-a577-2ad38d33a2bb is scheduled to L3 agent 04c360d0-3066-4f04-9af2-d4ef8586ad2b) bind_ha_router_to_agents /usr/lib/python2.7/dist-packages/neutron/scheduler/l3_agent_scheduler.py:330 2015-08-01 10:42:21.547 19430 DEBUG neutron.scheduler.l3_agent_scheduler [req-827c863f-cb01-4650-a26d-0f176ef84026 None] Router 04f6e792-3b79-4b8f-a577-2ad38d33a2bb is scheduled to L3 agent 4705d27c-5008-4828-b619-bbb2114188ba bind_router /usr/lib/python2.7/dist-packages/neutron/scheduler/l3_agent_scheduler.py:226
當確定的部署數量為2 時,會是下面的情形:
2.3 Keepalived notifier script ( VRRP 狀態變化時的通知腳本)
Neutron 的 KeepalivedNotifierMixin 類實現了 keepalived 在 VRRP 狀態變化時的通知方法。Neturon 添加了 VRRP 狀態變為 master,backup 和 fault 狀態時的 notifier。
- 變更為 master 狀態時的 notifier script:
#!/usr/bin/env bash neutron-ns-metadata-proxy --pid_file=/var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid --metadata_proxy_socket=/var/lib/neutron/metadata_proxy --router_id=04f6e792-3b79-4b8f-a577-2ad38d33a2bb --state_path=/var/lib/neutron --metadata_port=9697 --debug --verbose --log-file=neutron-ns-metadata-proxy-04f6e792-3b79-4b8f-a577-2ad38d33a2bb.log --log-dir=/var/log/neutron echo -n master > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state
第一個命令,在 router network namespace 中啟動 neutron-ns-metadata-proxy,這樣使用該 router 的虛機就可以通過該 proxy 訪問 metadata service 了。關於 metadata service, proxy 和 agent,可以參考文章 (1)(2)(3)。
第二個命令,設置 state 文件內容為 “master”。 Keepalived 不支持直接查詢 VRRP 狀態,因此,只能通過 state 變化時被調用的腳本文件來保存其狀態。
- 變更為 backup 狀態的 notifier script:
#!/usr/bin/env bash kill -9 $(cat /var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid) echo -n backup > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state
第一個命令,在 router network namespace 中殺掉 neutron-ns-metadata-proxy 進程,這樣使用該 router 的虛機就無法通過該 proxy 訪問 metadata service 了。
第二個命令,在 state 文件中記錄 VRRP state 為 “backup”。
#!/usr/bin/env bash kill -9 $(cat /var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid) echo -n fault > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state
因此,第一次 VRRP 選舉完成后,即有一個 Master router 被選舉出來。這時候,各節點上不同的 script 會執行不同的動作:
- Master router 的節點上:啟動 route namespace 對應的 neutron-ns-metadata-proxy 進程,保存 “master” 至 state 文件,設置 VIP(包括配置文件中 virtual ipaddress 指定的 IP 地址和 virtual_ipaddress_excluded 部分的 IP 地址)和 virtual_routes 部分的 route。
- Slave router 的節點上:殺掉 route namespace 對應的 neutron-ns-metadata-proxy 進程,保存 “backup” 至 state 文件,清除 VIP 和 virtual_routes 部分的 route。
2.4 依次添加 gateway,subnet,虛機 和 浮動 IP
這些操作都是對已有 HA router 的更新。 當 HA Router 的信息發生改變的時候,執行 DB 操作后,Neutron Server 會通知部署有 router 實例的 L3 Agent,然后它會去獲取到更新后的信息,重新生成新的 keepalived 配置文件,然后通知 keepalived 去使用新的配置文件。
2.4.1 添加 gateway
keepalived 的配置文件的 virtual_ipaddress_excluded 部分會增加 gateway 的 interface IP:192.168.1.113/24 dev qg-f19f97bc-e5 作為一個 VIP。這個 VIP 地址會在 Master node 被設置,在 Backup node 上不會被設置。
master:
36: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff inet 192.168.1.113/24 scope global qg-f19f97bc-e5 valid_lft forever preferred_lft forever
slave:
61: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff
2.4.2 添加 subnet interface
同樣地,keepalived 的配置文件的 virtual_ipaddress_excluded 部分會增加 subnet的 gateway IP:10.0.10.1/24 dev qr-73641a84-72 做為一個新的 VIP。這個 VIP 會在 Master node 被設置,在 Backup node 上不會被設置。
master:
37: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff inet 10.0.10.1/24 scope global qr-73641a84-72 valid_lft forever preferred_lft forever
bakckup:
62: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff
2.4.3 添加一個虛機到 10.0.10.1/24 網絡
虛機的默認網關為 10.0.10.1,這正是 Master node 上的 qr-73641a84-72 interface 上設置的 VIP 。因此,虛機發送到不是本機所在網絡的網絡包都會經過本 interface 進入 Master 路由器。
2.4.4 向該 IP 添加一個 浮動 IP 192.168.1.112
同樣地,keepalived 的配置文件的 virtual_ipaddress_excluded 部分會增加該 浮動 IP 和它所在的 external port:192.168.1.112/32 dev qg-f19f97bc-e5 作為一個新的 VIP。這個 VIP 會在 Master node 被設置,在 Backup node 上不會被設置。
該 VIP 被加到了 Master router 的 external port 上:
36: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff inet 192.168.1.113/24 scope global qg-f19f97bc-e5 valid_lft forever preferred_lft forever inet 192.168.1.112/32 scope global qg-f19f97bc-e5 valid_lft forever preferred_lft forever
同樣,在 Slave router 的 exernal port 上沒有添加。
但是,每一次 router 實例都更新的時候,Master router 和 Backup router 的 iptables 規則保持了一致的更新。 從以上過程可以看出,在某一時刻,只有 Master router 的 interface 在接受虛機發來的網絡包,其它 VRRP router 只是在 standby。
至此,Master 和 Backup L3 Agent namespace 的效果為:
2.5 HA Router 故障切換
非搶占模式下,因為在master 把某個 slave 低時也不會發生切換,因此要觸發 keepalived VRRP failover,需要考慮:
- 當 slave 節點在規定的時間內沒有收到 master 的心跳:比如 一個 L3 Agent 上的 keepalived 進程死了,或者 HA Device down 了,或者 HA network down 了等。除此以外的其它情況,都需要使用 workaround 來觸發故障切換:比如使用 peacemaker 監視 L3 Agent,一旦發現它 down 了,立刻將 HA Device down 掉。
- 當 master 節點上的 external gateway 在規定的時間內無法訪問時:這里有個 bug 報出來了,到目前為止還沒有被解決。
而在搶占模式下,可以通過修改 master 的 weight 來主動觸發切換,這可以使用 keepalived 自帶的 tracing script 來實現。該腳本可以自行執行檢查操作,然后 keepalived 會根據返回值做預訂的各項操作,包括設置 weight 甚至直接將狀態設置為 FAULT 來觸發切換。其格式為:
vrrp_script script_name { script "program_path arg ..." interval i # Run script every i seconds fall f # If script returns non-zero f times in succession, enter FAULT state rise r # If script returns zero r times in succession, exit FAULT state timeout t # Wait up to t seconds for script before assuming non-zero exit code weight w # Reduce priority by w on fall }
配置示例:
vrrp_instance instance_name { state MASTER interface eth0 virtual_router_id 21 priority 200 advert_int 1 virtual_ipaddress { 10.0.0.10/24 } track_script { script_name ... } }
簡單起見,為了驗證 failover,把 master node 上的 HA Device down 掉。
ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb ifconfig ha-20894a79-b6 down
這時候,該節點的 VRRP 狀態變為 fault。
Aug 1 15:26:58 network Keepalived_vrrp[4415]: Kernel is reporting: interface ha-20894a79-b6 DOWN
Aug 1 15:26:58 network Keepalived_vrrp[4415]: Kernel is reporting: interface ha-20894a79-b6 DOWN
Aug 1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering FAULT STATE
Aug 1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Now in FAULT state
Aug 1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to FAULT state
Aug 1 15:26:58 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_fault.sh
該master 被設置為 FAULT 狀態后,它會釋放 VIP,而且停止發送 VRRP 心跳。原 backup node 上的 keepalived 因為在規定時間內收不到 VRRP 通告,主動發起選舉過程,將自己變為 Master 狀態,觸發 notify_master.sh 被調用。這時候,該節點上的 router namespace 的 qr,gg 和 ha interface 的 VIP 全部被配置了,而且 route 規則也增加了。它已經接替原 master 節點向虛機提供路由服務。
60: ha-a8a3a49f-c1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:84:24:7f brd ff:ff:ff:ff:ff:ff inet 169.254.192.1/18 brd 169.254.255.255 scope global ha-a8a3a49f-c1 valid_lft forever preferred_lft forever inet 169.254.0.1/24 scope global ha-a8a3a49f-c1 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe84:247f/64 scope link valid_lft forever preferred_lft forever 61: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff inet 192.168.1.112/32 scope global qg-f19f97bc-e5 valid_lft forever preferred_lft forever inet 192.168.1.113/24 scope global qg-f19f97bc-e5 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe14:be06/64 scope link valid_lft forever preferred_lft forever 62: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff inet 10.0.10.1/24 scope global qr-73641a84-72 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe86:dd53/64 scope link valid_lft forever preferred_lft forever
root@network:/home/s1# ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 qg-f19f97bc-e5 10.0.10.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-73641a84-72 169.254.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ha-20894a79-b6 169.254.192.0 0.0.0.0 255.255.192.0 U 0 0 0 ha-20894a79-b6 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-f19f97bc-e5
從外網 ping 虛機看, failover 過程中,網絡有若干秒的中斷,而且有建立起來的連接斷了:
64 bytes from 192.168.1.112: icmp_seq=41 ttl=63 time=2.53 ms
64 bytes from 192.168.1.112: icmp_seq=42 ttl=63 time=2.23 ms
64 bytes from 192.168.1.112: icmp_seq=50 ttl=63 time=5.35 ms
64 bytes from 192.168.1.112: icmp_seq=51 ttl=63 time=2.11 ms
這個問題會在后續版本會使用 conntrackd 來解決。
在 HA Device 重新 UP 后,VRRP 狀態從 fault 變為 Backup 並一直保持該狀態:
Aug 1 15:39:37 network Keepalived_vrrp[4415]: Kernel is reporting: Group(VG_1) UP
Aug 1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Leaving FAULT state
Aug 1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering BACKUP STATE
Aug 1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to BACKUP state
Aug 1 15:39:37 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh
所有 port 的 VIP 和 route 規則都被刪除了。
再將 master namespace 上的 HA Device down 掉,backup namespace 中的 keepalive 重新變為 Master:
Aug 1 15:41:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Transition to MASTER STATE
Aug 1 15:41:58 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to MASTER state
Aug 1 15:41:58 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh
Aug 1 15:42:00 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering MASTER STATE
所有 port 的 VIP 和 route 規則都被設置了。
3. Juno VRRP 小結和后續版本的改進
3.1 Juno VRRP 實現小結
- Juno VRRP 使用 Keepalived,在每個 HA Route 的實例的 namespace 中啟動一個 keepalived 進程,它采用非搶占式模式。
- keepalived 進程只監控 HA Device,在其不可用時觸發選舉機制。
- neutron 依賴 keepalived 設置或者清除 VIP (包括 VRRP VIP 和浮動 IP,internal/external port IP)
- neutron 依賴 keepalived 設置或者清除 virtual_routes 部分的 route 規則
- neutron 在 keepalived VRRP 狀態變化時保存 VRRP 的狀態到本地文件
- 所有 HA Route 實例的 namespace 中,只有 master namespace 提供 port 上的 IP 實際向網絡內的虛機提供路由服務。neutron 依賴 keepalived 在 failover 時發送無用 ARP 來更新指定網絡內機器的 ARP 表。
- Juno 版本中的實現還不完善,比如只能監控 HA Device,不能監控別的 port,包括 internal 和 external 端口,以及 external link 等;還未實現切換時保留 TCP 連接的狀態。
3.2 后續版本中的持續改進
Juno 版本中增加 VRRP 的實現實現后,有很多地方需要改進,有大量的 blueprint 被開出來,但是大都進展緩慢。比較有意思的有幾個:
- 這個 blueprint 增加 conntracked 來支持 keepalived failover 后保存已建立的 TCP 連接。目前還沒完成。
- 通過監視 HA Device 的 ip 來監視 keepalived 節點的狀態
- 這個 blueprint 增加 neutron API 來 report keepalived master。已合並到 M 版本。
- 這個 blueprint 同時支持 DVR 和 HR,但是還未開工。
- 這個 bug 打算增加 checker 來監視 external gateway。未完成。
這也能看出,Neutron 在基礎部分(L2,L3)的實現比較扎實,但是在高級功能的實現上,包括 VRRP,DVR,VPN/FW/LBaas 等,還是不夠扎實,想應用到生成環境中還需要做大量的工作。
4. Kilo 版本更新 (//2015/11/27 更新)
Neutron Kilo 版本中,新增了 neutron l3-agent-list-hosting-router <router_id> 命令來查看 master l3-agent 在哪個網絡節點上。
root@hkg023:~# neutron l3-agent-list-hosting-router 03cb51be-3558-4253-8062-db177679e141 +--------------------------------------+-------------------+----------------+-------+----------+ | id | host | admin_state_up | alive | ha_state | +--------------------------------------+-------------------+----------------+-------+----------+ | 39b13aea-45a9-4336-afb4-c555b488e92d | hkg02kvm | True | :-) | | +--------------------------------------+-------------------+----------------+-------+----------+
參考資料:
- https://wiki.openstack.org/wiki/Neutron/L3_High_Availability_VRRP
- https://review.openstack.org/#/q/topic:bp/l3-high-availability,n,z
- http://bbs.nanjimao.com/thread-845-1-1.html
- http://bingotree.cn/?p=704
- http://www.keepalived.org/documentation.html
- https://docs.oracle.com/cd/E37670_01/E41138/html/section_hxz_zdw_pr.html
- https://www.haproxy.com/documentation/hapee/1-5r2/configuration/vrrp/
歡迎大家關注我的個人公眾號: