基於開源元件打造Kafka自治叢集

語言: CN / TW / HK

本文介紹了Slack如何基於Chef、Terraform、CruiseControl、CMAK等開源工具打造自動化的Kafka基礎設施,實現Kafka叢集的自動化運維管理。原文:Building Self-driving Kafka clusters using open source components [1]

本文討論Slack如何使用Kafka,並且介紹了在過去四年中是怎樣由一個小而精幹的團隊構建並規模化執行Kafka自治叢集。

Slack利用Kafka構建釋出/訂閱系統,這一系統在所有重要的作業佇列(Job Queue) [2] 中都扮演重要角色,為Slack使用者的幾乎所有操作(例如在channel中展開連結,傳送通知,通知機器人,更新搜尋指數以及執行安全檢查)提供非同步作業執行框架。此外,Kafka還充當了在Slack上傳輸關鍵任務資料的神經系統,為日誌管道 [3] 、跟蹤資料 [4] 、計費管道、企業分析 [5] 和安全分析資料提供賦能。

Slack的Kafka之旅

早在2018年,幾個團隊就在各自的用例中採用了Kafka,並各自執行獨立的Kafka叢集。結果,不同團隊部署了不同版本的Kafka,並且都做了重複的部署、運維以及管理Kafka叢集的工作。

因此我們接手了將所有Kafka叢集標準化到一個版本下的專案,並由一個團隊進行管理。然而,由於我們團隊規模比較小,所以希望能夠儘可能讓運維自動化。

今天,Slack在10個Kafka叢集中管理著大約0.7 PB的資料,執行在數百個節點上,每秒處理數百個topic的數百萬條訊息,最高總吞吐量為6.5 Gbps。我們的Kafka基礎設施成本不僅由硬體決定,也由網路決定,而消費者包括了從繁重的批處理作業到像Job Queue [2] 這樣對延遲非常敏感的應用。

什麼是Kafka自治叢集?

Kafka是一款優秀的軟體,可以在Slack的數百個節點上執行。然而,如果你曾經嘗試過部署或管理Kafka,就會知道這不是一件容易的事。我們經常會因為broker速度慢、時不時出現的故障或容量管理問題而被要求提供支援。

Kafka運維自動化的目標是消除日常管理Kafka的運維開銷。

為此,我們為Kafka叢集確定了一些常見的運維任務,包括:

  • Kafka常規管理操作,如建立topic,更改分割槽計數以及重新分配分割槽給broker

  • 容量規劃操作,比如向叢集新增/刪除broker

  • 運維問題,如替換broker或部署新版本的軟體

  • 診斷Kafka叢集問題的on-call工作

  • 解釋Kafka消費者的消費速度是否足夠的客戶支援工作

因此,當我們遷移到新版本的Kafka叢集時,決定將運維方面的操作自動化,或者為使用者提供自服務。

Kafka 2 專案

我們統一了努力的方向,基於2.0.1版本實現了一個更加自動化的Kafka。我們的Kafka設定由以下元件組成:

構建、釋出、配置和部署:Chef和Terraform

我們使用Chef來管理基本作業系統,在主機上部署和配置Kafka軟體。我們的每個Kafka叢集都執行在不同的角色下,有自己的自定義配置,但共享相同的基礎配置。我們用Terraform模組為AWS中的Chef角色建立ASG,自動管理節點的開通和關閉。

以前部署Kafka主要是通過部署Debian Kafka包來管理。然而,我們發現部署預構建的包是很痛苦的,因為配置並不總是可靠。此外,由於我們無論如何都要重寫預設配置,所以Chef配置非常複雜。為了解決這些問題,我們在內部fork了Kafka倉庫,並建立了CI/CD流水線來構建Kafka並將靜態二進位制檔案釋出到S3。然後,Chef將從S3中提取二進位制檔案並部署,這一過程可重複執行。

Apache Zookeeper 3.4叢集一般來說是手動配置的,我們沒有一種自動的方式來確保每個Zookeeper節點都有唯一的ID,並且沒法在不重啟整個叢集的情況下重新分配ID。手動配置Zookeeper節點不僅冗長乏味(我們經常在普通的節點故障發生時被要求提供支援),而且很容易出錯,我們有可能會意外的在同一個AWS可用域啟動多個Zookeeper節點,從而增加影響半徑。為了減少乏味和錯誤,我們通過升級到Zookeeper 3.6來自動化這個過程,它在替換broker時不需要重啟叢集。當配置Zookeeper節點時,通過Consul KV自動分配唯一的ID。有了這兩個變化,就可以使用Terraform通過ASG開通Zookeeper叢集。

Kafka叢集穩定性優化

