在 Kubernetes 環(huán)境下如何優(yōu)雅擴(kuò)縮容 Pulsar
背景
在整個(gè)大環(huán)境的降本增效的熏陶下,我們也不得不做好應(yīng)對(duì)方案。
根據(jù)對(duì)線上流量、存儲(chǔ)以及系統(tǒng)資源的占用,發(fā)現(xiàn)我們的 Pulsar 集群有許多的冗余,所以考慮進(jìn)行縮容從而減少資源浪費(fèi),最終也能省一些費(fèi)用。
不過在縮容之前很有必要先聊聊擴(kuò)容,Pulsar 一開始就是存算分離的架構(gòu)(更多關(guān)于 Pulsar 架構(gòu)的內(nèi)容本文不做過多介紹,感興趣的可以自行搜索),天然就非常適合 kubernetes 環(huán)境,也可以利用 kubernetes 的能力進(jìn)行快速擴(kuò)容。
擴(kuò)容
Pulsar 的擴(kuò)容相對(duì)比較簡(jiǎn)單,在 kubernetes 環(huán)境下只需要修改副本即可。
Broker
當(dāng)我們的 broker 層出現(xiàn)瓶頸時(shí)(比如 CPU、內(nèi)存負(fù)載較高、GC 頻繁時(shí))可以考慮擴(kuò)容。
計(jì)算層都擴(kuò)容了,也需要根據(jù)流量計(jì)算下存儲(chǔ)層是否夠用。
如果我們使用的是 helm 安裝的 Pulsar 集群,那只需要修改對(duì)于的副本數(shù)即可。
broker:
configuration
component: broker
replicaCount: 3->5
當(dāng)我們將副本數(shù)從 3 增加到 5 之后 kubernetes 會(huì)自動(dòng)拉起新增的兩個(gè) Pod,之后我們啥也不需要做了。
Pulsar 的負(fù)載均衡器會(huì)自動(dòng)感知到新增兩個(gè) broker 的加入,從而幫我們將一些負(fù)載高的節(jié)點(diǎn)的流量遷移到新增的節(jié)點(diǎn)中。
Bookkeeper
在介紹 bookkeeper 擴(kuò)容前先簡(jiǎn)單介紹些 Bookkeeper 的一些基本概念。
- Ensemble size (E):當(dāng)前 Bookkeeper 集群的節(jié)點(diǎn)數(shù)量
- Write quorum size (QW):一條消息需要寫入到幾個(gè) Bookkeeper 節(jié)點(diǎn)中
- ACK quorum size (QA):有多少個(gè) Bookkeeper 節(jié)點(diǎn) ACK 之后表示寫入成功
對(duì)應(yīng)到我們?cè)?nbsp;broker.conf 中的配置如下:
managedLedgerDefaultEnsembleSize: "2"
managedLedgerDefaultWriteQuorum: "2"
managedLedgerDefaultAckQuorum: "2"
這個(gè)三個(gè)參數(shù)表示一條消息需要同時(shí)寫入兩個(gè) Bookkeeper 節(jié)點(diǎn),同時(shí)都返回 ACK 之后才能表示當(dāng)前消息寫入成功。
從這個(gè)配置也可以看出,Bookkeeper 是多副本寫入模型,適當(dāng)?shù)慕档?QW 和 QA 的數(shù)量可以提高寫入吞吐率。
大部分場(chǎng)景下 Bookkeeper 有三個(gè)節(jié)點(diǎn)然后 E/QW/QA 都配置為 2 就可以滿足消息多副本寫入了。
多副本可以保證當(dāng)某個(gè)節(jié)點(diǎn)宕機(jī)后,這個(gè)節(jié)點(diǎn)的消息在其他節(jié)點(diǎn)依然有存放,消息讀取不會(huì)出現(xiàn)問題。
那什么情況下需要擴(kuò)容 Bookkeeper 了,當(dāng)然如果單個(gè) Bookkeeper 的負(fù)載較高也是可以擴(kuò)容的。
但我們當(dāng)時(shí)擴(kuò)容 Bookkeeper 的場(chǎng)景是想利用 Pulsar 的資源隔離功能。
因?yàn)橛胁糠謽I(yè)務(wù)的消息量明顯比高于其他的 topic,這樣會(huì)導(dǎo)致某個(gè) Broker 的負(fù)載較高,同時(shí)也可能影響到其他正常的 topic。
最好的方式就將這部分?jǐn)?shù)據(jù)用單獨(dú)的 broker 和 Bookkeeper 來承載,從而實(shí)現(xiàn)硬件資源的隔離。
這樣的需求如果使用其他消息隊(duì)列往往不太好實(shí)現(xiàn),到后來可能就會(huì)部署多個(gè)集群來實(shí)現(xiàn)隔離,但這樣也會(huì)增加運(yùn)維的復(fù)雜度。
好在 Pulsar 天然就支持資源隔離,只需要一個(gè)集群就可以實(shí)現(xiàn)不同 namespace 的流量隔離。
此時(shí)就可以額外擴(kuò)容幾個(gè) Bookkeeper 節(jié)點(diǎn)用于特定的 namespace 使用。
圖片
從上圖可以看到:我們可以將 broker 和 Bookkeeper 分別進(jìn)行分組,然后再配置對(duì)應(yīng)的 namespace,這樣就能實(shí)現(xiàn)資源隔離了。
更多關(guān)于資源隔離的細(xì)節(jié)本文就不過多贅述了。
鋪墊了這么多,其實(shí) Bookkeeper 的擴(kuò)容也蠻簡(jiǎn)單的:
bookkeeper:
component: bookie
metadata:
resources:
# requests:
# memory: 4Gi
# cpu: 2
replicaCount: 3->5
和 broker 擴(kuò)容類似,提高副本數(shù)量后,Pulsar 的元數(shù)據(jù)中心會(huì)感知到新的 Bookkeeper 節(jié)點(diǎn)加入,從而更新 broker 中的節(jié)點(diǎn)數(shù)據(jù),這樣就會(huì)根據(jù)我們配置的隔離策略分配流量。
縮容
其實(shí)本文的重點(diǎn)在于縮容,特別是 Bookkeeper 的縮容,這部分內(nèi)容我在互聯(lián)網(wǎng)上很少看到有人提及。
Broker
Broker 的縮容相對(duì)簡(jiǎn)單,因?yàn)榇嫠惴蛛x的特點(diǎn):broker 作為計(jì)算層是無狀態(tài)的,并不承載任何的數(shù)據(jù)。
其實(shí)是承載數(shù)據(jù)的,只是 Pulsar 會(huì)自動(dòng)遷移數(shù)據(jù),從而體感上覺得是無狀態(tài)的。
只是當(dāng)一個(gè) broker 下線后,它上面所綁定的 topic 會(huì)自動(dòng)轉(zhuǎn)移到其他在線的 broker 中。
這個(gè)過程會(huì)導(dǎo)致連接了這個(gè) broker 的 client 觸發(fā)重連,從而短暫的影響業(yè)務(wù)。
正因?yàn)?broker 的下線會(huì)導(dǎo)致 topic 的歸屬發(fā)生轉(zhuǎn)移,所以在下線前最好是先通過監(jiān)控面板觀察需要下線的 broker topic 是否過多,如果過多則可以先手動(dòng) unload 一些數(shù)據(jù),盡量避免一次性大批量的數(shù)據(jù)轉(zhuǎn)移。
圖片
觀察各個(gè)broker 的 topic 數(shù)量
Bookkeeper
而 Bookkeeper 的縮容則沒那么容易了,由于它是作為存儲(chǔ)層,本身是有狀態(tài)的,下線后節(jié)點(diǎn)上存儲(chǔ)的數(shù)據(jù)是需要遷移到其他的 Bookkeeper 節(jié)點(diǎn)中的。
不然就無法滿足之前提到的 Write quorum size (QW) 要求;因此縮容還有一個(gè)潛在條件需要滿足:
縮容后的 Bookkeeper 節(jié)點(diǎn)數(shù)量需要大于broker 中的配置:
managedLedgerDefaultEnsembleSize: "2"
managedLedgerDefaultWriteQuorum: "2"
managedLedgerDefaultAckQuorum: "2"
不然寫入會(huì)失敗,整個(gè)集群將變得不可用。
Pulsar 提供了兩種 Bookkeeper 的下線方案:
不需要遷移數(shù)據(jù)
其實(shí)兩種方案主要區(qū)別在于是否需要遷移數(shù)據(jù),第一種比較簡(jiǎn)單,就是不遷移數(shù)據(jù)的方案。
首先需要將 Bookkeeper 設(shè)置為 read-only 狀態(tài),此時(shí)該節(jié)點(diǎn)將不會(huì)接受寫請(qǐng)求,直到這個(gè) Bookkeeper 上的數(shù)據(jù)全部過期被回收后,我們就可以手動(dòng)下線該節(jié)點(diǎn)。
使用 forceReadOnlyBookie=true 可以強(qiáng)制將 Bookkeeper 設(shè)置為只讀。
但這個(gè)方案存在幾個(gè)問題:
- 下線時(shí)間不確定,如果該 Bookkeeper 上存儲(chǔ)的數(shù)據(jù)生命周期較長(zhǎng),則無法預(yù)估什么時(shí)候可以下線該節(jié)點(diǎn)。
- 該配置修改后需要重啟才能生效,在 kubernetes 環(huán)境中這些配置都是寫在了 configmap 中,一旦刷新后所有節(jié)點(diǎn)都會(huì)讀取到該配置,無法針對(duì)某一個(gè)節(jié)點(diǎn)生效;所以可能會(huì)出現(xiàn)將不該下線的節(jié)點(diǎn)設(shè)置為了只讀狀態(tài)。
但該方案的好處是不需要遷移數(shù)據(jù),人工介入的流程少,同樣也就減少了出錯(cuò)的可能。
比較適合于用虛擬機(jī)部署的集群。
遷移數(shù)據(jù)
第二種就是需要遷移數(shù)據(jù)的方案,更適用于 kubernetes 環(huán)境。
遷移原理
先來看看遷移的原理:
- 當(dāng) bookkeeper 停機(jī)后,AutoRecovery Auditor 會(huì)檢測(cè)到 zookeeper 節(jié)點(diǎn)/ledger/available 發(fā)生變化,將下線節(jié)點(diǎn)的 ledger 信息寫入到 zookeeper 的 /ledgers/underreplicated 節(jié)點(diǎn)中。
- AutoRecovery ReplicationWorker 會(huì)檢測(cè) /ledgers/underreplicated節(jié)點(diǎn)信息,然后輪訓(xùn)這些 ledger 信息從其他在線的 BK 中復(fù)制數(shù)據(jù)到?jīng)]有該數(shù)據(jù)的節(jié)點(diǎn),保證 QW 數(shù)量不變。
每復(fù)制一條數(shù)據(jù)后都會(huì)刪除 /ledgers/underreplicated 節(jié)點(diǎn)信息。
所有 /ledgers/underreplicated 被刪除后說明遷移任務(wù)完成。
- 執(zhí)行 bin/bookkeeper shell decommissionbookie 下線命令:
- 會(huì)等待 /ledgers/underreplicated 全部刪除
- 然后刪除 zookeeper 中的元數(shù)據(jù)
- 元數(shù)據(jù)刪除后 bookkeeper 才是真正下線成功,此時(shí) broker 才會(huì)感知到 Bookkeeper 下線。
AutoRecovery 是 Bookkeeper 提供的一個(gè)自動(dòng)恢復(fù)程序,他會(huì)在后臺(tái)檢測(cè)是否有數(shù)據(jù)需要遷移。
簡(jiǎn)單來說就是當(dāng)某個(gè)Bookkeeper 停機(jī)后,它上面所存儲(chǔ)的 ledgerID 會(huì)被寫入到元數(shù)據(jù)中心,此時(shí)會(huì)有一個(gè)單獨(dú)的線程來掃描這些需要遷移的數(shù)據(jù),最終將這些數(shù)據(jù)寫入到其他在線的 Bookkeeper 節(jié)點(diǎn)。
Bookkeeper 中的一些關(guān)鍵代碼:
圖片
圖片
下線步驟
下面來看具體的下線流程:
- 副本數(shù)-1
bin/bookkeeper shell listunderreplicated 檢測(cè)有多少 ledger 需要被遷移
- 執(zhí)行遠(yuǎn)程下線元數(shù)據(jù)
- nohup bin/bookkeeper shell decommissionbookie -bookieid bkid:3181 > bk.log 2>&1 &
- 這個(gè)命令會(huì)一直后臺(tái)運(yùn)行等待數(shù)據(jù)遷移完成,比較耗時(shí)
- 查看下線節(jié)點(diǎn)是否已被剔除
- bin/bookkeeper shell listbookies -a
- 循環(huán)第一步
第一步是檢測(cè)一些現(xiàn)在有多少數(shù)據(jù)需要遷移:bin/bookkeeper shell listunderreplicated 命令查看需要被遷移的 ledger 數(shù)據(jù)也是來自于 /ledgers/underreplicated節(jié)點(diǎn)
圖片
正常情況下是 0
第二步的命令會(huì)等待數(shù)據(jù)遷移完成后從 zookeeper 中刪除節(jié)點(diǎn)信息,這個(gè)進(jìn)程退出后表示下線成功。
圖片
這個(gè)命令最好是后臺(tái)執(zhí)行,并輸出日志到專門的文件,因?yàn)橹芷谳^長(zhǎng),很有可能終端會(huì)話已經(jīng)超時(shí)了。
我們登錄 zookeeper 可以看到需要遷移的 ledger 數(shù)據(jù):
bin/pulsar zookeeper-shell -server pulsar-zookeeper:2181
get /ledgers/underreplication/ledgers/0000/0000/0000/0002/urL0000000002
replica: "pulsar-test-2-bookie-0.pulsar-test-2-bookie.pulsar-test-2.svc.cluster.local:3181"
ctime: 1708507296519
underreplication 的節(jié)點(diǎn)路徑中存放了 ledgerId,通過 ledgerId 計(jì)算路徑:
圖片
圖片
注意事項(xiàng)
下線過程中我們可以查看 nohup bin/bookkeeper shell decommissionbookie -bookieid bkid:3181 > bk.log 2>&1 &這個(gè)命令寫入的日志來確認(rèn)遷移的進(jìn)度,日志中會(huì)打印當(dāng)前還有多少數(shù)量的 ledger 沒有遷移。
同時(shí)需要觀察 zookeeper、Bookkeeper 的資源占用情況。
因?yàn)檫w移過程中寫入大量數(shù)據(jù)到 zookeeper 節(jié)點(diǎn),同時(shí)遷移數(shù)時(shí)也會(huì)有大量流量寫入 Bookkeeper。
不要讓遷移過程影響到了正常的業(yè)務(wù)使用。
根據(jù)我的遷移經(jīng)驗(yàn)來看,通常 2w 的ledger 數(shù)據(jù)需要 2~3 小時(shí)不等的時(shí)間,具體情況還得根據(jù)你的集群來確認(rèn)。
回滾方案
當(dāng)然萬一遷移比較耗時(shí),或者影響了業(yè)務(wù)使用,所以還是要有一個(gè)回滾方案:
這里有一個(gè)大的前提:只要 BK 節(jié)點(diǎn)元數(shù)據(jù)、PVC(也就是磁盤中的數(shù)據(jù)) 沒有被刪除就可以進(jìn)行回滾。
所以只要上述的 decommissionbookie 命令沒有完全執(zhí)行完畢,我們就可以手動(dòng) kill 該進(jìn)程,然后恢復(fù)副本數(shù)據(jù)。
這樣恢復(fù)的 Bookkeeper 節(jié)點(diǎn)依然可以提供服務(wù),同時(shí)數(shù)據(jù)也還存在;只是浪費(fèi)了一些 autorecovery 的資源。
最后當(dāng) bookkeeper 成功下線后,我們需要?jiǎng)h除 PVC,不然如果今后需要擴(kuò)容的時(shí)候是無法啟動(dòng) bookkeeper 的,因?yàn)樵趩?dòng)過程中會(huì)判斷掛載的磁盤是否有數(shù)據(jù)。
總結(jié)
總的來說 Pulsar 的擴(kuò)縮容還是非常簡(jiǎn)單的,只是對(duì)于有狀態(tài)節(jié)點(diǎn)的數(shù)據(jù)遷移稍微復(fù)雜一些,但只要跟著流程走就不會(huì)有什么問題。
參考鏈接: