溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

發(fā)布時(shí)間:2022-01-11 17:43:01 來源:億速云 閱讀:223 作者:iii 欄目:云計(jì)算

本篇內(nèi)容介紹了“萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

在萬級(jí)K8s集群規(guī)模下的我們?nèi)绾胃咝ПU蟚tcd集群的穩(wěn)定性?

etcd集群的穩(wěn)定性風(fēng)險(xiǎn)又來自哪里?

我們通過基于業(yè)務(wù)場(chǎng)景、歷史遺留問題、現(xiàn)網(wǎng)運(yùn)營經(jīng)驗(yàn)等進(jìn)行穩(wěn)定性風(fēng)險(xiǎn)模型分析,風(fēng)險(xiǎn)主要來自舊TKE etcd架構(gòu)設(shè)計(jì)不合理、etcd穩(wěn)定性、etcd性能部分場(chǎng)景無法滿足業(yè)務(wù)、測(cè)試用例覆蓋不足、變更管理不嚴(yán)謹(jǐn)、監(jiān)控是否全面覆蓋、隱患點(diǎn)是否能自動(dòng)巡檢發(fā)現(xiàn)、極端災(zāi)難故障數(shù)據(jù)安全是否能保障。

前面所描述的etcd平臺(tái)已經(jīng)從架構(gòu)設(shè)計(jì)上、變更管理上、監(jiān)控及巡檢、數(shù)據(jù)遷移、備份幾個(gè)方面程度解決了我們管理的各類容器服務(wù)的etcd可擴(kuò)展性、可運(yùn)維性、可觀測(cè)性以及數(shù)據(jù)安全性,因此本文將重點(diǎn)描述我們?cè)谌f級(jí)K8s場(chǎng)景下面臨的etcd內(nèi)核穩(wěn)定性及性能挑戰(zhàn),比如:

  • 數(shù)據(jù)不一致

  • 內(nèi)存泄露

  • 死鎖

  • 進(jìn)程Crash

  • 大包請(qǐng)求導(dǎo)致etcd OOM及丟包

  • 較大數(shù)據(jù)量場(chǎng)景下啟動(dòng)慢

  • 鑒權(quán)及查詢key數(shù)量、查詢指定數(shù)量記錄接口性能較差

本文將簡(jiǎn)易描述我們是如何發(fā)現(xiàn)、分析、復(fù)現(xiàn)、解決以上問題及挑戰(zhàn),以及從以上過程中我們獲得了哪些經(jīng)驗(yàn)及教訓(xùn),并將之應(yīng)用到我們的各類容器服務(wù)存儲(chǔ)穩(wěn)定性保障中。

同時(shí),我們將解決方案全部貢獻(xiàn)、回饋給etcd開源社區(qū), 截止目前我們貢獻(xiàn)的30+ pr已全部合并到社區(qū)。騰訊云TKE etcd團(tuán)隊(duì)是etcd社區(qū)2020年上半年最活躍的貢獻(xiàn)團(tuán)隊(duì)之一, 為etcd的發(fā)展貢獻(xiàn)我們的一點(diǎn)力量, 在這過程中特別感謝社區(qū)AWS、Google、Ali等maintainer的支持與幫助。

穩(wěn)定性優(yōu)化案例剖析

從GitLab誤刪主庫丟失部分?jǐn)?shù)據(jù)到GitHub數(shù)據(jù)不一致導(dǎo)致中斷24小時(shí),再到號(hào)稱"不沉航母"的AWS S3故障數(shù)小時(shí)等,無一例外都是存儲(chǔ)服務(wù)。穩(wěn)定性對(duì)于一個(gè)存儲(chǔ)服務(wù)、乃至一個(gè)公司的口碑而言至關(guān)重要,它決定著一個(gè)產(chǎn)品生與死。穩(wěn)定性優(yōu)化案例我們將從數(shù)據(jù)不一致的嚴(yán)重性、兩個(gè)etcd數(shù)據(jù)不一致的bug、lease內(nèi)存泄露、mvcc 死鎖、wal crash方面闡述,我們是如何發(fā)現(xiàn)、分析、復(fù)現(xiàn)、解決以上case,并分享我們從每個(gè)case中的獲得的收獲和反思,從中汲取經(jīng)驗(yàn),防患于未然。

數(shù)據(jù)不一致(Data Inconsistency)

談到數(shù)據(jù)不一致導(dǎo)致的大故障,就不得不詳細(xì)提下GitHub在18年一次因網(wǎng)絡(luò)設(shè)備的例行維護(hù)工作導(dǎo)致的美國東海岸網(wǎng)絡(luò)中心與東海岸主要數(shù)據(jù)中心之間的連接斷開。雖然網(wǎng)絡(luò)的連通性在43秒內(nèi)得以恢復(fù),但是短暫的中斷引發(fā)了一系列事件,最終導(dǎo)致GitHub 24小時(shí)11分鐘的服務(wù)降級(jí),部分功能不可用。