雖然上述配置有助於減輕自動化配置主機的痛苦,但我們仍然不得不負責叢集運維,比如將分割槽遷移到新的broker,併為負載重新平衡broker。此外,叢集運維操作影響到了客戶,導致他們需要上線支援或者達不到SLO。

經過分析,我們發現Kafka叢集中的熱點導致了不穩定性,不同的問題會引發熱點。

我們注意到,叢集中有幾百個Kafka topic,每個topic基於不同的負載有不同的分割槽計數。在常規運維過程中,一些broker會比其他broker處理更多資料。在叢集運維操作(比如新增/刪除broker)過程中,這些熱點又會加劇,從而導致資料消費延時。

為了解決熱點問題,我們希望均勻利用叢集中的所有broker。 我們將所有分割槽計數更改為broker計數的倍數,從而消除寫熱點。我們通過在所有節點上選擇均勻的消費者數量來平滑讀取熱點。只要所有分割槽都均勻分佈在叢集中,就可以在整個叢集中獲得可靠的利用率,並平滑讀寫速率。此外,當擴充套件broker或消費者時,將更新topic的分割槽計數,這樣分割槽計數仍將是代理計數的倍數,以確保均勻的利用率。

Kafka叢集熱點發現的另一個原因是 分割槽再平衡事件期間的複製頻寬消耗 。我們發現,複製頻寬不足的生產者或消費者消耗了大部分資源,尤其是在高峰時段。因此,我們限制了叢集可以使用的複製頻寬。然而,限制複製頻寬會導致叢集運維操作變慢,為此我們還修改了操作,每次只移動少量分割槽,這讓我們能夠持續做出許多小的改變。

儘管做出了這些努力,Kafka叢集仍然會由於部分故障失去平衡。為了將這些操作自動化,我們使用了出色的Cruise Control [6] 自動化套件(由LinkedIn構建)來自動化叢集平衡操作,並確保叢集中所有節點的平均利用率。總體來說,這些調優為叢集穩定執行提供了幫助。

混沌工程

考慮到將流量從舊叢集切換到新叢集的影響很大,我們使用暗流量對新叢集進行了混沌實驗。

測試涉及到叢集負載下各種資源。此外,我們能夠在受控的條件下終止broker,這有助於我們更好的理解代理失效的模式及其對生產者和消費者的影響。

在這些測試中,我們發現叢集恢復操作大多受到主機每秒傳送的資料包數量的限制。為了支援更快的恢復,我們在這些主機上啟用了jumbo frame。現在我們的Kafka例項擁有Slack基礎設施團隊中最高的每秒資料包利用率。

此外,這也幫助我們識別到使用Go Sarama庫的使用者在一些邊緣情況下的bug。在某些用例下,我們已經將這些客戶端遷移到Confluent Go,這也幫助我們標準化了跨語言的客戶端配置。在我們無法升級使用者的情況下,我們添加了適當的工作區和告警來監視這些用例。

在這些測試中,我們也意識到Zookeeper的問題會迅速演變成更大的Kafka問題。所以,我們為每個Kafka叢集配置了單獨的Zookeeper叢集來減少Zookeeper故障的影響半徑,雖然這麼做會稍微增加一些成本。

混沌實驗還幫助我們理解在實際故障期間可能出現的操作問題,並幫助我們對叢集做出更好的調優。

自服務Kafka叢集

在很多情況下,消費者團隊會向我們詢問或者提出增加/減少叢集容量的問題。其中一類問題是關於常規操作的,比如容量規劃,另一類是瞭解流水線的健康狀況。

此外,使用CLI工具來理解Kafka發生了什麼是很乏味的。所以,我們部署了kafka manager [7] ,讓每個人都能看到kafka叢集的元資料,比如broker列表和topic列表。Kafka Manager還幫助我們簡化了日常操作,比如建立新的主題和增加主題的分割槽數量。

為了提供對Kafka消費者健康狀況的操作可見性,我們部署了Kafka offset exporter [8] 的一個分支,將消費者的偏移資訊匯出為Prometheus度量指標。我們在這些資料的基礎上構建了儀表盤,向消費者實時提供每個topic、每個消費者的聚合消費指標。

為了減少知識孤島,我們將各種執行手冊標準化為單一的執行手冊,有助於將所有Kafka知識聚集到一個地方。此外,我們將多個Kafka儀表板整合到一個全域性儀表板中,用於所有Kafka叢集。

總之,這些自助式工具幫助客戶更好的理解資料,同時減少了團隊的運維開銷。這些工具還通過減少SSH到Kafka broker的需要改善了安全狀況。

升級Kafka叢集

為了升級Kafka叢集,我們決定不進行本地叢集升級。原因是我們沒有信心確保在升級視窗期間做到不停機,特別是在同時升級多個版本時。此外,我們沒有辦法驗證新叢集是否存在問題,特別是在更改底層硬體型別時。

