Kafka 負載均衡在 vivo 的落地實踐
vivo 互聯網服務器團隊 - You Shuo
副本遷移是 Kafka 最高頻的操作,對於一個擁有幾十萬個副本的集羣,通過人工去完成副本遷移是一件很困難的事情。Cruise Control 作爲 Kafka 的運維工具,它包含了 Kafka 服務上下線、集羣內負載均衡、副本擴縮容、副本缺失修復以及節點降級等功能。顯然,Cruise Control 的出現,使得我們能夠更容易的運維大規模 Kafka 集羣。
備註:本文基於 Kafka 2.1.1 開展。
一、 Kafka 負載均衡
1.1 生產者負載均衡
Kafka 客戶端可以使用分區器依據消息的 key 計算分區,如果在發送消息時未指定 key,則默認分區器會基於 round robin 算法爲每條消息分配分區;
否則會基於 murmur2 哈希算法計算 key 的哈希值,並與分區數取模的到最後的分區編號。
很顯然,這並不是我們要討論的 Kafka 負載均衡,因爲生產者負載均衡看起來並不是那麼的複雜。
1.2 消費者負載均衡
考慮到消費者上下線、topic 分區數變更等情況,KafkaConsumer 還需要負責與服務端交互執行分區再分配操作,以保證消費者能夠更加均衡的消費 topic 分區,從而提升消費的性能;
Kafka 目前主流的分區分配策略有 2 種 (默認是 range,可以通過 partition.assignment.strategy 參數指定):
-
**range: ** 在保證均衡的前提下,將連續的分區分配給消費者,對應的實現是 RangeAssignor;
-
round-robin: 在保證均衡的前提下,輪詢分配,對應的實現是 RoundRobinAssignor;
-
0.11.0.0 版本引入了一種新的分區分配策略 StickyAssignor,其優勢在於能夠保證分區均衡的前提下儘量保持原有的分區分配結果,從而避免許多冗餘的分區分配操作,減少分區再分配的執行時間。
無論是生產者還是消費者,Kafka 客戶端內部已經幫我們做了負載均衡了,那我們還有討論負載均衡的必要嗎?答案是肯定的,因爲 Kafka 負載不均的主要問題存在於服務端而不是客戶端。
二、 Kafka 服務端爲什麼要做負載均衡
我們先來看一下 Kafka 集羣的流量分佈(圖 1)以及新上線機器後集羣的流量分佈(圖 2):
圖 1
圖 2
從圖 1 可以看出資源組內各 broker 的流量分佈並不是很均衡,而且由於部分 topic 分區集中分佈在某幾個 broker 上,當 topic 流量突增的時候,會出現只有部分 broker 流量突增。
這種情況下,我們就需要擴容 topic 分區或手動執行遷移動操作。
圖 2 是我們 Kafka 集羣的一個資源組擴容後的流量分佈情況,流量無法自動的分攤到新擴容的節點上。此時,就需要我們手動的觸發數據遷移,從而才能把流量引到新擴容的節點上。
2.1 Kafka 存儲結構
爲什麼會出現上述的問題呢?這個就需要從 Kafka 的存儲機制說起。
下圖是 Kafka topic 的存儲結構,其具體層級結構描述如下:
-
每個 broker 節點可以通過 logDirs 配置項指定多個 log 目錄,我們線上機器共有 12 塊盤,每塊盤都對應一個 log 目錄。
-
每個 log 目錄下會有若干個 [topic]-[x] 字樣的目錄,該目錄用於存儲指定 topic 指定分區的數據,對應的如果該 topic 是 3 副本,那在集羣的其他 broker 節點上會有兩個和該目錄同名的目錄。
-
客戶端寫入 kafka 的數據最終會按照時間順序成對的生成. index、.timeindex、.snapshot 以及. log 文件,這些文件保存在對應的 topic 分區目錄下。
-
爲了實現高可用目的,我們線上的 topic 一般都是 2 副本 / 3 副本,topic 分區的每個副本都分佈在不同的 broker 節點上,有時爲了降低機架故障帶來的風險,topic 分區的不同副本也會被要求分配在不同機架的 broker 節點上。
瞭解完 Kafka 存儲機制之後,我們可以清晰的瞭解到,客戶端寫入 Kafka 的數據會按照 topic 分區被路由到 broker 的不同 log 目錄下,只要我們不人工干預,那每次路由的結果都不會改變。因爲每次路由結果都不會改變,那麼問題來了:
隨着 topic 數量不斷增多,每個 topic 的分區數量又不一致,最終就會出現 topic 分區在 Kafka 集羣內分配不均的情況。
比如:topic1 是 10 個分區、topic2 是 15 個分區、topic3 是 3 個分區,我們集羣有 6 臺機器。那 6 臺 broker 上總會有 4 臺 broker 有兩個 topic1 的分區,有 3 臺 broke 上有 3 個 topic3 分區等等。
這樣的問題就會導致分區多的 broker 上的出入流量可能要比其他 broker 上要高,如果要考慮同一 topic 不同分區流量不一致、不同 topic 流量又不一致,再加上我們線上有 7000 個 topic、13 萬個分區、27 萬個副本等等這些。
這麼複雜的情況下,集羣內總會有 broker 負載特別高,有的 broker 負載特別低,當 broker 負載高到一定的時候,此時就需要我們的運維同學介入進來了,我們需要幫這些 broker 減減壓,從而間接的提升集羣總體的負載能力。
當集羣整體負載都很高,業務流量會持續增長的時候,我們會往集羣內擴機器。有些同學想擴機器是好事呀,這會有什麼問題呢?問題和上面是一樣的,因爲發往 topic 分區的數據,其路由結果不會改變,如果沒有人工干預的話,那新擴進來機器的流量就始終是 0,集羣內原來的 broker 負載依然得不到減輕。
三、如何對 Kafka 做負載均衡
3.1 人工生成遷移計劃和遷移
如下圖所示,我們模擬一個簡單的場景,其中的 T0-P0-R0 表示 topic - 分區 - 副本,假設 topic 各分區流量相同,假設每個分區 R0 副本是 leader。
我們可以看到,有兩個 topic T0 和 T1,T0 是 5 分區 2 副本(出入流量爲 10 和 5),T1 是 3 分區 2 副本(出入流量爲 5 和 1),如果嚴格考慮機架的話,那 topic 副本的分佈可能如下:
假設我們現在新擴入一臺 broker3(Rack2),如下圖所示:由於之前考慮了 topic 在機架上的分佈,所以從整體上看,broker2 的負載要高一些。
我們現在想把 broker2 上的一些分區遷移到新擴進來的 broker3 上,綜合考慮機架、流量、副本個數等因素,我們將 T0-P2-R0、T0-P3-R1、T0-P4-R0、T1-P0-R1 四個分區遷移到 broker3 上。
看起來還不是很均衡,我們再將 T1-P2 分區切換一下 leader:
經歷一番折騰後,整個集羣就均衡許多了,關於上面遷移副本和 leader 切換的命令參考如下:
Kafka 副本遷移腳本
# 副本遷移腳本:kafka-reassign-partitions.sh
# 1. 配置遷移文件
$ vi topic-reassignment.json
{"version":1,"partitions":[
{"topic":"T0","partition":2,"replicas":[broker3,broker1]},
{"topic":"T0","partition":3,"replicas":[broker0,broker3]},
{"topic":"T0","partition":4,"replicas":[broker3,broker1]},
{"topic":"T1","partition":0,"replicas":[broker2,broker3]},
{"topic":"T1","partition":2,"replicas":[broker2,broker0]}
]}
# 2. 執行遷移命令
bin/kafka-reassign-partitions.sh --throttle 73400320 --zookeeper zkurl --execute --reassignment-json-file topic-reassignment.json
# 3. 查看遷移狀態/清除限速配置
bin/kafka-reassign-partitions.sh --zookeeper zkurl --verify --reassignment-json-file topic-reassignment.json
3.2 使用負載均衡工具 - cruise control
經過對 Kafka 存儲結構、人工干預 topic 分區分佈等的瞭解後,我們可以看到 Kafka 運維起來是非常繁瑣的,那有沒有一些工具可以幫助我們解決這些問題呢?
答案是肯定的。
cruise control 是 LinkedIn 針對 Kafka 集羣運維困難問題而開發的一個項目,cruise control 能夠對 Kafka 集羣各種資源進行動態負載均衡,這些資源包括:CPU、磁盤使用率、入流量、出流量、副本分佈等,同時 cruise control 也具有首選 leader 切換和 topic 配置變更等功能。
3.2.1 cruise cotnrol 架構
我們先簡單介紹下 cruise control 的架構。
如下圖所示,其主要由 Monitor、Analyzer、Executor 和 Anomaly Detector四部分組成:
(來源:cruise control 官網)
(1)Monitor
Monitor 分爲客戶端 Metrics Reporter 和服務端 Metrics Sampler:
-
Metrics Reporter 實現了 Kafka 的指標上報接口 MetricsReporter,以特定的格式將原生的 Kafka 指標上報到
topic __CruiseControlMetrics 中。
-
Metrics Sampler 從__CruiseControlMetrics 中獲取原生指標後按照 broker 和分區級指標分別進行聚合,聚合後的指標包含了 broker、分區負載的均值、最大值等統計值,這些中間結果將被髮送
topic __KafkaCruiseControlModelTrainingSamples 和
__KafkaCruiseControlPartitionMetricSamples 中;
(2)Analyzer
Analyzer 作爲 cruise control 的核心部分,它根據用戶提供的優化目標和基於 Monitor 生成的集羣負載模型生成遷移計劃。
在 cruise control 中,“用戶提供的優化目標” 包括硬性目標和軟性目標兩大類,硬性目標是 Analyzer 在做預遷移的時候必須滿足的一類目標(例如:副本在遷移後必須滿足機架分散性原則),軟性目標則是儘可能要達到的目標,如果某一副本在遷移後只能同時滿足硬性目標和軟性目標中的一類,則以硬性目標爲主,如果存在硬性目標無法滿足的情況則本次分析失敗。
Analyzer 可能需要改進的地方:
-
由於 Monitor 生成的是整個集羣的負載模型,我們的 Kafka 平臺將 Kafka 集羣劃分爲多個資源組,不同資源組的資源利用率存在很大差別,所以原生的集羣負載模型不再適用於我們的應用場景。
-
大多數業務沒有指定 key 進行生產,所以各分區的負載偏差不大。如果 topic 分區副本均勻分佈在資源組內,則資源組也隨之變得均衡。
-
原生的 cruise control 會從集羣維度來展開均衡工作,指定資源組後可以從資源組維度展開均衡工作,但無法滿足跨資源組遷移的場景。
(3)Executor
Executor 作爲一個執行者,它執行 Analyzer 分析得到的遷移計劃。它會將遷移計劃以接口的形式分批提交到 Kafka 集羣上,後續 Kafka 會按照提交上來的遷移腳本執行副本遷移。
Executor 可能需要改進的地方:
cruise control 在執行副本遷移類的功能時,不能觸發集羣首選 leader 切換:有時在集羣均衡過程中出現了宕機重啓,以問題機器作爲首選 leader 的分區,其 leader 不能自動切換回來,造成集羣內其他節點壓力陡增,此時往往會產生連鎖反應。
(4)Anomaly Detector
Anomaly Detector 是一個定時任務,它會定期檢測 Kafka 集羣是否不均衡或者是否有副本缺失這些異常情況,當 Kafka 集羣出現這些情況後,Anomaly Detector 會自動觸發一次集羣內的負載均衡。
在後面的主要功能描述中,我會主要介紹 Monitor 和 Analyzer 的處理邏輯。
3.2.2 均衡 broker 出入流量 / 機器上下線均衡
對於 Kafka 集羣內各 broker 之間流量負載不均的原因、示意圖以及解決方案,我們在上面已經介紹過了,那麼 cruise control 是如何解決這個問題的。
其實 cruise control 均衡集羣的思路和我們手動去均衡集羣的思路大體一致,只不過它需要 Kafka 集羣詳細的指標數據,以這些指標爲基礎,去計算各 broker 之間的負載差距,並根據我們關注的資源去做分析,從而得出最終的遷移計劃。
以 topic 分區 leader 副本這類資源爲例:
服務端在接收到均衡請求後,Monitor 會先根據緩存的集羣指標數據構建一個能夠描述整個集羣負載分佈的模型。
下圖簡單描述了整個集羣負載信息的生成過程,smaple fetcher 線程會將獲取到的原生指標加載成可讀性更好的 Metric Sample,並對其進行進一步的加工,得到帶有 brokerid、partition 分區等信息的統計指標,這些指標保存在對應 broker、replica 的 load 屬性中,所以 broker 和 repilca 會包含流量負載、存儲大小、當前副本是否是 leader 等信息。
Analyzer 會遍歷我們指定的 broker(默認是集羣所有的 broker),由於每臺 broker 及其下面的 topic 分區副本都有詳細的指標信息,分析算法直接根據這些指標和指定資源對 broker 進行排序。
本例子的資源就是 topic 分區 leader 副本數量,接着 Analyzer 會根據我們提前設置的最大 / 最小閾值、離散因子等來判斷當前 broker 上某 topic 的 leader 副本數量是否需要增加或縮減,如果是增加,則變更 clustermodel 將負載比較高的 broker 上對應的 topic leader 副本遷移到當前 broker 上,反之亦然,在後面的改造點中,我們會對 Analyzer 的工作過程做簡單的描述。
遍歷過所有 broker,並且針對我們指定的所有資源都進行分析之後,就得出了最終版的 clustermodel,再與我們最初生成的 clustermodel 對比,就生成了 topic 遷移計劃。
cruise control 會根據我們指定的遷移策略分批次的將 topic 遷移計劃提交給 kafka 集羣執行。
遷移計劃示意圖如下:
3.2.3 首選 leader 切換
切換非首選 leader 副本,遷移計劃示意圖如下:
3.2.4 topic 配置變更
改變 topic 副本個數,遷移計劃示意圖如下:
3.3 改造 cruise control
3.3.1 指定資源組進行均衡
當集羣規模非常龐大的時候,我們想要均衡整個集羣就變得非常困難,往往均衡一次就需要半個月甚至更長時間,這在無形之中也加大了我們運維同學的壓力。
針對這個場景,我們對 cruise control 也進行了改造,我們從邏輯上將 Kafka 集羣劃分成多個資源組,使得業務擁有自己的資源組,當某個業務出現流量波動的時候,不會影響到其他的業務。
通過指定資源組,我們每次只需要對集羣的一小部分或多個部分進行均衡即可,大大縮短了均衡的時間,使得均衡的過程更加可控。
改造後的 cruise control 可以做到如下幾點:
-
通過均衡參數,我們可以只均衡某個或多個資源組的 broker。
-
更改 topic 配置,比如增加 topic 副本時,新擴的副本需要和 topic 原先的副本在同一個資源組內。
-
在資源組內分析 broker 上的資源是遷入還是遷出。對於每一類資源目標,cruise control 是計算資源組範圍內的統計指標,然後結合閾值和離散因子來分析 broker 是遷出資源還是遷入資源。
如下圖所示,我們將集羣、資源組、以及資源組下的 topic 這些元數據保存在數據庫中,Analyzer 得以在指定的資源組範圍內,對每個 broker 按照資源分佈目標做均衡分析。
例如:當對 broker-0 做均衡分析的時候,Analyzer 會遍歷 goals 列表,每個 goals 負責一類資源負載目標(cpu、入流量等),當均衡分析到達 goal-0 的時候,goal-0 會判斷 broker-0 的負載是否超出上限閾值,如果超出,則需要將 broker-0 的一些 topic 副本遷移到負載較低的 broker 上;反之,則需要將其他 broker 上的副本遷移到 broker-0 上。
其中,下面的 recheck goals 是排在後面的 goal 在做均衡分析的時候,在更新 cluster model 之前會判斷本次遷移會不會與之前的 goal 衝突,如果衝突,那就不更新 cluster model,當前的 goal 會繼續嘗試往其他 broker 上遷移,直到找到適合的遷移目標,然後更新 cluster model。
3.3.2 topic/topic 分區往指定 broker 上遷移
考慮這些場景:
-
一個項目下會有幾個資源組,由於業務變更,業務想要把 A 資源組下的 topic 遷移到 B 資源組。
-
業務想要把公共資源組的 topic 遷移到 C 資源組。
-
均衡完成之後,發現總有幾個 topic / 分區分佈不是很均勻。
面對這些場景,我們上面指定資源組進行均衡的功能就滿足不了我們的需求了。所以,我們針對上述場景改造後的 cruise control 可以做到如下幾點:
-
只均衡指定的 topic 或 topic 分區;
-
均衡的 topic 或 topic 分區只往指定的 broker 上遷移。
3.3.3 新增目標分析——topic 分區 leader 副本分散性
業務方大多都是沒有指定 key 進行發送數據的,所以同一 topic 每個分區的流量、存儲都是接近的,即每一個 topic 的各個分區的 leader 副本儘可能均勻的分佈在集羣的 broker 上時,那集羣的負載就會很均勻。
有同學會問了,topic 分區數並不總是能夠整除 broker 數量,那最後各 broker 的負載不還是不一致嘛?
答案是肯定的,只通過分區的 leader 副本還不能做到最終的均衡。
針對上述場景改造後的 cruise control 可以做到如下幾點:
-
新增一類資源分析:topic 分區 leader 副本分散性。
-
首先保證每個 topic 的 leader 副本和 follower 副本儘可能的均勻分佈在資源組的 broker 上。
-
在 2 的基礎上,副本會盡可能的往負載較低的 broker 上分佈。
如下圖所示,針對每一個 topic 的副本,Analyzer 會依次計算當前 broker 的 topic leader 數是否超過閾值上限,如果超過,則 Analyzer 會按照 topic 的 leader 副本數量、topic 的 follower 副本數量、broker 的出流量負載等來選出 AR 中的 follower 副本作爲新的 leader 進行切換,如果 AR 副本中也沒有符合要求的 broker,則會選擇 AR 列表以外的 broker。
3.3.4 最終均衡效果
下圖是某個資源組均衡後的流量分佈,各節點間流量偏差非常小,這種情況下,既可以增強集羣扛住流量異常突增的能力又可以提升集羣整體資源利用率和服務穩定性,降低成本。
3.4 安裝 / 部署 cruise control
3.4.1 客戶端部署:指標採集
【步驟 1】: 創建 Kafka 賬號,用於後面生產和消費指標數據
【步驟 2】: 創建 3 個 Kafka 內部 topic:a 是用來存儲 Kafka 服務原生 jmx 指標;b 和 c 分別是用來存儲 cruise control 處理過後的分區和模型指標;
【步驟 3】: 給步驟 1 創建的賬號授予讀 / 寫以及集羣的操作權限,用於讀 / 寫步驟 2 創建的 topic;
【步驟 4】: 修改 kafka 的 server.properties,增加如下配置:
在 Kafka 服務上配置採集程序
# 修改kafka的server.properties
metric.reporters=com.linkedin.kafka.cruisecontrol.metricsreporter.CruiseControlMetricsReporter
cruise.control.metrics.reporter.bootstrap.servers=域名:9092
cruise.control.metrics.reporter.security.protocol=SASL_PLAINTEXT
cruise.control.metrics.reporter.sasl.mechanism=SCRAM-SHA-256
cruise.control.metrics.reporter.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"ys\" password=\"ys\";
【步驟 5】: 添加 cruise-control-metrics-reporter 的 jar 包到 Kafka 的 lib 目錄下:mv cruise-control-metrics-reporter-2.0.104-SNAPSHOT.jar kafka_dir/lib/;
【步驟 6】: 重啓 Kafka 服務。
3.4.2 服務端部署:指標聚合 / 均衡分析
(1)到 https://github.com/linkedin/cruise-control 下載 zip 文件並解壓;
(2)將自己本地 cruise control 子模塊下生成的 jar 包替換 cruise control 的:
mv cruise-control-2.0.xxx-SNAPSHOT.jar
cruise-control/build/libs;
(3)修改 cruise control 配置文件,主要關注如下配置:
# 修改cruise control配置文件
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"ys\" password=\"ys\";
bootstrap.servers=域名:9092
zookeeper.connect=zkURL
(4)修改數據庫連接配置:
# 集羣id
cluster_id=xxx
db_url=jdbc:mysql://hostxxxx:3306/databasexxx
db_user=xxx
db_pwd=xxx
四、總結
通過以上的介紹,我們可以看出 Kafka 存在比較明顯的兩個缺陷:
-
Kafka 每個 partition replica 與機器的磁盤綁定,partition replica 由一系列的 Segment 組成,所以往往單分區存儲會佔用比較大的磁盤空間,對於磁盤會有很大壓力。
-
在集羣擴容 broker 時必須做 Rebalance,需要 broker 有良好的執行流程,保證沒有任何故障的情況下使得各 broker 負載均衡。
cruise control 就是針對 Kafka 集羣運維困難問題而誕生的,它能夠很好的解決 kafka 運維困難的問題。
參考文章:
1. linkedIn/cruise-control
2. Introduction to Kafka Cruise Control
3. Cloudera Cruise Control REST API Reference
4. http://dockone.io/article/2434664
5. https://www.zhenchao.org/2019/06/22/kafka/kafka-log-manage/
vivo 互聯網技術 分享 vivo 互聯網技術乾貨與沙龍活動,推薦最新行業動態與熱門會議。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Zio9V_XXUTNZ3-TDHCt6LQ