GitHub使用了大量的MySQL集群存儲(chǔ)GitHub的meta data,如issue、pr、page等等,同時(shí)做了東西海岸跨城級(jí)別的容災(zāi)。故障核心原因是網(wǎng)絡(luò)異常時(shí)GitHub的MySQL仲裁服務(wù)Orchestrator進(jìn)行了故障轉(zhuǎn)移,將寫入數(shù)據(jù)定向到美國西海岸的MySQL集群(故障前primary在東海岸),然而美國東海岸的MySQL包含一小段寫入,尚未復(fù)制到美國西海岸集群,同時(shí)故障轉(zhuǎn)移后由于兩個(gè)數(shù)據(jù)中心的集群現(xiàn)在都包含另一個(gè)數(shù)據(jù)中心中不存在的寫入,因此又無法安全地將主數(shù)據(jù)庫故障轉(zhuǎn)移回美國東海岸。

最終, 為了保證保證用戶數(shù)據(jù)不丟失,GitHub不得不以24小時(shí)的服務(wù)降級(jí)為代價(jià)來修復(fù)數(shù)據(jù)一致性。

數(shù)據(jù)不一致的故障嚴(yán)重性不言而喻,然而etcd是基于raft協(xié)議實(shí)現(xiàn)的分布式高可靠存儲(chǔ)系統(tǒng),我們也并未做跨城容災(zāi),按理數(shù)據(jù)不一致這種看起來高大上bug我們是很難遇到的。然而夢(mèng)想是美好的,現(xiàn)實(shí)是殘酷的,我們不僅遇到了不可思議的數(shù)據(jù)不一致bug, 還一踩就是兩個(gè),一個(gè)是重啟etcd有較低的概率觸發(fā),一個(gè)是升級(jí)etcd版本時(shí)如果開啟了鑒權(quán),在K8s場(chǎng)景下較大概率觸發(fā)。在詳細(xì)討論這兩個(gè)bug前,我們先看看在K8s場(chǎng)景下etcd數(shù)據(jù)不一致會(huì)導(dǎo)致哪些問題呢?

  • 數(shù)據(jù)不一致最恐怖之處在于client寫入是成功的,但可能在部分節(jié)點(diǎn)讀取到空或者是舊數(shù)據(jù),client無法感知到寫入在部分節(jié)點(diǎn)是失敗的和可能讀到舊數(shù)據(jù)

  • 讀到空可能會(huì)導(dǎo)致業(yè)務(wù)Node消失、Pod消失、Node上Service路由規(guī)則消失,一般場(chǎng)景下,只會(huì)影響用戶變更的服務(wù)

  • 讀到老數(shù)據(jù)會(huì)導(dǎo)致業(yè)務(wù)變更不生效,如服務(wù)擴(kuò)縮容、Service rs替換、變更鏡像異常等待,一般場(chǎng)景下,只會(huì)影響用戶變更的服務(wù)

  • 在etcd平臺(tái)遷移場(chǎng)景下,client無法感知到寫入失敗,若校驗(yàn)數(shù)據(jù)一致性也無異常時(shí)(校驗(yàn)時(shí)連接到了正常節(jié)點(diǎn)),會(huì)導(dǎo)致遷移后整個(gè)集群全面故障(apiserver連接到了異常節(jié)點(diǎn)),用戶的Node、部署的服務(wù)、lb等會(huì)被全部刪除,嚴(yán)重影響用戶業(yè)務(wù)

首先第一個(gè)不一致bug是重啟etcd過程中遇到的,人工嘗試復(fù)現(xiàn)多次皆失敗,分析、定位、復(fù)現(xiàn)、解決這個(gè)bug之路幾經(jīng)波折,過程很有趣并充滿挑戰(zhàn),最終通過我對(duì)關(guān)鍵點(diǎn)增加debug日志,編寫chaos monkey模擬各種異常場(chǎng)景、邊界條件,實(shí)現(xiàn)復(fù)現(xiàn)成功。最后的真兇竟然是一個(gè)授權(quán)接口在重啟后重放導(dǎo)致鑒權(quán)版本號(hào)不一致,然后放大導(dǎo)致多版本數(shù)據(jù)庫不一致, 部分節(jié)點(diǎn)無法寫入新數(shù)據(jù), 影響所有v3版本的3年之久bug。

隨后我們提交若干個(gè)相關(guān)pr到社區(qū), 并全部合并了, 最新的etcd v3.4.9[1],v3.3.22[2]已修復(fù)此問題, 同時(shí)google的jingyih也已經(jīng)提K8s issue和pr[3]將K8s 1.19的etcd client及server版本升級(jí)到最新的v3.4.9。此bug詳細(xì)可參考超凡同學(xué)寫的文章三年之久的 etcd3 數(shù)據(jù)不一致 bug 分析。

第二個(gè)不一致bug是在升級(jí)etcd過程中遇到的,因etcd缺少關(guān)鍵的錯(cuò)誤日志,故障現(xiàn)場(chǎng)有效信息不多,定位較困難,只能通過分析代碼和復(fù)現(xiàn)解決。然而人工嘗試復(fù)現(xiàn)多次皆失敗,于是我們通過chaos monkey模擬client行為場(chǎng)景,將測(cè)試環(huán)境所有K8s集群的etcd分配請(qǐng)求調(diào)度到我們復(fù)現(xiàn)集群,以及對(duì)比3.2與3.3版本差異,在可疑點(diǎn)如lease和txn模塊增加大量的關(guān)鍵日志,并對(duì)etcd apply request失敗場(chǎng)景打印錯(cuò)誤日志。

