戰(zhàn):從VRRP原理到主備切換與踩坑指南)
Keepalived這套工具我第一次在Linux服務(wù)器上安裝配置時(shí)是給Nginx做VIP入口的。當(dāng)時(shí)想法特別簡(jiǎn)單兩臺(tái)機(jī)器一個(gè)IP誰掛了另一個(gè)頂上。結(jié)果配置完啟動(dòng)之后兩臺(tái)節(jié)點(diǎn)完全感知不到對(duì)方查了大半天才發(fā)現(xiàn)是防火墻沒放行VRRP協(xié)議。接下來我就把Keepalived在Linux環(huán)境下的安裝、配置、啟動(dòng)整條鏈路寫清楚附帶這些年跌過的坑和常用的驗(yàn)證方法。不管你是要給Nginx、MySQL還是RabbitMQ做高可用Keepalived都是Linux上最經(jīng)典、最輕量、最容易上手的一層方案值得花時(shí)間徹底搞明白。1. 高可用不是玄學(xué)Keepalived與VIP的核心機(jī)制1.1 單點(diǎn)故障問題的本質(zhì)任何一臺(tái)服務(wù)器、任何一個(gè)進(jìn)程都有概率發(fā)生故障。硬件老化、系統(tǒng)崩潰、進(jìn)程被殺、網(wǎng)絡(luò)分區(qū)隨便一個(gè)原因都可能讓服務(wù)不可用。在Keepalived出現(xiàn)之前解決單點(diǎn)故障最樸素的想法是“多加一臺(tái)機(jī)器”但加完機(jī)器后問題就來了客戶端訪問的IP只有一個(gè)怎么讓請(qǐng)求自動(dòng)切換到備用機(jī)器這時(shí)候虛擬IPVIP的概念就出來了——對(duì)外只暴露一個(gè)VIP這個(gè)VIP由主備節(jié)點(diǎn)中的一臺(tái)持有主節(jié)點(diǎn)故障后VIP自動(dòng)漂移到備用節(jié)點(diǎn)客戶端完全無感知。Keepalived干的就是這件事把VIP的持有權(quán)做成一種可以被“選舉、移交、接管”的資源。1.2 VRRP協(xié)議的核心工作過程Keepalived的底層協(xié)議是VRRPVirtual Router Redundancy Protocol這個(gè)協(xié)議最早是為路由器高可用設(shè)計(jì)的思路和我們現(xiàn)在講的服務(wù)器高可用完全一致。每個(gè)參與高可用的節(jié)點(diǎn)會(huì)周期性地向同一個(gè)網(wǎng)段發(fā)送VRRP通告報(bào)文報(bào)文中包含優(yōu)先級(jí)、virtual_router_id等信息。收到通告后每個(gè)節(jié)點(diǎn)會(huì)比較自己和對(duì)方的優(yōu)先級(jí)優(yōu)先級(jí)高的成為MASTER持有VIP優(yōu)先級(jí)低的進(jìn)入BACKUP狀態(tài)只是不停監(jiān)聽MASTER的通告。當(dāng)BACKUP連續(xù)一段時(shí)間默認(rèn)是3倍advert_int沒有收到MASTER的通告就會(huì)認(rèn)為MASTER已經(jīng)失聯(lián)立即重新選舉——如果它的優(yōu)先級(jí)最高就把VIP接管過來。這個(gè)過程中有兩點(diǎn)值得注意。第一state字段里寫的MASTER或BACKUP并不是最終決定誰是主的唯一依據(jù)priority的數(shù)值才是。你寫MASTER但priority寫得比對(duì)方低照樣會(huì)退讓。所以我在配置時(shí)習(xí)慣把主節(jié)點(diǎn)priority設(shè)為100備節(jié)點(diǎn)設(shè)為90既保證主備穩(wěn)定又留出足夠差距避免因網(wǎng)絡(luò)瞬斷導(dǎo)致的反復(fù)橫跳。第二VRRP默認(rèn)采用組播方式發(fā)送通告目標(biāo)地址是224.0.0.18。這意味著同一網(wǎng)段內(nèi)所有的Keepalived節(jié)點(diǎn)理論上都能收到彼此的報(bào)文而這也帶來了防火墻放行和virtual_router_id隔離這兩個(gè)后續(xù)要重點(diǎn)處理的問題。1.3 從“機(jī)器活著”到“服務(wù)可用”Keepalived只做VRRP的話只能感知“對(duì)端機(jī)器是否在線”感知不到“本機(jī)Nginx進(jìn)程是不是掛了”。因此有了vrrp_script健康檢查機(jī)制定期執(zhí)行一段腳本腳本返回0表示正常返回非0判定為故障。當(dāng)腳本檢測(cè)到業(yè)務(wù)進(jìn)程異常時(shí)本節(jié)點(diǎn)會(huì)主動(dòng)降低優(yōu)先級(jí)、退出MASTER狀態(tài)把VIP讓給備用節(jié)點(diǎn)。這種設(shè)計(jì)把“高可用”從機(jī)器層面下沉到了服務(wù)層面也是我在實(shí)際配置中一定會(huì)加上的一環(huán)。沒有健康檢查的Keepalived只能算半個(gè)高可用。1.4 狀態(tài)機(jī)與切換觸發(fā)條件小結(jié)Keepalived節(jié)點(diǎn)通常會(huì)在Initialize、Backup、Master、Fault幾個(gè)狀態(tài)之間流轉(zhuǎn)。啟動(dòng)時(shí)進(jìn)入Initialize隨后如果判斷自己是MASTER就直接進(jìn)入Master狀態(tài)并開始發(fā)送VRRP通告否則進(jìn)入Backup狀態(tài)等待。Fault狀態(tài)代表本節(jié)點(diǎn)出現(xiàn)異常比如配置錯(cuò)誤、腳本檢測(cè)失敗、網(wǎng)卡故障此時(shí)不會(huì)持有VIP。梳理清楚這幾個(gè)狀態(tài)再看后面的日志時(shí)就會(huì)順暢很多。日志里出現(xiàn)的Entering MASTER STATE、Entering BACKUP STATE、Entering FAULT STATE分別對(duì)應(yīng)一次完整的狀態(tài)切換過程。2. 從空白系統(tǒng)到Keepalived就緒安裝全流程2.1 安裝方式選型yum/apt優(yōu)先還是源碼編譯Keepalived的安裝方式主要有三種系統(tǒng)包管理器安裝、源碼編譯安裝、容器部署。我個(gè)人對(duì)生產(chǎn)環(huán)境的建議是能直接用yum或apt就用別一開始就上源碼。原因很簡(jiǎn)單包管理器安裝會(huì)自動(dòng)處理好依賴、配置文件路徑和systemd服務(wù)文件一條命令之后就能用后續(xù)升級(jí)也方便。源碼編譯的優(yōu)勢(shì)在于版本新、可定制參數(shù)但需要自己處理依賴和服務(wù)文件對(duì)新手不夠友好。容器化部署Keepalived不是不能做但在“給物理機(jī)或云主機(jī)上的業(yè)務(wù)提供VIP漂移”這個(gè)場(chǎng)景下直接用宿主機(jī)進(jìn)程的方式更簡(jiǎn)單也少一層網(wǎng)絡(luò)映射的復(fù)雜性。2.2 yum/apt安裝的具體操作在CentOS、RHEL系上安裝Keepalived只需要一行yum install -y keepalived安裝完成后二進(jìn)制文件默認(rèn)在/usr/sbin/keepalived配置文件自動(dòng)生成在/etc/keepalived/keepalived.confsystemd服務(wù)文件也注冊(cè)好了。Ubuntu/Debian系上對(duì)應(yīng)的是apt update apt install -y keepalived安裝完成后同樣可以用systemctl管理。這里特別提一下很多同學(xué)喜歡“裝完立刻啟動(dòng)”但Keepalived此時(shí)只有一個(gè)默認(rèn)的空配置模板直接啟動(dòng)不會(huì)報(bào)錯(cuò)但也什么都干不了。正確順序是先檢查配置目錄、確認(rèn)二進(jìn)制版本再動(dòng)手改配置。2.3 源碼編譯安裝的完整過程如果系統(tǒng)源里沒有Keepalived或者你需要特定版本源碼編譯也不難但前置依賴一定要裝齊。在CentOS系上至少需要這些包yum install -y gcc gcc-c make openssl-devel popt-devel libnl3-devel這些都是Keepalived編譯時(shí)要用到的頭文件和工具鏈。缺失libnl3-devel時(shí)configure階段會(huì)報(bào)libnl headers not found這種情況不是Keepalived本身的問題而是依賴沒補(bǔ)齊。Ubuntu系上對(duì)應(yīng)apt install -y build-essential libssl-dev libpopt-dev libnl-3-dev libnl-genl-3-dev依賴裝好之后進(jìn)入源碼目錄./configure --prefix/usr/local/keepalived --sysconfdir/etc/keepalived make make install--sysconfdir/etc/keepalived這一步非常關(guān)鍵它把配置文件的默認(rèn)路徑固定到/etc/keepalived和后面systemd服務(wù)文件里ExecStart的默認(rèn)配置路徑保持一致。如果漏了默認(rèn)配置路徑會(huì)變成/usr/local/etc/keepalived啟動(dòng)時(shí)服務(wù)找不到配置文件排查又浪費(fèi)一輪時(shí)間。2.4 安裝后的驗(yàn)證與環(huán)境自檢安裝完成后用下面幾條命令確認(rèn)環(huán)境keepalived --version which keepalived ls -l /etc/keepalived/keepalived.conf systemctl status keepalived接著確認(rèn)網(wǎng)卡名這一步直接決定后面interface參數(shù)怎么寫ip link我看到太多人直接把網(wǎng)上教程的eth0抄進(jìn)配置結(jié)果自己機(jī)器網(wǎng)卡是ens33VRRP通告根本發(fā)不出去。確認(rèn)完網(wǎng)卡再在兩個(gè)節(jié)點(diǎn)之間互相ping一下確認(rèn)基礎(chǔ)網(wǎng)絡(luò)是通的。這幾件事做完才算真正具備配置Keepalived的前提條件。3. 配置文件的正確打開方式主備節(jié)點(diǎn)的每項(xiàng)關(guān)鍵參數(shù)3.1 配置文件整體結(jié)構(gòu)Keepalived的配置文件通常包含幾個(gè)段global_defs是全局定義vrrp_script是健康檢查腳本定義vrrp_instance是虛擬IP實(shí)例virtual_server是與LVS集成時(shí)的配置段。日常使用中最核心的是vrrp_script和vrrp_instance。一個(gè)配置可以同時(shí)包含多個(gè)vrrp_instance一臺(tái)機(jī)器上跑多個(gè)實(shí)例就能實(shí)現(xiàn)“我是A組的主、B組的備”這種互備架構(gòu)。這也是Keepalived比較靈活的地方高可用不一定是兩臺(tái)機(jī)器一主一閑資源可以互為備份。3.2 global_defs里的關(guān)鍵參數(shù)global_defs里最常用的就是router_id它是一個(gè)字符串標(biāo)識(shí)建議兩臺(tái)節(jié)點(diǎn)設(shè)置成不同值比如主節(jié)點(diǎn)叫LVS_MASTER備節(jié)點(diǎn)叫LVS_BACKUP。雖然它不參與選舉但會(huì)在日志里出現(xiàn)設(shè)置成有意義的名稱后期排查多實(shí)例時(shí)能一眼看出是哪臺(tái)機(jī)器。另一個(gè)值得了解的是enable_script_security如果健康檢查腳本里會(huì)執(zhí)行較復(fù)雜的命令加上這個(gè)參數(shù)并配合script_user指定用戶可以限制腳本的執(zhí)行權(quán)限。不過我的實(shí)際經(jīng)驗(yàn)是健康檢查腳本越簡(jiǎn)單越好盡量別在腳本里做復(fù)雜的邏輯否則腳本本身就會(huì)變成新的故障源。3.3 vrrp_instance逐參數(shù)拆解下面是一份最典型的主節(jié)點(diǎn)配置global_defs { router_id LVS_MASTER } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 192.168.10.100/24 dev eth0 } }備份節(jié)點(diǎn)大部分參數(shù)相同只有state、router_id、priority不同。下面這張表可以直接對(duì)照著寫參數(shù)主節(jié)點(diǎn)備份節(jié)點(diǎn)stateMASTERBACKUPpriority10090virtual_router_id5151interface實(shí)際網(wǎng)卡名實(shí)際網(wǎng)卡名authentication與主節(jié)點(diǎn)一致與主節(jié)點(diǎn)一致三個(gè)容易出問題的點(diǎn)第一interface必須寫本機(jī)實(shí)際網(wǎng)卡名別照抄示例里的eth0。第二如果同一網(wǎng)段里部署了多套Keepalived集群virtual_router_id絕對(duì)不能重復(fù)否則會(huì)互相搶VIP。第三auth_pass官方限制最多8位建議就寫8位以內(nèi)的純數(shù)字或字母別學(xué)網(wǎng)上某些教程寫一長(zhǎng)串否則可能因認(rèn)證報(bào)文不符合預(yù)期導(dǎo)致節(jié)點(diǎn)之間無法正常協(xié)商。advert_int默認(rèn)1秒表示每隔1秒發(fā)送一次VRRP通告建議保持默認(rèn)。virtual_ipaddress里的VIP必須和業(yè)務(wù)網(wǎng)卡在同一網(wǎng)段否則外部訪問不通。3.4 健康檢查腳本與track_script聯(lián)動(dòng)健康檢查是Keepalived真正體現(xiàn)業(yè)務(wù)感知的模塊。假設(shè)要給Nginx做健康檢查先寫一個(gè)腳本#!/bin/bash if pgrep -x nginx /dev/null 21; then exit 0 else exit 1 fi給腳本加執(zhí)行權(quán)限后在配置文件中定義vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 timeout 2 rise 2 fall 2 }然后在vrrp_instance里引用track_script { check_nginx }這里幾個(gè)參數(shù)的實(shí)際含義需要說清楚interval是健康檢查執(zhí)行間隔秒數(shù)timeout是單次腳本執(zhí)行超時(shí)時(shí)間超過就按失敗處理rise表示連續(xù)成功多少次才把狀態(tài)從故障恢復(fù)為正常fall表示連續(xù)失敗多少次才判定故障。默認(rèn)情況下rise和fall都是1但在網(wǎng)絡(luò)或服務(wù)恢復(fù)場(chǎng)景下很容易出現(xiàn)“剛恢復(fù)就切換”的抖動(dòng)現(xiàn)象所以我通常會(huì)把fall設(shè)成2甚至3避免一次偶發(fā)失敗就造成VIP來回漂。腳本返回值0表示健康非0表示不健康這個(gè)約定是Keepalived判定狀態(tài)的唯一依據(jù)。3.5 notify腳本讓切換動(dòng)作可感知、可追蹤VIP漂移只是一層表象實(shí)際運(yùn)維中我更關(guān)心切換發(fā)生之后系統(tǒng)做了什么。在vrrp_instance里配置三個(gè)notify參數(shù)notify_master /etc/keepalived/notify_master.sh notify_backup /etc/keepalived/notify_backup.sh notify_fault /etc/keepalived/notify_fault.sh三個(gè)腳本分別在節(jié)點(diǎn)變成MASTER、BACKUP、FAULT狀態(tài)時(shí)被調(diào)用。最常見的用法是notify_master里reload業(yè)務(wù)進(jìn)程、刷新ARP緩沖notify_backup里把機(jī)器上不屬于自己的VIP清理干凈notify_fault里調(diào)用告警接口做通知。腳本務(wù)必使用絕對(duì)路徑加執(zhí)行權(quán)限腳本內(nèi)部如果有輸出最好重定向到日志文件。我在notify_master腳本里通常會(huì)這樣寫#!/bin/bash echo $(date %F %T) [notify_master] /var/log/keepalived_notify.log systemctl reload nginx 2/dev/null || true這樣既能確認(rèn)切換邏輯觸發(fā)又不會(huì)讓script腳本的輸出干擾Keepalived主進(jìn)程的日志。4. 啟動(dòng)、驗(yàn)證與主備切換演練4.1 啟動(dòng)前先做配置語法檢查改完配置后不要急著start先讓Keepalived自檢一下keepalived -t -f /etc/keepalived/keepalived.conf輸出里出現(xiàn)語法正常之類的提示后再用systemctl啟動(dòng)。語法檢查這一步看似多余實(shí)際上能幫你攔截一大半的“服務(wù)起不來”問題。配置文件的括號(hào)、分號(hào)、參數(shù)名只要有一處錯(cuò)誤服務(wù)都可能直接失敗而這類錯(cuò)誤恰好是最讓人頭大的。4.2 啟動(dòng)的正確姿勢(shì)與進(jìn)程判斷systemd下啟動(dòng)很簡(jiǎn)單systemctl start keepalived systemctl enable keepalived啟動(dòng)后我通常第一時(shí)間看兩樣?xùn)|西進(jìn)程是否完整、狀態(tài)是否正常。ps -ef | grep keepalived systemctl status keepalived正常情況下ps能看到主進(jìn)程加VRRP子進(jìn)程如果配置了健康檢查還會(huì)看到check子進(jìn)程也就是三條記錄。如果只有一條或者兩條異常多半是子進(jìn)程啟動(dòng)失敗了這時(shí)馬上看日志journalctl -u keepalived -f日志是Keepalived排錯(cuò)的第一手資料比到處猜強(qiáng)得多??吹紼ntering MASTER STATE就說明這臺(tái)已經(jīng)成為主節(jié)點(diǎn)VIP應(yīng)該很快會(huì)出現(xiàn)在網(wǎng)卡上。4.3 VIP驗(yàn)證這幾條命令最直接判斷VIP是否生效不需要看管理界面命令行就夠了ip addr show ip addr show dev eth0如果VIP已經(jīng)配置成功能看到類似192.168.10.100/24 scope global eth0這樣的地址。如果看不到按這個(gè)順序排查interface參數(shù)寫沒寫對(duì)防火墻有沒有放行VRRP兩臺(tái)節(jié)點(diǎn)之間的網(wǎng)絡(luò)通不通。協(xié)議層面可以用tcpdump驗(yàn)證tcpdump -ni eth0 vrrpMASTER節(jié)點(diǎn)應(yīng)該每隔1秒就發(fā)一個(gè)VRRP通告包。如果你在BACKUP節(jié)點(diǎn)上也頻繁抓到通告說明兩臺(tái)節(jié)點(diǎn)至少能從協(xié)議層發(fā)現(xiàn)對(duì)方問題大概率出在配置細(xì)節(jié)上。4.4 主備切換演練不能只在理論上說Keepalived配完必須做真實(shí)切換演練不然你永遠(yuǎn)不知道哪一步配置其實(shí)有問題。我的標(biāo)準(zhǔn)流程是在主節(jié)點(diǎn)執(zhí)行systemctl stop keepalived模擬進(jìn)程異常退出。到備用節(jié)點(diǎn)執(zhí)行ip addr showVIP應(yīng)該幾秒內(nèi)出現(xiàn)在備用節(jié)點(diǎn)網(wǎng)卡上。查看備用節(jié)點(diǎn)notify_master腳本日志確認(rèn)切換通知觸發(fā)。重新啟動(dòng)主節(jié)點(diǎn)Keepalived觀察VIP是否按預(yù)期搶占回來。再一次執(zhí)行ip addr show和日志檢查確認(rèn)主備狀態(tài)恢復(fù)。演練時(shí)如果發(fā)現(xiàn)VIP沒有切換先不要急著改配置而是按“網(wǎng)絡(luò)是否通-防火墻是否放行-配置是否一致-日志是否報(bào)錯(cuò)”的順序逐步排查。我見過最隱蔽的問題是兩個(gè)節(jié)點(diǎn)的時(shí)間差太大導(dǎo)致日志和故障時(shí)序?qū)Σ簧吓挪闀r(shí)硬是繞了很久所以也建議大家配置NTP同步雖然VRRP本身不強(qiáng)依賴時(shí)間但日志對(duì)不上真的太痛苦了。5. 踩坑復(fù)盤從防火墻到云主機(jī)的常見翻車現(xiàn)場(chǎng)5.1 防火墻與SELinux占翻車案例的一半Keepalived部署后最容易翻車的地方就是防火墻。VRRP使用的協(xié)議號(hào)是112既不是TCP也不是UDP所以常規(guī)的放行80端口、443端口的規(guī)則對(duì)它不起作用。firewalld下可以這樣放行firewall-cmd --permanent --add-rich-rulerule protocol valuevrrp accept firewall-cmd --reloadiptables下則是iptables -I INPUT -p vrrp -j ACCEPTSELinux也經(jīng)常會(huì)成為隱性攔路虎。如果你發(fā)現(xiàn)配置完全正確但VRRP報(bào)文就是發(fā)不出去可以用setenforce 0臨時(shí)驗(yàn)證一下確認(rèn)是SELinux之后再考慮是否永久關(guān)閉或者調(diào)整對(duì)應(yīng)的布爾值。這里補(bǔ)一句修改防火墻或SELinux之后建議把Keepalived服務(wù)重啟一次讓進(jìn)程重新建立網(wǎng)絡(luò)綁定否則可能出現(xiàn)規(guī)則已經(jīng)放行了但連接還是不通的怪現(xiàn)象。5.2 網(wǎng)卡名不一致最常見的低級(jí)錯(cuò)誤現(xiàn)代Linux發(fā)行版和云主機(jī)的網(wǎng)卡命名已經(jīng)不一定是eth0了ens33、ens5、eth1都很常見。interface參數(shù)填錯(cuò)后Keepalived進(jìn)程可能正常啟動(dòng)但完全監(jiān)聽不到正確的網(wǎng)卡導(dǎo)致VRRP通告發(fā)不出、收不到主備互不感知。我建議在配置前先在每個(gè)節(jié)點(diǎn)上執(zhí)行ip link把真實(shí)的網(wǎng)卡名記下來兩臺(tái)節(jié)點(diǎn)各自填各自的interface不需要強(qiáng)求一致。比如主節(jié)點(diǎn)是ens33備節(jié)點(diǎn)是eth0只要各自填對(duì)VRRP照樣能工作。5.3 virtual_router_id沖突跨集群的干擾virtual_router_id是0到255之間的整數(shù)它的作用是在同一網(wǎng)段內(nèi)區(qū)分不同的VRRP虛擬路由器。同一個(gè)高可用集群的兩臺(tái)節(jié)點(diǎn)必須寫同一個(gè)ID但不同集群之間絕對(duì)不能重復(fù)。如果同一網(wǎng)段里有兩組Keepalived都用了ID 51這兩組會(huì)互相干擾輕則日志怪異重則VIP被誤搶。這個(gè)問題的隱蔽性在于日志里往往不會(huì)有明顯的報(bào)錯(cuò)只有用tcpdump抓包后才能看到不同集群之間發(fā)送了相同virtual_router_id的VRRP報(bào)文。我的習(xí)慣是給每個(gè)集群規(guī)劃獨(dú)立的ID同時(shí)在配置文件里用注釋寫明這個(gè)ID屬于哪套業(yè)務(wù)避免后續(xù)維護(hù)時(shí)撞車。5.4 腦裂高可用最怕的兩個(gè)字腦裂是所有高可用方案的噩夢(mèng)Keepalived也不例外。腦裂發(fā)生后兩臺(tái)節(jié)點(diǎn)同時(shí)認(rèn)為自己是MASTER同時(shí)持有同一個(gè)VIP流量被打散到兩臺(tái)機(jī)器上如果后面連的是數(shù)據(jù)庫后果不堪設(shè)想。判斷腦裂的標(biāo)準(zhǔn)動(dòng)作有兩個(gè)第一在兩臺(tái)節(jié)點(diǎn)上分別執(zhí)行ip addr show看VIP是否同時(shí)存在第二在兩臺(tái)節(jié)點(diǎn)上分別抓VRRP包看到底是誰在正常發(fā)通告。定位到腦裂后按順序檢查兩臺(tái)節(jié)點(diǎn)priority是否設(shè)置正確、防火墻是否放行了VRRP、interface是否填寫正確、三層網(wǎng)絡(luò)是否互通。腦裂的根源幾乎都是“一個(gè)節(jié)點(diǎn)能正常發(fā)通告但另一個(gè)節(jié)點(diǎn)收不到”所以重點(diǎn)永遠(yuǎn)是排查通信鏈路而不是懷疑Keepalived本身。5.5 非搶占模式nopreempt寫在哪很有講究默認(rèn)的Keepalived是搶占行為主節(jié)點(diǎn)恢復(fù)后會(huì)立刻把VIP搶回來。但有些業(yè)務(wù)場(chǎng)景不允許VIP頻繁切換比如長(zhǎng)連接服務(wù)一旦切換就要斷連重連。這時(shí)需要配置非搶占模式。網(wǎng)上很多配置都寫錯(cuò)了。注意nopreempt只能寫在BACKUP節(jié)點(diǎn)的vrrp_instance里并且兩個(gè)節(jié)點(diǎn)的state都要寫B(tài)ACKUP只靠priority區(qū)分主次。原因在于Keepalived只在BACKUP狀態(tài)下才會(huì)處理nopreempt邏輯。如果你在MASTER節(jié)點(diǎn)上寫nopreempt根本不會(huì)生效。還有一種更平滑的方案是preempt_delay主節(jié)點(diǎn)恢復(fù)后等待一段時(shí)間再搶占比如300秒給業(yè)務(wù)和網(wǎng)絡(luò)一個(gè)穩(wěn)定窗口期。5.6 健康檢查腳本的隱性坑健康檢查腳本看起來簡(jiǎn)單實(shí)際上坑不少。第一個(gè)坑是權(quán)限腳本沒有執(zhí)行權(quán)限Keepalived會(huì)直接判定腳本執(zhí)行失敗。第二個(gè)坑是環(huán)境變量比如腳本里用到某個(gè)軟件的絕對(duì)路徑但Keepalived的systemd環(huán)境里PATH和交互式shell不一致導(dǎo)致命令找不到。第三個(gè)坑是超時(shí)如果腳本執(zhí)行時(shí)間超過了timeout配置會(huì)被直接判為失敗。我的建議是腳本先用root手動(dòng)執(zhí)行一遍確認(rèn)返回碼正確腳本里所有命令用絕對(duì)路徑腳本執(zhí)行時(shí)間務(wù)必小于配置的timeout如果腳本在業(yè)務(wù)高峰期可能變慢就把timeout適當(dāng)調(diào)大同時(shí)把fall閾值調(diào)大避免CPU抖動(dòng)導(dǎo)致誤判。5.7 云主機(jī)上的單播改造云環(huán)境是當(dāng)前部署Keepalived的重要場(chǎng)景。傳統(tǒng)物理機(jī)環(huán)境下VRRP用組播方式互發(fā)通告組播地址是224.0.0.18交換機(jī)默認(rèn)會(huì)轉(zhuǎn)發(fā)同網(wǎng)段組播。但許多公有云的VPC網(wǎng)絡(luò)并不支持VRRP組播兩臺(tái)節(jié)點(diǎn)互相收不到報(bào)文結(jié)果都認(rèn)為自己該當(dāng)MASTER。解決辦法是把Keepalived改成單播模式在vrrp_instance里寫unicast_src_ip 192.168.10.10 unicast_peer { 192.168.10.11 }這樣本節(jié)點(diǎn)只向指定的對(duì)端IP發(fā)送VRRP通告不再依賴組播。同時(shí)云平臺(tái)安全組也要放行對(duì)應(yīng)協(xié)議和端口。我在任何云主機(jī)上部署Keepalived時(shí)不管平臺(tái)是否支持組播都會(huì)直接使用單播配置寧可多寫兩行也不給自己留隱患。6. 場(chǎng)景延伸Keepalived在Nginx、MySQL、RabbitMQ高可用中的接入方式6.1 Keepalived不具備業(yè)務(wù)感知感知邏輯都在腳本里搞清楚這一點(diǎn)你就能理解為什么Keepalived能接那么多不同場(chǎng)景。它自身只負(fù)責(zé)VRRP協(xié)議和VIP漂移而對(duì)業(yè)務(wù)進(jìn)程是否正常的判斷全部交給vrrp_script里的腳本。所以要想讓Keepalived監(jiān)控某種服務(wù)只需要寫對(duì)應(yīng)的檢測(cè)腳本檢測(cè)Nginx就檢查nginx進(jìn)程和80端口檢測(cè)MySQL就檢查mysqld進(jìn)程和3306端口檢測(cè)RabbitMQ就檢查rabbitmq-server和5672端口??蚣芡耆粯又恍枰哪_本內(nèi)容。這也是Keepalived在Linux運(yùn)維體系里長(zhǎng)盛不衰的原因它足夠底層、足夠通用。6.2 Nginx高可用最標(biāo)準(zhǔn)的一套組合NginxKeepalived是目前最常見的組合做法也最簡(jiǎn)單。兩臺(tái)Nginx服務(wù)器各自啟動(dòng)Nginx監(jiān)聽自己的本機(jī)IPKeepalived提供VIP??蛻舳嗽L問VIPVIP落在哪臺(tái)機(jī)器上請(qǐng)求就打到哪臺(tái)機(jī)器的Nginx。當(dāng)MASTER的Nginx進(jìn)程異常退出check腳本返回非0Keepalived主動(dòng)讓出VIPBACKUP接管VIP后通過notify_master里的腳本確保本機(jī)Nginx也在運(yùn)行。這樣整體對(duì)外提供的服務(wù)地址始終不變。需要提醒的是不要在notify_master里只做“切換VIP”這件事一定要確認(rèn)業(yè)務(wù)進(jìn)程本身可用否則就是VIP切過去了業(yè)務(wù)反而不可用這種“假高可用”我在生產(chǎn)上見過不止一次。6.3 MySQL和RabbitMQ等中間件的高可用思路很多接觸Keepalived的人都是為了解決MySQL、RabbitMQ這類中間件的高可用問題。以MySQL主從場(chǎng)景為例正常情況下VIP掛在主庫上應(yīng)用連接串里寫的是VIP主庫故障后VIP漂移到從庫應(yīng)用不需要改配置。健康檢查腳本要同時(shí)探測(cè)本機(jī)IP的3306端口和數(shù)據(jù)庫進(jìn)程狀態(tài)避免只檢測(cè)進(jìn)程存活而端口已經(jīng)不可用的情況。RabbitMQ類似腳本里檢查5672端口或進(jìn)程狀態(tài)配合notify腳本做集群節(jié)點(diǎn)止損。但要直說Keepalived只解決了“入口漂移”解決不了中間件內(nèi)部的數(shù)據(jù)同步、腦裂等問題。MySQL的數(shù)據(jù)一致性得靠半同步復(fù)制或額外的高可用編排工具RabbitMQ的鏡像隊(duì)列、仲裁隊(duì)列也有自己的高可用機(jī)制。Keepalived適合作為最上層的接入入口別指望它包辦一切。6.4 給生產(chǎn)環(huán)境的幾條配置建議最后把這些年看到的問題匯總成幾條生產(chǎn)建議第一雙節(jié)點(diǎn)的Keepalived配置一定要放在版本控制里配置文件改動(dòng)需要可回溯不然線上有一次手誤改錯(cuò)了優(yōu)先級(jí)就是一次隱蔽故障。第二VIP的地址要提前規(guī)劃好不要用DHCP可能分配的地址否則IP沖突時(shí)候的故障極其難查。第三advert_int不要為了追求切換速度無限調(diào)小網(wǎng)絡(luò)抖動(dòng)會(huì)引起VIP反復(fù)漂移我一般保持默認(rèn)1秒即可。第四每個(gè)節(jié)點(diǎn)的日志和notify日志要有獨(dú)立文件并配置輪轉(zhuǎn)Keepalived本身日志量不大但vrrp通告在故障期間會(huì)連續(xù)輸出不輪轉(zhuǎn)的話可能撐爆磁盤。第五高可用演練要定期做不要只在剛部署時(shí)做一次。Keepalived這套東西配置本身不復(fù)雜真正考驗(yàn)人的是故障發(fā)生時(shí)的判斷力而這種判斷力只能靠一次次真實(shí)演練積累出來。