萬級K8s集羣背後etcd穩定性及性能優化實踐

背景與挑戰

隨着騰訊自研上雲及公有云用戶的迅速增加,一方面,騰訊雲容器服務TKE服務數量和核數大幅增加, 另外一方面咱們提供的容器服務類型(TKE託管及獨立集羣、EKS彈性集羣、edge邊緣計算集羣、mesh服務網格、serverless knative)也愈來愈豐富。各種容器服務類型背後的核心都是K8s,K8s核心的存儲etcd又統一由咱們基於K8s構建的etcd平臺進行管理。基於它咱們目前管理了千級etcd集羣,背後支撐了萬級K8s集羣。git

在萬級K8s集羣規模下的咱們如何高效保障etcd集羣的穩定性?github

etcd集羣的穩定性風險又來自哪裏?golang

咱們經過基於業務場景、歷史遺留問題、現網運營經驗等進行穩定性風險模型分析,風險主要來自舊TKE etcd架構設計不合理、etcd穩定性、etcd性能部分場景沒法知足業務、測試用例覆蓋不足、變動管理不嚴謹、監控是否全面覆蓋、隱患點是否能自動巡檢發現、極端災難故障數據安全是否能保障。web

前面所描述的etcd平臺已經從架構設計上、變動管理上、監控及巡檢、數據遷移、備份幾個方面程度解決了咱們管理的各種容器服務的etcd可擴展性、可運維性、可觀測性以及數據安全性,所以本文將重點描述咱們在萬級K8s場景下面臨的etcd內核穩定性及性能挑戰,好比:算法

  • 數據不一致
  • 內存泄露
  • 死鎖
  • 進程Crash
  • 大包請求致使etcd OOM及丟包
  • 較大數據量場景下啓動慢
  • 鑑權及查詢key數量、查詢指定數量記錄接口性能較差

img

本文將簡易描述咱們是如何發現、分析、復現、解決以上問題及挑戰,以及從以上過程當中咱們得到了哪些經驗及教訓,並將之應用到咱們的各種容器服務存儲穩定性保障中。數據庫

同時,咱們將解決方案所有貢獻、回饋給etcd開源社區, 截止目前咱們貢獻的30+ pr已所有合併到社區。騰訊雲TKE etcd團隊是etcd社區2020年上半年最活躍的貢獻團隊之一, 爲etcd的發展貢獻咱們的一點力量, 在這過程當中特別感謝社區AWS、Google、Ali等maintainer的支持與幫助。後端

穩定性優化案例剖析

從GitLab誤刪主庫丟失部分數據到GitHub數據不一致致使中斷24小時,再到號稱"不沉航母"的AWS S3故障數小時等,無一例外都是存儲服務。穩定性對於一個存儲服務、乃至一個公司的口碑而言相當重要,它決定着一個產品生與死。穩定性優化案例咱們將從數據不一致的嚴重性、兩個etcd數據不一致的bug、lease內存泄露、mvcc 死鎖、wal crash方面闡述,咱們是如何發現、分析、復現、解決以上case,並分享咱們從每一個case中的得到的收穫和反思,從中汲取經驗,防患於未然。api

數據不一致(Data Inconsistency)

談到數據不一致致使的大故障,就不得不詳細提下GitHub在18年一次因網絡設備的例行維護工做致使的美國東海岸網絡中心與東海岸主要數據中心之間的鏈接斷開。雖然網絡的連通性在43秒內得以恢復,可是短暫的中斷引起了一系列事件,最終致使GitHub 24小時11分鐘的服務降級,部分功能不可用。數組

GitHub使用了大量的MySQL集羣存儲GitHub的meta data,如issue、pr、page等等,同時作了東西海岸跨城級別的容災。故障核心緣由是網絡異常時GitHub的MySQL仲裁服務Orchestrator進行了故障轉移,將寫入數據定向到美國西海岸的MySQL集羣(故障前primary在東海岸),然而美國東海岸的MySQL包含一小段寫入,還沒有複製到美國西海岸集羣,同時故障轉移後因爲兩個數據中心的集羣如今都包含另外一個數據中心中不存在的寫入,所以又沒法安全地將主數據庫故障轉移回美國東海岸。安全

最終, 爲了保證保證用戶數據不丟失,GitHub不得不以24小時的服務降級爲代價來修復數據一致性。

數據不一致的故障嚴重性不言而喻,然而etcd是基於raft協議實現的分佈式高可靠存儲系統,咱們也並未作跨城容災,按理數據不一致這種看起來高大上bug咱們是很難遇到的。然而夢想是美好的,現實是殘酷的,咱們不只遇到了難以想象的數據不一致bug, 還一踩就是兩個,一個是重啓etcd有較低的機率觸發,一個是升級etcd版本時若是開啓了鑑權,在K8s場景下較大機率觸發。在詳細討論這兩個bug前,咱們先看看在K8s場景下etcd數據不一致會致使哪些問題呢?

  • 數據不一致最恐怖之處在於client寫入是成功的,但可能在部分節點讀取到空或者是舊數據,client沒法感知到寫入在部分節點是失敗的和可能讀到舊數據
  • 讀到空可能會致使業務Node消失、Pod消失、Node上Service路由規則消失,通常場景下,只會影響用戶變動的服務
  • 讀到老數據會致使業務變動不生效,如服務擴縮容、Service rs替換、變動鏡像異常等待,通常場景下,只會影響用戶變動的服務
  • 在etcd平臺遷移場景下,client沒法感知到寫入失敗,若校驗數據一致性也無異常時(校驗時鏈接到了正常節點),會致使遷移後整個集羣全面故障(apiserver鏈接到了異常節點),用戶的Node、部署的服務、lb等會被所有刪除,嚴重影響用戶業務

首先第一個不一致bug是重啓etcd過程當中遇到的,人工嘗試復現屢次皆失敗,分析、定位、復現、解決這個bug之路幾經波折,過程頗有趣並充滿挑戰,最終經過我對關鍵點增長debug日誌,編寫chaos monkey模擬各類異常場景、邊界條件,實現復現成功。最後的真兇居然是一個受權接口在重啓後重放致使鑑權版本號不一致,而後放大致使多版本數據庫不一致, 部分節點沒法寫入新數據, 影響全部v3版本的3年之久bug。

隨後咱們提交若干個相關pr到社區, 並所有合併了, 最新的etcd v3.4.9[1],v3.3.22[2]已修復此問題, 同時google的jingyih也已經提K8s issue和pr[3]將K8s 1.19的etcd client及server版本升級到最新的v3.4.9。此bug詳細可參考超凡同窗寫的文章三年之久的 etcd3 數據不一致 bug 分析

img

第二個不一致bug是在升級etcd過程當中遇到的,因etcd缺乏關鍵的錯誤日誌,故障現場有效信息很少,定位較困難,只能經過分析代碼和復現解決。然而人工嘗試復現屢次皆失敗,因而咱們經過chaos monkey模擬client行爲場景,將測試環境全部K8s集羣的etcd分配請求調度到咱們復現集羣,以及對比3.2與3.3版本差別,在可疑點如lease和txn模塊增長大量的關鍵日誌,並對etcd apply request失敗場景打印錯誤日誌。

經過以上措施,咱們比較快就復現成功了, 最終經過代碼和日誌發現是3.2版本與3.3版本在revoke lease權限上出現了差別,3.2無權限,3.3須要寫權限。當lease過時的時候,若是leader是3.2,那麼請求在3.3節點就會因無權限致使失敗,進而致使key數量不一致,mvcc版本號不一致,致使txn事務部分場景執行失敗等。最新的3.2分支也已合併咱們提交的修復方案,同時咱們增長了etcd核心過程失敗的錯誤日誌以提升數據不一致問題定位效率,完善了升級文檔,詳細說明了lease會在此場景下引發數據不一致性,避免你們再次採坑。

img

  • 從這兩個數據不一致bug中咱們得到了如下收穫和最佳實踐:
    • 算法理論數據一致性,不表明總體服務實現能保證數據一致性,目前業界對於這種基於日誌複製狀態機實現的分佈式存儲系統,沒有一個核心的機制能保證raft、wal、mvcc、snapshot等模塊協做不出問題,raft只能保證日誌狀態機的一致性,不能保證應用層去執行這些日誌對應的command都會成功
    • etcd版本升級存在必定的風險,須要仔細review代碼評估是否存在不兼容的特性,如若存在是否影響鑑權版本號及mvcc版本號,若影響則升級過程當中可能會致使數據不一致性,同時必定要灰度變動現網集羣
    • 對全部etcd集羣增長了一致性巡檢告警,如revision差別監控、key數量差別監控等
    • 定時對etcd集羣數據進行備份,再小几率的故障,根據墨菲定律均可能會發生,即使etcd自己雖具有完備的自動化測試(單元測試、集成測試、e2e測試、故障注入測試等),但測試用例仍有很多場景沒法覆蓋,咱們須要爲最壞的場景作準備(如3個節點wal、snap、db文件同時損壞),下降極端狀況下的損失, 作到可用備份數據快速恢復
    • etcd v3.4.4後的集羣灰度開啓data corruption檢測功能,當集羣出現不一致時,拒絕集羣寫入、讀取,及時止損,控制不一致的數據範圍
    • 繼續完善咱們的chaos monkey和使用etcd自己的故障注入測試框架functional,以協助咱們驗證、壓測新版本穩定性(長時間持續跑),復現隱藏極深的bug, 下降線上採坑的機率

內存泄露(OOM)

衆所周知etcd是golang寫的,而golang自帶垃圾回收機制也會內存泄露嗎?首先咱們得搞清楚golang垃圾回收的原理,它是經過後臺運行一個守護線程,監控各個對象的狀態,識別而且丟棄再也不使用的對象來釋放和重用資源,若你遲遲未釋放對象,golang垃圾回收不是萬能的,不泄露才怪。好比如下場景會致使內存泄露:

  • goroutine泄露
  • deferring function calls(如for循環裏面未使用匿名函數及時調用defer釋放資源,而是整個for循環結束才調用)
  • 獲取string/slice中的一段致使長string/slice未釋放(會共享相同的底層內存塊)
  • 應用內存數據結構管理不周致使內存泄露(如爲及時清理過時、無效的數據)

接下來看看咱們遇到的這個etcd內存泄露屬於哪一種狀況呢?事情起源於3月末的一個週末起牀後收到現網3.4集羣大量內存超過安全閾值告警,馬上排查了下發現如下現象:

  • QPS及流量監控顯示都較低,所以排除高負載及慢查詢因素
  • 一個集羣3個節點只有兩個follower節點出現異常,leader 4g,follower節點高達23G
  • goroutine、fd等資源未出現泄漏
  • go runtime memstats指標顯示各個節點申請的內存是一致的,可是follower節點go_memstats_heap_release_bytes遠低於leader節點,說明某數據結構可能長期未釋放
  • 生產集羣默認關閉了pprof,開啓pprof,等待復現, 並在社區上搜索釋放有相似案例, 結果發現有多個用戶1月份就報了,沒引發社區重視,使用場景和現象跟咱們同樣
  • 經過社區的heap堆棧快速定位到了是因爲etcd經過一個heap堆來管理lease的狀態,當lease過時時須要從堆中刪除,可是follower節點卻無此操做,所以致使follower內存泄露, 影響全部3.4版本。
  • 問題分析清楚後,我提交的修復方案是follower節點不須要維護lease heap,當leader發生選舉時確保新的follower節點能重建lease heap,老的leader節點則清空lease heap.

此內存泄露bug屬於內存數據結構管理不周致使的,問題修復後,etcd社區當即發佈了新的版本(v3.4.6+)以及K8s都當即進行了etcd版本更新。

img

img

從這個內存泄露bug中咱們得到了如下收穫和最佳實踐:

  • 持續關注社區issue和pr, 別人今天的問題極可能咱們明天就會遇到
  • etcd自己測試沒法覆蓋此類須要必定時間運行的才能觸發的資源泄露bug,咱們內部須要增強此類場景的測試與壓測
  • 持續完善、豐富etcd平臺的各種監控告警,機器留足足夠的內存buffer以扛各類意外的因素。