為了解決這些問題,我們制定了一個新的叢集切換的升級策略。切換過程如下:

  • 啟動新叢集

  • 在新叢集上使用暗流量執行所有驗證測試

  • 停止向舊叢集生產資料

  • 開始向新叢集生產資料

  • 在保留視窗過期後關閉舊叢集

雖然這種策略有協調截斷消費者的缺點,但這是一種標準的運維過程,也適用於其他場景,如跨叢集移動topic以及測試新的EC2例項型別。

分裂Kafka主叢集

在投入時間讓Kafka可以自我維持以及提升了可靠性之後,我們可以有時間去做其他重要的功能,比如tracing [4] 。然而,即使我們已經做了所有工作,當系統達到臨界點時,仍然需要重新審視假設和容量。

我們在2021年初接近了這個點,90個broker叢集在網路吞吐量上達到了臨界點,最高達到40,000 pps。網路飽和導致下游管道延時,Kafka在正常工作負載下都難以跟上使用者,更不用說處理大流量峰值了。依賴日誌管道來除錯問題的開發者每天都會受到Kafka網路飽和的影響。

為了減少Kafka主叢集的負載,我們利用工具和自動化將大型topic分割到更小、更高效能的叢集中(從舊的d2 [9] 例項升級到現代的啟用了nitro的d3en例項 [10] )。比較兩個叢集之間類似的工作負載,新的叢集能夠在20個broker上實現類似的效能(每1,000 pps),大約提高了2.5倍的效率。

在將三個最大的topic移出主集群后,問題立即獲得了緩解。下面是一些當時的圖表,用來說明這項工作的影響。

這些峰值代表了消費者從最大的topic之一消費資料產生的滯後,每當滯後超過5億,就會因為日誌重新整理而破壞SLA。

主題遷移完成後,消費者延遲大大改善。我們的日誌延遲從最壞情況下的1.5小時減少到最壞情況下的3-4分鐘。此外,我們的日誌記錄流水線的on-call數量從一個月71個告警減少到一個月9個告警。相當大的進步!

更小的專用Kafka叢集也更容易管理,叢集運維操作可以完成得更快,而且由於干擾產生的噪音問題更少。

結論

使用Cruise Control、Kafka Manager、Chef和Terraform等開源元件,可以大規模執行自愈的Kafka叢集。此外,使用標準的SRE原則和適當的工具,如Kafka Manager和Kafka offset exporter,可以構建可靠、自助式和自治的Kafka。

我們從其他人的Kafka配置中受益匪淺,本著分享傳遞的精神,可以在Gibhub上找到我們的Kafka配置 [11]

過去幾年來,這一架構一直在Slack成功執行。展望未來,Kafka將在Slack扮演更重要的角色,它是新的變更資料捕獲(CDC,Change Data Capture)專案的一部分。新的CDC功能將支援Slack的許可權服務(Permission Service)的快取需求,該服務用於授權Slack中的操作,也將支援資料倉庫的近實時更新。為此,我們在Slack成立了一個新的資料流團隊來處理所有當前和未來的Kafka用例,並維護和支援Slack的所有Kafka叢集。

References: [1] Building Self-driving Kafka clusters using open source components: https://slack.engineering/building-self-driving-kafka-clusters-using-open-source-components/ [2] Scaling Slack's Job Queue: https://slack.engineering/scaling-slacks-job-queue [3] Data Wrangling at Slack: https://slack.engineering/data-wrangling-at-slack/ [4] Tracing at Slack Thinking in Causal Graphs: https://slack.engineering/tracing-at-slack-thinking-in-causal-graphs/ [5] Understand the data in your Slack analytics dashboard: https://slack.com/help/articles/360057638533-Understand-the-data-in-your-Slack-analytics-dashboard [6] Cruise Control: https://github.com/linkedin/cruise-control [7] Cluster Manager for Apache Kafka: https://github.com/yahoo/CMAK [8] Kafka offset exporter: https://github.com/echojc/kafka-offset-exporter [9] Now afailable d2 instance the latest generation of Amazon EC2 dense storage instances: https://aws.amazon.com/about-aws/whats-new/2015/03/now-available-d2-instances-the-latest-generation-of-amazon-ec2-dense-storage-instances/ [10] D3: https://aws.amazon.com/ec2/instance-types/d3/ [11] https://gist.github.com/mansu/dfe521987b48c060eb17cf3c5f7c3068

你好,我是俞凡,在Motorola做過研發,現在在Mavenir做技術工作,對通訊、網路、後端架構、雲原生、DevOps、CICD、區塊鏈、AI等技術始終保持著濃厚的興趣,平時喜歡閱讀、思考,相信持續學習、終身成長,歡迎一起交流學習。

微信公眾號:DeepNoMind