通過以上措施,我們比較快就復(fù)現(xiàn)成功了, 最終通過代碼和日志發(fā)現(xiàn)是3.2版本與3.3版本在revoke lease權(quán)限上出現(xiàn)了差異,3.2無權(quán)限,3.3需要寫權(quán)限。當(dāng)lease過期的時(shí)候,如果leader是3.2,那么請(qǐng)求在3.3節(jié)點(diǎn)就會(huì)因無權(quán)限導(dǎo)致失敗,進(jìn)而導(dǎo)致key數(shù)量不一致,mvcc版本號(hào)不一致,導(dǎo)致txn事務(wù)部分場(chǎng)景執(zhí)行失敗等。最新的3.2分支也已合并我們提交的修復(fù)方案,同時(shí)我們?cè)黾恿薳tcd核心過程失敗的錯(cuò)誤日志以提高數(shù)據(jù)不一致問題定位效率,完善了升級(jí)文檔,詳細(xì)說明了lease會(huì)在此場(chǎng)景下引起數(shù)據(jù)不一致性,避免大家再次采坑。

  • 從這兩個(gè)數(shù)據(jù)不一致bug中我們獲得了以下收獲和最佳實(shí)踐:

    • 算法理論數(shù)據(jù)一致性,不代表整體服務(wù)實(shí)現(xiàn)能保證數(shù)據(jù)一致性,目前業(yè)界對(duì)于這種基于日志復(fù)制狀態(tài)機(jī)實(shí)現(xiàn)的分布式存儲(chǔ)系統(tǒng),沒有一個(gè)核心的機(jī)制能保證raft、wal、mvcc、snapshot等模塊協(xié)作不出問題,raft只能保證日志狀態(tài)機(jī)的一致性,不能保證應(yīng)用層去執(zhí)行這些日志對(duì)應(yīng)的command都會(huì)成功

    • etcd版本升級(jí)存在一定的風(fēng)險(xiǎn),需要仔細(xì)review代碼評(píng)估是否存在不兼容的特性,如若存在是否影響鑒權(quán)版本號(hào)及mvcc版本號(hào),若影響則升級(jí)過程中可能會(huì)導(dǎo)致數(shù)據(jù)不一致性,同時(shí)一定要灰度變更現(xiàn)網(wǎng)集群

    • 對(duì)所有etcd集群增加了一致性巡檢告警,如revision差異監(jiān)控、key數(shù)量差異監(jiān)控等

    • 定時(shí)對(duì)etcd集群數(shù)據(jù)進(jìn)行備份,再小概率的故障,根據(jù)墨菲定律都可能會(huì)發(fā)生,即便etcd本身雖具備完備的自動(dòng)化測(cè)試(單元測(cè)試、集成測(cè)試、e2e測(cè)試、故障注入測(cè)試等),但測(cè)試用例仍有不少場(chǎng)景無法覆蓋,我們需要為最壞的場(chǎng)景做準(zhǔn)備(如3個(gè)節(jié)點(diǎn)wal、snap、db文件同時(shí)損壞),降低極端情況下的損失, 做到可用備份數(shù)據(jù)快速恢復(fù)

    • etcd v3.4.4后的集群灰度開啟data corruption檢測(cè)功能,當(dāng)集群出現(xiàn)不一致時(shí),拒絕集群寫入、讀取,及時(shí)止損,控制不一致的數(shù)據(jù)范圍

    • 繼續(xù)完善我們的chaos monkey和使用etcd本身的故障注入測(cè)試框架functional,以協(xié)助我們驗(yàn)證、壓測(cè)新版本穩(wěn)定性(長時(shí)間持續(xù)跑),復(fù)現(xiàn)隱藏極深的bug, 降低線上采坑的概率

內(nèi)存泄露(OOM)

眾所周知etcd是golang寫的,而golang自帶垃圾回收機(jī)制也會(huì)內(nèi)存泄露嗎?首先我們得搞清楚golang垃圾回收的原理,它是通過后臺(tái)運(yùn)行一個(gè)守護(hù)線程,監(jiān)控各個(gè)對(duì)象的狀態(tài),識(shí)別并且丟棄不再使用的對(duì)象來釋放和重用資源,若你遲遲未釋放對(duì)象,golang垃圾回收不是萬能的,不泄露才怪。比如以下場(chǎng)景會(huì)導(dǎo)致內(nèi)存泄露:

  • goroutine泄露

  • deferring function calls(如for循環(huán)里面未使用匿名函數(shù)及時(shí)調(diào)用defer釋放資源,而是整個(gè)for循環(huán)結(jié)束才調(diào)用)

  • 獲取string/slice中的一段導(dǎo)致長string/slice未釋放(會(huì)共享相同的底層內(nèi)存塊)

  • 應(yīng)用內(nèi)存數(shù)據(jù)結(jié)構(gòu)管理不周導(dǎo)致內(nèi)存泄露(如為及時(shí)清理過期、無效的數(shù)據(jù))