存儲層死鎖(Mvcc Deadlock)

  • 死鎖是指兩個或兩個以上的goroutine的執行過程當中,因爲競爭資源相互等待(通常是鎖)或因爲彼此通訊(chan引發)而形成的一種程序卡死現象,沒法對外提供服務。deadlock問題由於每每是在併發狀態下資源競爭致使的, 通常比較難定位和復現, 死鎖的性質決定着咱們必須保留好分析現場,不然分析、復現及其困難。

    那麼咱們是如何發現解決這個deadlock bug呢?問題起源於內部團隊在壓測etcd集羣時,發現一個節點忽然故障了,並且一直沒法恢復,沒法正常獲取key數等信息。收到反饋後,我經過分析卡住的etcd進程和查看監控,獲得如下結論:

    • 不通過raft及mvcc模塊的rpc請求如member list能夠正常返回結果,而通過的rpc請求所有context timeout
    • etcd health健康監測返回503,503的報錯邏輯也是通過了raft及mvcc
    • 經過tcpdump和netstat排除raft網絡模塊異常,可疑目標縮小到mvcc
    • 分析日誌發現卡住的時候因數據落後leader較多,接收了一個數據快照,而後執行更新快照的時候卡住了,沒有輸出快照加載完畢的日誌,同時確認日誌未丟失
    • 排查快照加載的代碼,鎖定幾個可疑的鎖和相關goroutine,準備獲取卡住的goroutine堆棧
    • 經過kill或pprof獲取goroutine堆棧,根據goroutine卡住的時間和相關可疑點的代碼邏輯,成功找到兩個相互競爭資源的goroutine,其中一個正是執行快照加載,重建db的主goroutine,它獲取了一把mvcc鎖等待全部異步任務結束,而另一個goroutine則是執行歷史key壓縮任務,當它收到stop的信號後,馬上退出,調用一個compactBarrier邏輯,而這個邏輯又偏偏須要獲取mvcc鎖,所以出現死鎖,堆棧以下。

img

這個bug也隱藏了好久,影響全部etcd3版本,在集羣中寫入量較大,某落後的較多的節點執行了快照重建,同時此時又偏偏在作歷史版本壓縮,那就會觸發。我提交的修復PR目前也已經合併到3.3和3.4分支中,新的版本已經發布(v3.3.21+/v3.4.8+)。

img

從這個死鎖bug中咱們得到了如下收穫和最佳實踐:

  • 多併發場景的組合的etcd自動化測試用例覆蓋不到,也較難構建,所以也容易出bug, 是否還有其餘相似場景存在一樣的問題?須要參與社區一塊兒繼續提升etcd測試覆蓋率(etcd以前官方博客介紹一大半代碼已是測試代碼),才能避免此類問題。
  • 監控雖然能及時發現異常節點宕機,可是死鎖這種場景以前咱們不會自動重啓etcd,所以須要完善咱們的健康探測機制(好比curl /health來判斷服務是否正常),出現死鎖時可以保留堆棧、自動重啓恢復服務。
  • 對於讀請求較高的場景,需評估3節點集羣在一節點宕機後,剩餘兩節點提供的QPS容量是否可以支持業務,若不夠則考慮5節點集羣。

Wal crash(Panic)

panic是指出現嚴重運行時和業務邏輯錯誤,致使整個進程退出。panic對於咱們而言並不陌生,咱們在現網遇到過幾回,最先遭遇的不穩定性因素就是集羣運行過程當中panic了。

雖然說咱們3節點的etcd集羣是能夠容忍一個節點故障,可是crash瞬間對用戶依然有影響,甚至出現集羣撥測鏈接失敗。

