PostgreSQL技術(shù)大講堂 - 第48講:PG高可用實現(xiàn)keepalived
最新學(xué)訊:近期OCP認(rèn)證正在報名中,因考試人員較多請盡快報名獲取最近考試時間,報名費用請聯(lián)系在線老師,甲骨文官方認(rèn)證,報名從速!
我要咨詢
PostgreSQL從小白到專家,是從入門逐漸能力提升的一個系列教程,內(nèi)容包括對PG基礎(chǔ)的認(rèn)知、包括安裝使用、包括角色權(quán)限、包括維護(hù)管理、、等內(nèi)容,希望對熱愛PG、學(xué)習(xí)PG的同學(xué)們有幫助,歡迎持續(xù)關(guān)注CUUG PG技術(shù)大講堂。
第48講:PG高可用實現(xiàn)
內(nèi)容1:keepalived簡介
內(nèi)容2:Keepalived安裝與配置
內(nèi)容3:Keepalived&流復(fù)制實現(xiàn)高可用
內(nèi)容4:主備切換技巧
PostgreSQL技術(shù)大講堂 第48講:PG高可用實現(xiàn),3月23日19:30,釘釘群(35822460)& 視頻號(數(shù)據(jù)庫老陳)直播
往期視頻,聯(lián)系 ”CUUG“
keepalived簡介
Keepalived是一個用C編寫的路由軟件。
keepalive起初專門為lvs負(fù)載均衡軟件設(shè)計的,用來管理監(jiān)控lvs集群系統(tǒng)中各個服務(wù)節(jié)點的狀態(tài),后來又加入了可以實現(xiàn)高可用的vrrp功能。
該項目的主要目標(biāo)是為Linux系統(tǒng)和基于Linux的基礎(chǔ)架構(gòu)提供簡單而強大的負(fù)載平衡和高可用性設(shè)施。 負(fù)載平衡框架依賴于眾所周知且廣泛使用的Linux虛擬服務(wù)器(IPVS)內(nèi)核模塊,提供Layer4負(fù)載均衡。
keepalived完全遵守VRRP協(xié)議,包括競選機(jī)制等等,此外,Keepalived為VRRP有限狀態(tài)機(jī)實現(xiàn)了一組掛鉤,提供低級和高速協(xié)議交互。為了提供最快的網(wǎng)絡(luò)故障檢測,Keepalived實現(xiàn)了BFD協(xié)議。VRRP狀態(tài)轉(zhuǎn)換可以考慮BFD提示來驅(qū)動快速狀態(tài)轉(zhuǎn)換。Keepalived框架可以單獨使用,也可以一起使用,以提供靈活的基礎(chǔ)架構(gòu)。
這個協(xié)議起先應(yīng)用于網(wǎng)絡(luò),網(wǎng)絡(luò)在設(shè)計的時候必須考慮到冗余容災(zāi),包括線路冗余,設(shè)備冗余等,防止網(wǎng)絡(luò)存在單點故障,那在路由器或三層交換機(jī)處實現(xiàn)冗余就顯得尤為重要,在網(wǎng)絡(luò)里面有個協(xié)議就是來做這事的,這個協(xié)議就是VRRP協(xié)議,Keepalived就是巧用VRRP協(xié)議來實現(xiàn)高可用性(HA)的。
其用IP組播的方式,實現(xiàn)服務(wù)節(jié)點間的通信, 通過一種競選機(jī)制來將路由任務(wù)交給某臺VRRP路由器. 工作時主節(jié)點廣播發(fā)送VRRP協(xié)議報文, 備節(jié)點接收報文, 若一段時間(默認(rèn)3個報文發(fā)送時間)備節(jié)點接收不到主節(jié)點發(fā)送的報文, 就會啟動接管程序接管主節(jié)點的資源. 備節(jié)點可以有多個, 通過優(yōu)先級競選。
Keepalived組件進(jìn)程
keepalived是模塊化設(shè)計,不同模塊復(fù)雜不同的功能,下面是keepalived的組件。
Control Plane:
Keepalived配置通過keepalived.conf文件完成。編譯器設(shè)計用于解析。解析器使用關(guān)鍵字樹層次結(jié)構(gòu)來使用特定處理程序映射每個配置關(guān)鍵字。中央多級遞歸函數(shù)讀取配置文件并遍歷關(guān)鍵字樹。在解析期間,配置文件被轉(zhuǎn)換為內(nèi)部存儲器表示。
調(diào)度程序 - I / O多路復(fù)用器:
所有事件都安排在同一個進(jìn)程中。Keepalived是一個單一的過程。
Keepalived是一種網(wǎng)絡(luò)路由軟件,它對I / O非常封閉。這里使用的設(shè)計是一個中央選擇(...),負(fù)責(zé)安排所有內(nèi)部任務(wù)。不使用POSIX線程庫。該框架提供了自己的線程抽象,針對網(wǎng)絡(luò)目的進(jìn)行了優(yōu)化。
Memory Mngt(內(nèi)存管理):
該框架提供對一些通用內(nèi)存管理功能的訪問,例如分配,重新分配,發(fā)布......這個框架可以用于兩種模式:normal_mode和debug_mode。
使用debug_mode時,它提供了一種消除和跟蹤內(nèi)存泄漏的強大方法。這個低級env通過跟蹤分配內(nèi)存并釋放來提供緩沖區(qū)欠載保護(hù)。使用的所有緩沖區(qū)都是固定長度的,以防止最終的緩沖區(qū)溢出。
Core components(核心組件):
是keepalived的核心,復(fù)雜主進(jìn)程的啟動和維護(hù),全局配置文件的加載解析等。
該框架定義了所有代碼中使用的一些通用和全局庫。這些庫是:html解析,鏈接列表,計時器,向量,字符串格式化,緩沖區(qū)轉(zhuǎn)儲,網(wǎng)絡(luò)工具,守護(hù)程序管理,pid處理,低級TCP層4。這里的目標(biāo)是將代碼分解為最大限度,以盡可能地代碼重復(fù)以增加模塊性。
WatchDog:
該框架提供子進(jìn)程監(jiān)控(VRRP和健康檢查)。每個子進(jìn)程都接受與其自己的監(jiān)視器unix域套接字的連接。父進(jìn)程向此子unix域套接字發(fā)送“hello”消息。Hello消息使用父端的I / O多路復(fù)用器發(fā)送,并使用子端的I / O多路復(fù)用器接收/處理。如果父檢測到損壞的管道,則在子項仍然存活時使用sysV信號進(jìn)行測試并重新啟動它。
Checkers:
這是主要的Keepalived功能之一,Checkers負(fù)責(zé)realserver healthchecking。
包括了各種健康檢查方式,以及對應(yīng)的配置的解析包括LVS的配置解析
檢查器測試如果realserver處于活動狀態(tài),則此測試以二進(jìn)制決策結(jié)束:從LVS拓?fù)渲袆h除或添加realserver。內(nèi)部檢查器設(shè)計是實時網(wǎng)絡(luò)軟件,它使用完全多線程的FSM設(shè)計(有限狀態(tài)機(jī))。此檢查器堆棧提供符合layer4到layer5 / 7測試結(jié)果的LVS拓?fù)洳僮鳌K谟筛高M(jìn)程監(jiān)視的獨立進(jìn)程中運行。
IPVS包裝器:
借助于Checkers實現(xiàn)后端lvs主機(jī)的健康狀態(tài)檢測。
此框架用于將規(guī)則發(fā)送到內(nèi)核IPVS代碼。它提供Keepalived內(nèi)部數(shù)據(jù)表示和IPVS rule_user表示之間的轉(zhuǎn)換。它使用IPVS libipvs來保持與IPVS代碼的通用集成。來自LinuxVirtualServer.org OpenSource Project的Wensong提供的Linux內(nèi)核代碼。
IPVS:
為lvs生成ipvs規(guī)則的組件,是內(nèi)核級別的。
System call:
在VRRP框架中,它提供了在協(xié)議狀態(tài)轉(zhuǎn)換期間啟動額外腳本的能力。系統(tǒng)調(diào)用完成分叉進(jìn)程,以便不會影響全局調(diào)度計時器。
keepalived相關(guān)進(jìn)程
keepalived啟動后會有三個進(jìn)程
PID
111 Keepalived <-- Parent process monitoring children
112 \_ Keepalived <-- VRRP child
113 \_ Keepalived <-- Healthchecking child
父進(jìn)程:內(nèi)存管理,子進(jìn)程管理等等
子進(jìn)程:VRRP子進(jìn)程
子進(jìn)程:healthchecker子進(jìn)程
兩個子進(jìn)程都被系統(tǒng)WatchDog看管,兩個子進(jìn)程各自負(fù)責(zé)自己的事。
healthchecker子進(jìn)程負(fù)責(zé)檢查各自服務(wù)器的健康程度,例如HTTP,LVS等等,如果healthchecker子進(jìn)程檢查到MASTER上服務(wù)不可用了,就會通知本機(jī)上的兄弟VRRP子進(jìn)程,讓他刪除通告,并且去掉虛擬IP,轉(zhuǎn)換為BACKUP狀態(tài)。
keepalived腦裂問題
根據(jù)上述工作原理知道, 備節(jié)點接收不到報文時, 如兩者間的網(wǎng)絡(luò)不通了, 備節(jié)點就會啟動接管程序接管主節(jié)點的資源, 對外提供服務(wù), 表現(xiàn)形式就是備節(jié)點上出現(xiàn)了虛擬IP, 此時主節(jié)點也是持有虛擬IP的.
只要有虛擬IP存在, 就不可能完全規(guī)避這個問題. 也就是沒有虛擬IP了, 就沒有腦裂了, 那么節(jié)點又如何向外提供服務(wù)呢?
推薦自己寫腳本
寫一個while循環(huán),每輪ping網(wǎng)關(guān),累計連續(xù)失敗的次數(shù),當(dāng)連續(xù)失敗達(dá)到一定次數(shù)則運行service keepalived stop關(guān)閉keepalived服務(wù)。
如果發(fā)現(xiàn)又能夠ping通網(wǎng)關(guān),再重啟keepalived服務(wù)。最后在腳本開頭再加上腳本是否已經(jīng)運行的判斷邏輯,將該腳本加到crontab里面。
keepalived腦裂原因
一般來說腦裂問題有以下這幾種原因:
高可用服務(wù)器對之間心跳線鏈路發(fā)生故障,導(dǎo)致無法正常通信
心跳線壞了(包括斷了,老化)、
網(wǎng)卡及相關(guān)驅(qū)動壞了,IP配置及沖突問題(網(wǎng)卡直連)
心跳線之間的設(shè)備故障(網(wǎng)卡及交換機(jī))、
仲裁的機(jī)器出現(xiàn)問題(才用仲裁的方案)
高可用服務(wù)器上開啟了iptables防火墻,阻止了心跳傳消息輸
高可用服務(wù)器上心跳網(wǎng)卡地址等信息配置不正確,導(dǎo)致發(fā)送心跳失敗
其他服務(wù)配置不當(dāng)?shù)脑颍缧奶绞讲煌奶鴱V播沖突,軟件bug等
提示keepalive配置里同一VRRP實例如果virtual_router_id兩端參數(shù)配置不一致,也會導(dǎo)致腦裂問題
keepalived腦裂方案
在實際生產(chǎn)環(huán)境中,我們從以下方面防止腦裂:
同時使用串行電纜和以太網(wǎng)電纜連接、同時使用兩條心跳線路,這樣一條線路斷了,另外一條還是好的,依然能傳送心跳消息;
當(dāng)檢查腦裂時強行關(guān)閉一個心跳節(jié)點(這個功能需要特殊設(shè)備支持,如stonith、fence)相當(dāng)于備節(jié)點接收不到心跳消息,通過單獨的線路發(fā)送關(guān)機(jī)命令關(guān)閉主節(jié)點的電源;
做好對腦裂的監(jiān)控報警;
利用服務(wù)注冊機(jī)制避免腦裂發(fā)生。
常見解決方案:
如果開啟防火墻,一定要讓心跳消息通過,一般通過允許IP段的形式解決
可以拉一條以太網(wǎng)網(wǎng)線或者串口線作為主備節(jié)點心跳線路的冗余
開發(fā)檢測程序通過監(jiān)控軟件檢測腦裂
Keepalived優(yōu)缺點
keepalived與zookeeper都可以用來實現(xiàn)高可用,高可用一般跟負(fù)載均衡會一起考慮,所以通常也會考慮到相應(yīng)的負(fù)載均衡能力。
以下是Keepalived與Zookeeper的對比:
Keepalived:
優(yōu)點:簡單,基本不需要業(yè)務(wù)層面做任何事情,就可以實現(xiàn)高可用,主備容災(zāi)。而且容災(zāi)的宕機(jī)時間也比較短。
缺點:也是簡單,因為VRRP、主備切換都沒有什么復(fù)雜的邏輯,所以無法應(yīng)對某些特殊場景,比如主備通信鏈路出問題,會導(dǎo)致腦裂。同時,keepalived也不容易做負(fù)載均衡。
zookeeper:
優(yōu)點:可以支持高可用,負(fù)載均衡。本身是個分布式的服務(wù)。
缺點:跟業(yè)務(wù)結(jié)合的比較緊密。需要在業(yè)務(wù)代碼中寫好ZK使用的邏輯,比如注冊名字。拉取名字對應(yīng)的服務(wù)地址等。
keepalived配置文件
keepalived有三類配置區(qū)域
注意:不是三種配置文件,是一個配置文件里面三種不同類別的配置區(qū)域
全局配置(Global Configuration)
VRRPD配置
LVS配置
全局定義(global definition)配置范例:
! Configuration File for keepalived
global_defs {
router_id pg_xc1 #主備機(jī)必須不一樣
script_user postgres #如果是涉及到pg數(shù)據(jù)庫的相關(guān)操作,則需要選擇pg用戶
enable_script_security
}
vrrp_script check_pg_alived {
script “/etc/keepalived/check_pg.sh“ #檢查pg數(shù)據(jù)庫狀態(tài)的腳本,主要靠這個判斷是否進(jìn)行主備切換
interval 5
weight 30 #很重要,判斷主備切換的權(quán)重
fall 3 # require 3 failures for KO
}
VRRPD配置范例:
vrrp_instance VI_1 {
state BACKUP #BACKUP 必須大寫
nopreempt #no preempt為非搶占模式
interface enp0s8 #vip使用的網(wǎng)絡(luò)接口
virtual_router_id 10 #主備必須一樣
priority 80 #設(shè)置節(jié)點的優(yōu)先級,跟weight值組合使用,決定選擇哪個為主
advert_int 1
authentication {
auth_type PASS
auth_pass abcdefgh
}
VRRPD配置范例(續(xù)):
track_script {
check_pg_alived
}
virtual_ipaddress {
192.168.18.100 #vip
}
notify_master “/etc/keepalived/failover.sh“ #當(dāng)節(jié)點成為master時,執(zhí)行的腳本
notify_fault “/etc/keepalived/fault.sh“ #節(jié)點出現(xiàn)故障,執(zhí)行的腳本
}
Keepalived腳本編寫
1、腳本要放在/etc/keepalived目錄下,否則無法切換,可能是基于安全的原因。
2、腳本的權(quán)限很重要,權(quán)限太多執(zhí)行不成功,一般為755
監(jiān)控腳本check_pg.sh,對主從PG 進(jìn)行狀態(tài)監(jiān)控。
#!/bin/bash
export PGDATABASE=postgres
export PGPORT=1922
export PGUSER=postgres
export PGHOME=/usr/local/pg12.2
export PATH=$PGHOME/bin:$PATH:$HOME/bin
PGMIP=127.0.0.1
LOGFILE=/etc/keepalived/log/pg_status.log
SQL1='SELECT pg_is_in_recovery from pg_is_in_recovery();'
SQL2='update sr_delay set sr_date = now() where id =1;'
SQL3='SELECT 1;'
db_role=`echo $SQL1 | $PGHOME/bin/psql -d $PGDATABASE -U $PGUSER -At -w`
if [ $db_role == 't' ];
then
echo -e `date +"%F %T"` "Attention1:the current database is standby DB!" >> $LOGFILE
exit 0
fi
# 備庫不檢查存活,主庫更新狀態(tài)
echo $SQL3 | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w
if [ $? -eq 0 ] ;
then
echo $SQL2 | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w
echo -e `date +"%F %T"` "Success: update the master sr_delay successed!" >> $LOGFILE
exit 0
else
echo -e `date +"%F %T"` "Error:Is the server is running?" >> $LOGFILE
exit 1
fi
切換腳本failover.sh,主庫宕機(jī)后,keepalived調(diào)用執(zhí)行切換腳本。
#!/bin/bash
export PGPORT=1922
export PGUSER=postgres
export PG_OS_USER=postgres
export PGDATA=/usr/local/pg12.2/data
export PGDBNAME=postgres
export LANG=zh_CN.UTF-8
export PGPATH=/usr/local/pg12.2/bin
export PATH=$PATH:$PGPATH
PGMIP=127.0.0.1
LOGFILE=/etc/keepalived/log/failover.log
# master-to-slave delay
sr_allowed_delay_time=10
SQL1='select pg_is_in_recovery from pg_is_in_recovery();'
SQL2="select sr_date as delay_time from sr_delay where now()-sr_date < interval '60';"
db_role=`echo $SQL1 | psql -At -p $PGPORT -U $PGUSER -d $PGDBNAME -w`
db_sr_delaytime=`echo $SQL2 | psql -p $PGPORT -d $PGDBNAME -U $PGUSER -At -w`
SWITCH_COMMAND='pg_ctl promote -D $PGDATA'
# slave switchover to master if delay large than specical second
if [ $db_role == f ];then
echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is master database,cannot switched!" >> $LOGFILE
exit 0
fi
if [ $db_sr_delaytime -gt 0 ];then
echo -e `date +"%F %T"` "Attention:The current master database is health,the standby DB cannot switched!" >> $LOGFILE
exit 0
fi
echo $db_sr_delaytime
if [ -z $db_sr_delaytime ];then
echo -e `date +"%F %T"` "Attention:The current database is statndby,ready to switch master database!" >> $LOGFILE
pg_ctl promote -D $PGDATA
sed -i 's/primary_conninfo/#primary_conninfo/' $PGDATA/postgresql.auto.conf
pg_ctl restart -D $PGDATA
elif [ $? eq 0 ];then
echo -e `date +"%F %T"` "success:The current standby database successed to switched the primary PG database !" >> $LOGFILE
exit 0
else
echo -e `date +"%F %T"` "Error: the standby database failed to switch the primary PG database !,pelease checked it!" >> $LOGFILE
exit 1
fi
腳本fault.sh,keepalived 進(jìn)入錯誤狀態(tài)時執(zhí)行的腳本。
#!/bin/bash
LOGGFILE=/etc/keepalived/log/pg_db_fault.log
PGPORT=1922
PGMIP=10.10.10.111
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE
PGPID="`netstat -anp|grep $PGPORT |awk '{printf $7}'|cut -d/ -f1`"
service keepalived stop
kill -9 $PGPID
if [ $? eq 0 ];then
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE
service keepalived stop
exit 1
fi
Keepalived配置文件(備)
備庫配置:
1、把master的keepalived.conf文件和scripts目錄復(fù)制到備機(jī)。
2、修改keepalived.conf的router_id為pg_xc2,priority為80即可。
keepalived啟動及狀態(tài)查看
1、MASTER啟動keepalived 服務(wù)器
# service keepalived start
2、MASTER查看keepalived 狀態(tài)
# service keepalived status
3、查看后臺日志
# tail -f /var/log/messages
4、查看虛擬ip(ifconfig看不到虛擬IP):
# ip addr
Keepalived主備切換
1、把主庫給停掉
在原來的主節(jié)點可以看到報錯信息/var/log/message
2、此時第三方主機(jī)通過vip連接到的是新的主庫數(shù)據(jù)庫(說明vip和備庫切換到主庫成功)。
3、備庫切換到主庫,需要額外操作完成所有切換(或者在切換腳本實現(xiàn))
3.1、修改postgresql.auto.conf中的內(nèi)容,屏蔽掉之前當(dāng)作備庫時的配置,否則還是名義上的備庫。
3.2、重啟實例
pg_ctl restart
4、原主庫變成備庫需要手動下面的操作(需要手動操作)
4.1、編輯standby.signal文件
primary_conninfo = 'host=pg-xc2 port=1922 user=repl password=repl options=''-c wal_sender_timeout=5000'''
restore_command = 'cp /home/postgres/arch/%f %p'
archive_cleanup_command = 'pg_archivecleanup /home/postgres/arch %r'
standby_mode = on
4.2、編輯postgresql.auto.conf
primary_conninfo = 'host=pg-xc2 port=1922 user=repl password=repl options=''-c wal_sender_timeout=5000'''
restore_command = 'cp /home/postgres/arch/%f %p'
archive_cleanup_command = 'pg_archivecleanup /home/postgres/arch %r'
standby_mode = on
4.3、啟動數(shù)據(jù)庫
$ pg_ctl start
4.4、查看狀態(tài),并且通過第三方通過vip登錄時,此時登錄的是主庫。
select * from pg_is_in_recovery();
Keepalived主備競選規(guī)則
Keepalived的主備角色是可以改變的,其改變策略是:
1、如果weight值設(shè)置為正整數(shù),當(dāng)主庫的vrrp_script腳本執(zhí)行成功,其權(quán)值為priority+weight,而備庫的權(quán)值為priority+weight的值。那么此時主庫的和大于備庫的和。
2、如果weight值設(shè)置為正整數(shù),當(dāng)主庫的vrrp_script腳本執(zhí)行失敗,其權(quán)值為priority設(shè)置的值;而備庫的權(quán)值為priority+weight,如果其和大于主庫的priority,則備庫會變成主庫。
注意weight設(shè)置的值必須要大于主備priority之間的差。比如:主庫priority設(shè)為100,備庫priority設(shè)為80,weight則要設(shè)置大于100-80=20的差。
通過觀察/var/log/messages可以看到權(quán)值的變化。
keepalived切換模式
keepalived工作模式分為搶占和非搶占模式:
搶占模式:
通過優(yōu)先級(priority)來決定誰是master,優(yōu)先級高的為master,擁有虛擬IP。
這種模式有一個問題就是當(dāng)原主節(jié)點從故障中恢復(fù)后會重新獲得master角色搶占虛擬IP,這在有些場景可能會有問題(例如需要數(shù)據(jù)同步的場景,恢復(fù)后需要先同步數(shù)據(jù))。
搶占模式配置:preempt
非搶占模式:
發(fā)生故障才切換(比如keepalived進(jìn)程中斷),否則不切換。切換測試時先停止主庫,再關(guān)閉keepalived進(jìn)程。
非搶占模式配置:no preempt
切換模式選擇規(guī)則
如果是要實現(xiàn)主庫和vip都在同一臺主機(jī)的目標(biāo),那么應(yīng)該選擇非搶占模式,這樣子雖然主庫中斷,只要keepalived進(jìn)程還在,則不會發(fā)生主庫切換和vip切換。雖然切換響應(yīng)時間受到影響,但是保證通過VIP都能夠連接主庫上。
如果是搶占模式,會出現(xiàn)以下情況:
1、A是主庫(擁有vip),B是備庫
2、A主庫關(guān)閉,B變成主庫(搶到vip) #此時沒有問題。
3、A備庫啟動(根據(jù)搶占模式,搶到vip),B還是主庫(但是vip被搶占) #此時通過vip就會訪問到備庫。
4、如果一定要提高數(shù)據(jù)庫故障切換響應(yīng)時間,可以配置為搶占模式,但是當(dāng)備庫(原來的主庫)啟動前,得降低該主機(jī)的優(yōu)先級模式。也就是保證主庫所在的優(yōu)先級要高,vip不會被搶占。
?-->> 以上就是【PostgreSQL從小白到專家】第48講 - PG高可用實現(xiàn) 的內(nèi)容,歡迎一起探討交流,往期視頻及文檔,聯(lián)系CUUG客服
- 冉乃綱-老師CUUG金牌講師
- 冉老師 CUUG金牌講師 Oracle及RedHat高級講師、Unix/Linux 資深專家...[詳細(xì)了解老師]

- 陳衛(wèi)星-老師CUUG金牌講師
- 陳老師 CUUG金牌講師 精通Oracle管理、備份恢復(fù)、性能優(yōu)化 11年Ora...[詳細(xì)了解老師]