接下來看看我們遇到的這個(gè)etcd內(nèi)存泄露屬于哪種情況呢?事情起源于3月末的一個(gè)周末起床后收到現(xiàn)網(wǎng)3.4集群大量?jī)?nèi)存超過安全閾值告警,立刻排查了下發(fā)現(xiàn)以下現(xiàn)象:

  • QPS及流量監(jiān)控顯示都較低,因此排除高負(fù)載及慢查詢因素

  • 一個(gè)集群3個(gè)節(jié)點(diǎn)只有兩個(gè)follower節(jié)點(diǎn)出現(xiàn)異常,leader 4g,follower節(jié)點(diǎn)高達(dá)23G

  • goroutine、fd等資源未出現(xiàn)泄漏

  • go runtime memstats指標(biāo)顯示各個(gè)節(jié)點(diǎn)申請(qǐng)的內(nèi)存是一致的,但是follower節(jié)點(diǎn)go_memstats_heap_release_bytes遠(yuǎn)低于leader節(jié)點(diǎn),說明某數(shù)據(jù)結(jié)構(gòu)可能長期未釋放

  • 生產(chǎn)集群默認(rèn)關(guān)閉了pprof,開啟pprof,等待復(fù)現(xiàn), 并在社區(qū)上搜索釋放有類似案例, 結(jié)果發(fā)現(xiàn)有多個(gè)用戶1月份就報(bào)了,沒引起社區(qū)重視,使用場(chǎng)景和現(xiàn)象跟我們一樣

  • 通過社區(qū)的heap堆棧快速定位到了是由于etcd通過一個(gè)heap堆來管理lease的狀態(tài),當(dāng)lease過期時(shí)需要從堆中刪除,但是follower節(jié)點(diǎn)卻無此操作,因此導(dǎo)致follower內(nèi)存泄露, 影響所有3.4版本。

  • 問題分析清楚后,我提交的修復(fù)方案是follower節(jié)點(diǎn)不需要維護(hù)lease heap,當(dāng)leader發(fā)生選舉時(shí)確保新的follower節(jié)點(diǎn)能重建lease heap,老的leader節(jié)點(diǎn)則清空lease heap.

此內(nèi)存泄露bug屬于內(nèi)存數(shù)據(jù)結(jié)構(gòu)管理不周導(dǎo)致的,問題修復(fù)后,etcd社區(qū)立即發(fā)布了新的版本(v3.4.6+)以及K8s都立即進(jìn)行了etcd版本更新。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

從這個(gè)內(nèi)存泄露bug中我們獲得了以下收獲和最佳實(shí)踐:

  • 持續(xù)關(guān)注社區(qū)issue和pr, 別人今天的問題很可能我們明天就會(huì)遇到

  • etcd本身測(cè)試無法覆蓋此類需要一定時(shí)間運(yùn)行的才能觸發(fā)的資源泄露bug,我們內(nèi)部需要加強(qiáng)此類場(chǎng)景的測(cè)試與壓測(cè)

  • 持續(xù)完善、豐富etcd平臺(tái)的各類監(jiān)控告警,機(jī)器留足足夠的內(nèi)存buffer以扛各種意外的因素。

存儲(chǔ)層死鎖(Mvcc Deadlock)

  • 死鎖是指兩個(gè)或兩個(gè)以上的goroutine的執(zhí)行過程中,由于競(jìng)爭(zhēng)資源相互等待(一般是鎖)或由于彼此通信(chan引起)而造成的一種程序卡死現(xiàn)象,無法對(duì)外提供服務(wù)。deadlock問題因?yàn)橥窃诓l(fā)狀態(tài)下資源競(jìng)爭(zhēng)導(dǎo)致的, 一般比較難定位和復(fù)現(xiàn), 死鎖的性質(zhì)決定著我們必須保留好分析現(xiàn)場(chǎng),否則分析、復(fù)現(xiàn)及其困難。

    **那么我們是如何發(fā)現(xiàn)解決這個(gè)deadlock bug呢?**問題起源于內(nèi)部團(tuán)隊(duì)在壓測(cè)etcd集群時(shí),發(fā)現(xiàn)一個(gè)節(jié)點(diǎn)突然故障了,而且一直無法恢復(fù),無法正常獲取key數(shù)等信息。收到反饋后,我通過分析卡住的etcd進(jìn)程和查看監(jiān)控,得到以下結(jié)論:

    • 不經(jīng)過raft及mvcc模塊的rpc請(qǐng)求如member list可以正常返回結(jié)果,而經(jīng)過的rpc請(qǐng)求全部context timeout

    • etcd health健康監(jiān)測(cè)返回503,503的報(bào)錯(cuò)邏輯也是經(jīng)過了raft及mvcc

    • 通過tcpdump和netstat排除raft網(wǎng)絡(luò)模塊異常,可疑目標(biāo)縮小到mvcc

    • 分析日志發(fā)現(xiàn)卡住的時(shí)候因數(shù)據(jù)落后leader較多,接收了一個(gè)數(shù)據(jù)快照,然后執(zhí)行更新快照的時(shí)候卡住了,沒有輸出快照加載完畢的日志,同時(shí)確認(rèn)日志未丟失

    • 排查快照加載的代碼,鎖定幾個(gè)可疑的鎖和相關(guān)goroutine,準(zhǔn)備獲取卡住的goroutine堆棧

    • 通過kill或pprof獲取goroutine堆棧,根據(jù)goroutine卡住的時(shí)間和相關(guān)可疑點(diǎn)的代碼邏輯,成功找到兩個(gè)相互競(jìng)爭(zhēng)資源的goroutine,其中一個(gè)正是執(zhí)行快照加載,重建db的主goroutine,它獲取了一把mvcc鎖等待所有異步任務(wù)結(jié)束,而另外一個(gè)goroutine則是執(zhí)行歷史key壓縮任務(wù),當(dāng)它收到stop的信號(hào)后,立刻退出,調(diào)用一個(gè)compactBarrier邏輯,而這個(gè)邏輯又恰恰需要獲取mvcc鎖,因此出現(xiàn)死鎖,堆棧如下。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