咱們遇到的第一個crash bug,是發現集羣連接數較多的時候有必定的機率出現crash, 而後根據堆棧查看社區已有人報grpc crash(issue)[4], 緣由是etcd依賴的組件grpc-go出現了grpc crash(pr)[5],而最近咱們遇到的crash bug[6]是v3.4.8/v3.3.21新版本發佈引發的,這個版本跟咱們有很大關係,咱們貢獻了3個PR到這個版本,佔了一大半以上, 那麼這個crash bug是如何產生以及復現呢?會不會是咱們本身的鍋呢?

  • 首先crash報錯是walpb: crc mismatch, 而咱們並未提交代碼修改wal相關邏輯,排除本身的鍋。
  • 其次經過review新版本pr, 目標鎖定到google一位大佬在修復一個wal在寫入成功後,而snapshot寫入失敗致使的crash bug的時候引入的.
  • 可是具體是怎麼引入的?pr中包含多個測試用例驗證新加邏輯,本地建立空集羣和使用存量集羣(數據比較小)也沒法復現.
  • 錯誤日誌信息太少,致使沒法肯定是哪一個函數報的錯,所以首先仍是加日誌,對各個可疑點增長錯誤日誌後,在咱們測試集羣隨便找了個老節點替換版本,而後很容易就復現了,並肯定是新加的驗證快照文件合法性的鍋,那麼它爲何會出現crc mismatch呢? 首先咱們來簡單瞭解下wal文件。
  • etcd任何通過raft的模塊的請求在寫入etcd mvcc db前都會經過wal文件持久化,若進程在apply command過程當中出現被殺等異常,重啓時可經過wal文件重放將數據補齊,避免數據丟失。wal文件包含各類請求命令如成員變化信息、涉及key的各個操做等,爲了保證數據完整性、未損壞,wal每條記錄都會計算其的crc32,寫入wal文件。重啓後解析wal文件過程當中,會校驗記錄的完整性,若是數據出現損壞或crc32計算算法出現變化則會出現crc32 mismatch.
  • 硬盤及文件系統並未出現異常,排除了數據損壞,通過深刻排查crc32算法的計算,發現是新增邏輯未處理crc32類型的數據記錄,它會影響crc32算法的值,致使出現差別,並且只有在當etcd集羣建立產生後的第一個wal文件被回收纔會觸發,所以對存量運行一段時間的集羣,100%復現。
  • 解決方案就是經過增長crc32算法的處理邏輯以及增長單元測試覆蓋wal文件被回收的場景,社區已合併併發布了新的3.4和3.3版本(v3.4.9/v3.3.22).

img

雖然這個bug是社區用戶反饋的,但從這個crash bug中咱們得到了如下收穫和最佳實踐:

  • 單元測試用例很是有價值,然而編寫完備的單元測試用例並不容易,須要考慮各種場景。
  • etcd社區對存量集羣升級、各版本之間兼容性測試用例幾乎是0,須要你們一塊兒來爲其舔磚加瓦,讓測試用例覆蓋更多場景。
  • 新版本上線內部流程標準化、自動化, 如測試環境壓測、混沌測試、不一樣版本性能對比、優先在非核心場景使用(如event)、灰度上線等流程必不可少。

配額及限速(Quota&QoS)

etcd面對一些大數據量的查詢(expensive read)和寫入操做時(expensive write),如全key遍歷(full keyspace fetch)、大量event查詢, list all Pod, configmap寫入等會消耗大量的cpu、內存、帶寬資源,極其容易致使過載,乃至雪崩。

然而,etcd目前只有一個極其簡單的限速保護,當etcd的commited index大於applied index的閾值大於5000時,會拒絕一切請求,返回Too Many Request,其缺陷很明顯,沒法精確的對expensive read/write進行限速,沒法有效防止集羣過載不可用。

爲了解決以上挑戰,避免集羣過載目前咱們經過如下方案來保障集羣穩定性:

  • 基於K8s apiserver上層限速能力,如apiserver默認寫100/s,讀200/s
  • 基於K8s resource quota控制不合理的Pod/configmap/crd數
  • 基於K8s controller-manager的-terminated-Pod-gc-threshold參數控制無效Pod數量(此參數默認值高達12500,有很大優化空間)
  • 基於K8s的apiserver各種資源可獨立的存儲的特性, 將event/configmap以及其餘核心數據分別使用不一樣的etcd集羣,在提升存儲性能的同時,減小核心主etcd故障因素
  • 基於event admission webhook對讀寫event的apiserver請求進行速率控制
  • 基於不一樣業務狀況,靈活調整event-ttl時間,儘可能減小event數
  • 基於etcd開發QoS特性,目前也已經向社區提交了初步設計方案,支持基於多種對象類型設置QoS規則(如按grpcMethod、grpcMethod+請求key前綴路徑、traffic、cpu-intensive、latency)
  • 經過多維度的集羣告警(etcd集羣lb及節點自己出入流量告警、內存告警、精細化到每一個K8s集羣的資源容量異常增加告警、集羣資源讀寫QPS異常增加告警)來提早防範、規避可能出現的集羣穩定性問題

多維度的集羣告警在咱們的etcd穩定性保障中發揮了重要做用,屢次幫助咱們發現用戶和咱們自身集羣組件問題。用戶問題如內部某K8s平臺以前出現bug, 寫入大量的集羣CRD資源和client讀寫CRD QPS明顯偏高。咱們自身組件問題如某舊日誌組件,當集羣規模增大後,因日誌組件不合理的頻繁調用list Pod,致使etcd集羣流量高達3Gbps, 同時apiserver自己也出現5XX錯誤。