這個(gè)bug也隱藏了很久,影響所有etcd3版本,在集群中寫入量較大,某落后的較多的節(jié)點(diǎn)執(zhí)行了快照重建,同時(shí)此時(shí)又恰恰在做歷史版本壓縮,那就會(huì)觸發(fā)。我提交的修復(fù)PR目前也已經(jīng)合并到3.3和3.4分支中,新的版本已經(jīng)發(fā)布(v3.3.21+/v3.4.8+)。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

從這個(gè)死鎖bug中我們獲得了以下收獲和最佳實(shí)踐:

  • 多并發(fā)場(chǎng)景的組合的etcd自動(dòng)化測(cè)試用例覆蓋不到,也較難構(gòu)建,因此也容易出bug, 是否還有其他類似場(chǎng)景存在同樣的問題?需要參與社區(qū)一起繼續(xù)提高etcd測(cè)試覆蓋率(etcd之前官方博客介紹一大半代碼已經(jīng)是測(cè)試代碼),才能避免此類問題。

  • 監(jiān)控雖然能及時(shí)發(fā)現(xiàn)異常節(jié)點(diǎn)宕機(jī),但是死鎖這種場(chǎng)景之前我們不會(huì)自動(dòng)重啟etcd,因此需要完善我們的健康探測(cè)機(jī)制(比如curl /health來判斷服務(wù)是否正常),出現(xiàn)死鎖時(shí)能夠保留堆棧、自動(dòng)重啟恢復(fù)服務(wù)。

  • 對(duì)于讀請(qǐng)求較高的場(chǎng)景,需評(píng)估3節(jié)點(diǎn)集群在一節(jié)點(diǎn)宕機(jī)后,剩余兩節(jié)點(diǎn)提供的QPS容量是否能夠支持業(yè)務(wù),若不夠則考慮5節(jié)點(diǎn)集群。

Wal crash(Panic)

panic是指出現(xiàn)嚴(yán)重運(yùn)行時(shí)和業(yè)務(wù)邏輯錯(cuò)誤,導(dǎo)致整個(gè)進(jìn)程退出。panic對(duì)于我們而言并不陌生,我們?cè)诂F(xiàn)網(wǎng)遇到過幾次,最早遭遇的不穩(wěn)定性因素就是集群運(yùn)行過程中panic了。

雖說我們3節(jié)點(diǎn)的etcd集群是可以容忍一個(gè)節(jié)點(diǎn)故障,但是crash瞬間對(duì)用戶依然有影響,甚至出現(xiàn)集群撥測(cè)連接失敗。

我們遇到的第一個(gè)crash bug,是發(fā)現(xiàn)集群鏈接數(shù)較多的時(shí)候有一定的概率出現(xiàn)crash, 然后根據(jù)堆棧查看社區(qū)已有人報(bào)grpc crash(issue)[4], 原因是etcd依賴的組件grpc-go出現(xiàn)了grpc crash(pr)[5],而最近我們遇到的crash bug[6]是v3.4.8/v3.3.21新版本發(fā)布引起的,這個(gè)版本跟我們有很大關(guān)系,**我們貢獻(xiàn)了3個(gè)PR到這個(gè)版本,占了一大半以上, 那么這個(gè)crash bug是如何產(chǎn)生以及復(fù)現(xiàn)呢?**會(huì)不會(huì)是我們自己的鍋呢?

  • 首先crash報(bào)錯(cuò)是walpb: crc mismatch, 而我們并未提交代碼修改wal相關(guān)邏輯,排除自己的鍋。

  • 其次通過review新版本pr, 目標(biāo)鎖定到google一位大佬在修復(fù)一個(gè)wal在寫入成功后,而snapshot寫入失敗導(dǎo)致的crash bug的時(shí)候引入的.

  • 但是具體是怎么引入的?pr中包含多個(gè)測(cè)試用例驗(yàn)證新加邏輯,本地創(chuàng)建空集群和使用存量集群(數(shù)據(jù)比較小)也無法復(fù)現(xiàn).

  • 錯(cuò)誤日志信息太少,導(dǎo)致無法確定是哪個(gè)函數(shù)報(bào)的錯(cuò),因此首先還是加日志,對(duì)各個(gè)可疑點(diǎn)增加錯(cuò)誤日志后,在我們測(cè)試集群隨便找了個(gè)老節(jié)點(diǎn)替換版本,然后很容易就復(fù)現(xiàn)了,并確定是新加的驗(yàn)證快照文件合法性的鍋,那么它為什么會(huì)出現(xiàn)crc mismatch呢? 首先我們來簡(jiǎn)單了解下wal文件。

  • etcd任何經(jīng)過raft的模塊的請(qǐng)求在寫入etcd mvcc db前都會(huì)通過wal文件持久化,若進(jìn)程在apply command過程中出現(xiàn)被殺等異常,重啟時(shí)可通過wal文件重放將數(shù)據(jù)補(bǔ)齊,避免數(shù)據(jù)丟失。wal文件包含各種請(qǐng)求命令如成員變化信息、涉及key的各個(gè)操作等,為了保證數(shù)據(jù)完整性、未損壞,wal每條記錄都會(huì)計(jì)算其的crc32,寫入wal文件。重啟后解析wal文件過程中,會(huì)校驗(yàn)記錄的完整性,如果數(shù)據(jù)出現(xiàn)損壞或crc32計(jì)算算法出現(xiàn)變化則會(huì)出現(xiàn)crc32 mismatch.

  • 硬盤及文件系統(tǒng)并未出現(xiàn)異常,排除了數(shù)據(jù)損壞,經(jīng)過深入排查crc32算法的計(jì)算,發(fā)現(xiàn)是新增邏輯未處理crc32類型的數(shù)據(jù)記錄,它會(huì)影響crc32算法的值,導(dǎo)致出現(xiàn)差異,而且只有在當(dāng)etcd集群創(chuàng)建產(chǎn)生后的第一個(gè)wal文件被回收才會(huì)觸發(fā),因此對(duì)存量運(yùn)行一段時(shí)間的集群,100%復(fù)現(xiàn)。

  • 解決方案就是通過增加crc32算法的處理邏輯以及增加單元測(cè)試覆蓋wal文件被回收的場(chǎng)景,社區(qū)已合并并發(fā)布了新的3.4和3.3版本(v3.4.9/v3.3.22).

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