雖然經過以上措施,咱們能極大的減小因expensive read致使的穩定性問題,然而從線上實踐效果看,目前咱們仍然比較依賴集羣告警幫助咱們定位一些異常client調用行爲,沒法自動化的對異常client的進行精準智能限速,。etcd層因沒法區分是哪一個client調用,若是在etcd側限速會誤殺正常client的請求, 所以依賴apiserver精細化的限速功能實現。社區目前已在1.18中引入了一個API Priority and Fairness[7],目前是alpha版本,期待此特性早日穩定。

性能優化案例剖析

etcd讀寫性能決定着咱們能支撐多大規模的集羣、多少client併發調用,啓動耗時決定着咱們當重啓一個節點或因落後leader太多,收到leader的快照重建時,它從新提供服務須要多久?性能優化案例剖析咱們將從啓動耗時減小一半、密碼鑑權性能提高12倍、查詢key數量性能提高3倍等來簡單介紹下如何對etcd進行性能優化。

啓動耗時及查詢key數量、查詢指定記錄數性能優化

當db size達到4g時,key數量百萬級別時,發現重啓一個集羣耗時居然高達5分鐘, key數量查詢也是超時,調整超時時間後,發現高達21秒,內存暴漲6G。同時查詢只返回有限的記錄數的場景(如業務使用etcd grpc-proxy來減小watch數,etcd grpc proxy在默認建立watch的時候,會發起對watch路徑的一次limit讀查詢),依然耗時很高且有巨大的內存開銷。因而週末空閒的時候我對這幾個問題進行了深刻調查分析,啓動耗時到底花在了哪裏?是否有優化空間?查詢key數量爲什麼如何耗時,內存開銷如此之大?

帶着這些問題對源碼進行了深刻分析和定位,首先來看查詢key數和查詢只返回指定記錄數的耗時和內存開銷極大的問題,分析結論以下:

  • 查詢key數量時etcd以前實現是遍歷整個內存btree,把key對應的revision存放在slice數組裏面
  • 問題就在於key數量較多時,slice擴容涉及到數據拷貝,以及slice也須要大量的內存開銷
  • 所以優化方案是新增一個CountRevision來統計key的數量便可,不須要使用slice,此方案優化後性能從21s下降到了7s,同時無任何內存開銷
  • 對於查詢指定記錄數據耗時和內存開銷很是大的問題,經過分析發現是limit記錄數並未下推到索引層,經過將查詢limit參數下推到索引層,大數據場景下limit查詢性能提高百倍,同時無額外的內存開銷。

img

img

再看啓動耗時問題太高的問題,經過對啓動耗時各階段增長日誌,獲得如下結論:

  • 啓動的時候機器上的cpu資源etcd進程未能充分利用
  • 9%耗時在打開後端db時,如將整個db文件mmap到內存
  • 91%耗時在重建內存索引btree上。當etcd收到一個請求Get Key時,請求被層層傳遞到了mvcc層後,它首先須要從內存索引btree中查找key對應的版本號,隨後從boltdb裏面根據版本號查出對應的value, 而後返回給client. 重建內存索引btree數的時候,偏偏是相反的流程,遍歷boltdb,從版本號0到最大版本號不斷遍歷,從value裏面解析出對應的key、revision等信息,重建btree,由於這個是個串行操做,因此操做及其耗時
  • 嘗試將串行構建btree優化成高併發構建,儘可能把全部覈計算力利用起來,編譯新版本測試後發現效果甚微,因而編譯新版本打印重建內存索引各階段的詳細耗時分析,結果發現瓶頸在內存btree的插入上,而這個插入擁有一個全局鎖,所以幾乎無優化空間
  • 繼續分析91%耗時發現重建內存索引居然被調用了兩次,第一處是爲了獲取一個mvcc的關鍵的consistent index變量,它是用來保證etcd命令不會被重複執行的關鍵數據結構,而咱們前面提到的一個數據不一致bug剛好也是跟consistent index有密切關係。
  • consistent index實現不合理,封裝在mvcc層,所以我前面提了一個pr將此特性重構,作爲了一個獨立的包,提供各種方法給etcdserver、mvcc、auth、lease等模塊調用。
  • 特性重構後的consistent index在啓動的時候就再也不須要經過重建內存索引數等邏輯來獲取了,優化成調用cindex包的方法快速獲取到consistent index,就將整個耗時從5min從縮短到2分30秒左右。所以優化同時依賴的consistent index特性重構,改動較大暫未backport到3.4/3.3分支,在將來3.5版本中、數據量較大時能夠享受到啓動耗時的顯著提高。

img

img

密碼鑑權性能提高12倍

某內部業務服務一直跑的好好的,某天client略微增多後,忽然現網etcd集羣出現大量超時,各類折騰,切換雲盤類型、切換部署環境、調整參數都不發揮做用,收到求助後,索要metrics和日誌後,通過一番排查後,獲得如下結論:

  • 現象的確很詭異,db延時相關指標顯示沒任何異常,日誌無任何有效信息
  • 業務反饋大量讀請求超時,甚至能夠經過etcdctl客戶端工具簡單復現,但是metric對應的讀請求相關指標數居然是0
  • 引導用戶開啓trace日誌和metrics開啓extensive模式,開啓後發現無任何trace日誌,然而開啓extensive後,我發現耗時居然所有花在了Authenticate接口,業務反饋是經過密碼鑑權,而不是基於證書的鑑權
  • 嘗試讓業務同窗短暫關閉鑑權測試業務是否恢復,業務同窗找了一個節點關閉鑑權後,此節點馬上恢復了正常,因而選擇臨時經過關閉鑑權來恢復現網業務
  • 那鑑權爲何耗時這麼慢?咱們對可疑之處增長了日誌,打印了鑑權各個步驟的耗時,結果發現是在等待鎖的過程當中出現了超時,而這個鎖爲何耗時這麼久呢?排查發現是由於加鎖過程當中會調用bcrpt加密函數計算密碼hash值,每次耗費60ms左右,數百併發下等待此鎖的最高耗時高達5s+。
  • 因而咱們編寫新版本將鎖的範圍減小,下降持鎖阻塞時間,用戶使用新版本後,開啓鑑權後,業務再也不超時,恢復正常。
  • 隨後咱們將修復方案提交給了社區,並編寫了壓測工具,測試提高後的性能高達近12倍(8核32G機器,從18/s提高到202/s),可是依然是比較慢,主要是鑑權過程當中涉及密碼校驗計算, 社區上也有用戶反饋密碼鑑權慢問題, 目前最新的v3.4.9版本已經包含此優化, 同時能夠經過調整bcrpt-cost參數來進一步提高性能。

img

本文簡單描述了咱們在管理萬級K8s集羣和其餘業務過程當中遇到的etcd穩定性和性能挑戰,以及咱們是如何定位、分析、復現、解決這些挑戰,並將解決方案貢獻給社區。

同時,詳細描述了咱們從這些挑戰中收穫了哪些寶貴的經驗和教訓,並將之應用到後續的etcd穩定性保障中,以支持更大規模的單集羣和總集羣數。

最後咱們面對萬級K8s集羣數, 千級的etcd集羣數, 10幾個版本分佈,其中很多低版本包含重要的潛在可能觸發的嚴重bug, 咱們還須要投入大量工做不斷優化咱們的etcd平臺,使其更智能、變動更加高效、安全、可控(如支持自動化、可控的集羣升級等), 同時數據安全也相當重要,目前騰訊雲TKE託管集羣咱們已經全面備份,獨立集羣的用戶後續將引導經過應用市場的etcd備份插件開啓定時備份到騰訊雲對象存儲COS上。

將來咱們將繼續緊密融入etcd的社區,爲etcd社區的發展貢獻咱們的力量,與社區一塊提高etcd的各個功能。

參考資料

[1]v3.4.9: https://github.com/etcd-io/etcd/releases/tag/v3.4.9

[2]v3.3.22: https://github.com/etcd-io/etcd/releases/tag/v3.3.22

[3]K8s issue和pr: https://github.com/kubernetes/kubernetes/issues/91266

[4]grpc crash(issue): https://github.com/etcd-io/etcd/issues/9956

[5]grpc crash(pr): https://github.com/grpc/grpc-go/pull/2695

[6]crash bug : https://github.com/etcd-io/etcd/issues/11918

[7]API Priority and Fairness: https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/20190228-priority-and-fairness.md

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!

相關文章
相關標籤/搜索