雖然這個(gè)bug是社區(qū)用戶反饋的,但從這個(gè)crash bug中我們獲得了以下收獲和最佳實(shí)踐:

  • 單元測(cè)試用例非常有價(jià)值,然而編寫完備的單元測(cè)試用例并不容易,需要考慮各類場(chǎng)景。

  • etcd社區(qū)對(duì)存量集群升級(jí)、各版本之間兼容性測(cè)試用例幾乎是0,需要大家一起來為其舔磚加瓦,讓測(cè)試用例覆蓋更多場(chǎng)景。

  • 新版本上線內(nèi)部流程標(biāo)準(zhǔn)化、自動(dòng)化, 如測(cè)試環(huán)境壓測(cè)、混沌測(cè)試、不同版本性能對(duì)比、優(yōu)先在非核心場(chǎng)景使用(如event)、灰度上線等流程必不可少。

配額及限速(Quota&QoS)

etcd面對(duì)一些大數(shù)據(jù)量的查詢(expensive read)和寫入操作時(shí)(expensive write),如全key遍歷(full keyspace fetch)、大量event查詢, list all Pod, configmap寫入等會(huì)消耗大量的cpu、內(nèi)存、帶寬資源,極其容易導(dǎo)致過載,乃至雪崩。

然而,etcd目前只有一個(gè)極其簡(jiǎn)單的限速保護(hù),當(dāng)etcd的commited index大于applied index的閾值大于5000時(shí),會(huì)拒絕一切請(qǐng)求,返回Too Many Request,其缺陷很明顯,無法精確的對(duì)expensive read/write進(jìn)行限速,無法有效防止集群過載不可用。

為了解決以上挑戰(zhàn),避免集群過載目前我們通過以下方案來保障集群穩(wěn)定性:

  • 基于K8s apiserver上層限速能力,如apiserver默認(rèn)寫100/s,讀200/s

  • 基于K8s resource quota控制不合理的Pod/configmap/crd數(shù)

  • 基于K8s controller-manager的-terminated-Pod-gc-threshold參數(shù)控制無效Pod數(shù)量(此參數(shù)默認(rèn)值高達(dá)12500,有很大優(yōu)化空間)

  • 基于K8s的apiserver各類資源可獨(dú)立的存儲(chǔ)的特性, 將event/configmap以及其他核心數(shù)據(jù)分別使用不同的etcd集群,在提高存儲(chǔ)性能的同時(shí),減少核心主etcd故障因素

  • 基于event admission webhook對(duì)讀寫event的apiserver請(qǐng)求進(jìn)行速率控制

  • 基于不同業(yè)務(wù)情況,靈活調(diào)整event-ttl時(shí)間,盡量減少event數(shù)

  • 基于etcd開發(fā)QoS特性,目前也已經(jīng)向社區(qū)提交了初步設(shè)計(jì)方案,支持基于多種對(duì)象類型設(shè)置QoS規(guī)則(如按grpcMethod、grpcMethod+請(qǐng)求key前綴路徑、traffic、cpu-intensive、latency)

  • 通過多維度的集群告警(etcd集群lb及節(jié)點(diǎn)本身出入流量告警、內(nèi)存告警、精細(xì)化到每個(gè)K8s集群的資源容量異常增長告警、集群資源讀寫QPS異常增長告警)來提前防范、規(guī)避可能出現(xiàn)的集群穩(wěn)定性問題

**多維度的集群告警在我們的etcd穩(wěn)定性保障中發(fā)揮了重要作用,多次幫助我們發(fā)現(xiàn)用戶和我們自身集群組件問題。**用戶問題如內(nèi)部某K8s平臺(tái)之前出現(xiàn)bug, 寫入大量的集群CRD資源和client讀寫CRD QPS明顯偏高。我們自身組件問題如某舊日志組件,當(dāng)集群規(guī)模增大后,因日志組件不合理的頻繁調(diào)用list Pod,導(dǎo)致etcd集群流量高達(dá)3Gbps, 同時(shí)apiserver本身也出現(xiàn)5XX錯(cuò)誤。

雖然通過以上措施,我們能極大的減少因expensive read導(dǎo)致的穩(wěn)定性問題,然而從線上實(shí)踐效果看,目前我們?nèi)匀槐容^依賴集群告警幫助我們定位一些異常client調(diào)用行為,無法自動(dòng)化的對(duì)異常client的進(jìn)行精準(zhǔn)智能限速,。etcd層因無法區(qū)分是哪個(gè)client調(diào)用,如果在etcd側(cè)限速會(huì)誤殺正常client的請(qǐng)求, 因此依賴apiserver精細(xì)化的限速功能實(shí)現(xiàn)。社區(qū)目前已在1.18中引入了一個(gè)API Priority and Fairness[7],目前是alpha版本,期待此特性早日穩(wěn)定。

性能優(yōu)化案例剖析

etcd讀寫性能決定著我們能支撐多大規(guī)模的集群、多少client并發(fā)調(diào)用,啟動(dòng)耗時(shí)決定著我們當(dāng)重啟一個(gè)節(jié)點(diǎn)或因落后leader太多,收到leader的快照重建時(shí),它重新提供服務(wù)需要多久?性能優(yōu)化案例剖析我們將從啟動(dòng)耗時(shí)減少一半、密碼鑒權(quán)性能提升12倍、查詢key數(shù)量性能提升3倍等來簡(jiǎn)單介紹下如何對(duì)etcd進(jìn)行性能優(yōu)化。

啟動(dòng)耗時(shí)及查詢key數(shù)量、查詢指定記錄數(shù)性能優(yōu)化

當(dāng)db size達(dá)到4g時(shí),key數(shù)量百萬級(jí)別時(shí),發(fā)現(xiàn)重啟一個(gè)集群耗時(shí)竟然高達(dá)5分鐘, key數(shù)量查詢也是超時(shí),調(diào)整超時(shí)時(shí)間后,發(fā)現(xiàn)高達(dá)21秒,內(nèi)存暴漲6G。同時(shí)查詢只返回有限的記錄數(shù)的場(chǎng)景(如業(yè)務(wù)使用etcd grpc-proxy來減少watch數(shù),etcd grpc proxy在默認(rèn)創(chuàng)建watch的時(shí)候,會(huì)發(fā)起對(duì)watch路徑的一次limit讀查詢),依然耗時(shí)很高且有巨大的內(nèi)存開銷。于是周末空閑的時(shí)候我對(duì)這幾個(gè)問題進(jìn)行了深入調(diào)查分析,啟動(dòng)耗時(shí)到底花在了哪里?是否有優(yōu)化空間?查詢key數(shù)量為何如何耗時(shí),內(nèi)存開銷如此之大?

帶著這些問題對(duì)源碼進(jìn)行了深入分析和定位,首先來看查詢key數(shù)和查詢只返回指定記錄數(shù)的耗時(shí)和內(nèi)存開銷極大的問題,分析結(jié)論如下:

  • 查詢key數(shù)量時(shí)etcd之前實(shí)現(xiàn)是遍歷整個(gè)內(nèi)存btree,把key對(duì)應(yīng)的revision存放在slice數(shù)組里面

  • 問題就在于key數(shù)量較多時(shí),slice擴(kuò)容涉及到數(shù)據(jù)拷貝,以及slice也需要大量的內(nèi)存開銷

  • 因此優(yōu)化方案是新增一個(gè)CountRevision來統(tǒng)計(jì)key的數(shù)量即可,不需要使用slice,此方案優(yōu)化后性能從21s降低到了7s,同時(shí)無任何內(nèi)存開銷

  • 對(duì)于查詢指定記錄數(shù)據(jù)耗時(shí)和內(nèi)存開銷非常大的問題,通過分析發(fā)現(xiàn)是limit記錄數(shù)并未下推到索引層,通過將查詢limit參數(shù)下推到索引層,大數(shù)據(jù)場(chǎng)景下limit查詢性能提升百倍,同時(shí)無額外的內(nèi)存開銷。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

再看啟動(dòng)耗時(shí)問題過高的問題,通過對(duì)啟動(dòng)耗時(shí)各階段增加日志,得到以下結(jié)論:

  • 啟動(dòng)的時(shí)候機(jī)器上的cpu資源etcd進(jìn)程未能充分利用

  • 9%耗時(shí)在打開后端db時(shí),如將整個(gè)db文件mmap到內(nèi)存

  • 91%耗時(shí)在重建內(nèi)存索引btree上。當(dāng)etcd收到一個(gè)請(qǐng)求Get Key時(shí),請(qǐng)求被層層傳遞到了mvcc層后,它首先需要從內(nèi)存索引btree中查找key對(duì)應(yīng)的版本號(hào),隨后從boltdb里面根據(jù)版本號(hào)查出對(duì)應(yīng)的value, 然后返回給client. 重建內(nèi)存索引btree數(shù)的時(shí)候,恰恰是相反的流程,遍歷boltdb,從版本號(hào)0到最大版本號(hào)不斷遍歷,從value里面解析出對(duì)應(yīng)的key、revision等信息,重建btree,因?yàn)檫@個(gè)是個(gè)串行操作,所以操作及其耗時(shí)

  • 嘗試將串行構(gòu)建btree優(yōu)化成高并發(fā)構(gòu)建,盡量把所有核計(jì)算力利用起來,編譯新版本測(cè)試后發(fā)現(xiàn)效果甚微,于是編譯新版本打印重建內(nèi)存索引各階段的詳細(xì)耗時(shí)分析,結(jié)果發(fā)現(xiàn)瓶頸在內(nèi)存btree的插入上,而這個(gè)插入擁有一個(gè)全局鎖,因此幾乎無優(yōu)化空間

  • 繼續(xù)分析91%耗時(shí)發(fā)現(xiàn)重建內(nèi)存索引竟然被調(diào)用了兩次,第一處是為了獲取一個(gè)mvcc的關(guān)鍵的consistent index變量,它是用來保證etcd命令不會(huì)被重復(fù)執(zhí)行的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),而我們前面提到的一個(gè)數(shù)據(jù)不一致bug恰好也是跟consistent index有密切關(guān)系。

  • consistent index實(shí)現(xiàn)不合理,封裝在mvcc層,因此我前面提了一個(gè)pr將此特性重構(gòu),做為了一個(gè)獨(dú)立的包,提供各類方法給etcdserver、mvcc、auth、lease等模塊調(diào)用。

  • 特性重構(gòu)后的consistent index在啟動(dòng)的時(shí)候就不再需要通過重建內(nèi)存索引數(shù)等邏輯來獲取了,優(yōu)化成調(diào)用cindex包的方法快速獲取到consistent index,就將整個(gè)耗時(shí)從5min從縮短到2分30秒左右。因此優(yōu)化同時(shí)依賴的consistent index特性重構(gòu),改動(dòng)較大暫未backport到3.4/3.3分支,在未來3.5版本中、數(shù)據(jù)量較大時(shí)可以享受到啟動(dòng)耗時(shí)的顯著提升。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

密碼鑒權(quán)性能提升12倍

某內(nèi)部業(yè)務(wù)服務(wù)一直跑的好好的,某天client略微增多后,突然現(xiàn)網(wǎng)etcd集群出現(xiàn)大量超時(shí),各種折騰,切換云盤類型、切換部署環(huán)境、調(diào)整參數(shù)都不發(fā)揮作用,收到求助后,索要metrics和日志后,經(jīng)過一番排查后,得到以下結(jié)論:

  • 現(xiàn)象的確很詭異,db延時(shí)相關(guān)指標(biāo)顯示沒任何異常,日志無任何有效信息

  • 業(yè)務(wù)反饋大量讀請(qǐng)求超時(shí),甚至可以通過etcdctl客戶端工具簡(jiǎn)單復(fù)現(xiàn),可是metric對(duì)應(yīng)的讀請(qǐng)求相關(guān)指標(biāo)數(shù)竟然是0

  • 引導(dǎo)用戶開啟trace日志和metrics開啟extensive模式,開啟后發(fā)現(xiàn)無任何trace日志,然而開啟extensive后,我發(fā)現(xiàn)耗時(shí)竟然全部花在了Authenticate接口,業(yè)務(wù)反饋是通過密碼鑒權(quán),而不是基于證書的鑒權(quán)

  • 嘗試讓業(yè)務(wù)同學(xué)短暫關(guān)閉鑒權(quán)測(cè)試業(yè)務(wù)是否恢復(fù),業(yè)務(wù)同學(xué)找了一個(gè)節(jié)點(diǎn)關(guān)閉鑒權(quán)后,此節(jié)點(diǎn)立刻恢復(fù)了正常,于是選擇臨時(shí)通過關(guān)閉鑒權(quán)來恢復(fù)現(xiàn)網(wǎng)業(yè)務(wù)

  • 那鑒權(quán)為什么耗時(shí)這么慢?我們對(duì)可疑之處增加了日志,打印了鑒權(quán)各個(gè)步驟的耗時(shí),結(jié)果發(fā)現(xiàn)是在等待鎖的過程中出現(xiàn)了超時(shí),而這個(gè)鎖為什么耗時(shí)這么久呢?排查發(fā)現(xiàn)是因?yàn)榧渔i過程中會(huì)調(diào)用bcrpt加密函數(shù)計(jì)算密碼hash值,每次耗費(fèi)60ms左右,數(shù)百并發(fā)下等待此鎖的最高耗時(shí)高達(dá)5s+。

  • 于是我們編寫新版本將鎖的范圍減少,降低持鎖阻塞時(shí)間,用戶使用新版本后,開啟鑒權(quán)后,業(yè)務(wù)不再超時(shí),恢復(fù)正常。

  • 隨后我們將修復(fù)方案提交給了社區(qū),并編寫了壓測(cè)工具,測(cè)試提升后的性能高達(dá)近12倍(8核32G機(jī)器,從18/s提升到202/s),但是依然是比較慢,主要是鑒權(quán)過程中涉及密碼校驗(yàn)計(jì)算, 社區(qū)上也有用戶反饋密碼鑒權(quán)慢問題, 目前最新的v3.4.9版本已經(jīng)包含此優(yōu)化, 同時(shí)可以通過調(diào)整bcrpt-cost參數(shù)來進(jìn)一步提升性能。

萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么

“萬級(jí)K8s集群穩(wěn)定性及性能優(yōu)化的方法是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

k8s
AI