本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Kubernetes 效能測量和路線圖
無論您的容器編排系統多麼靈活可靠,最終您總有一些工作要做,並且希望它能快速完成。對於大問題,一個常見的答案是投入更多的機器來解決。畢竟,更多的計算力 = 更快,對嗎?
有趣的是,新增更多節點有點像火箭方程的暴政——在某些系統中,新增更多機器實際上會使您的處理速度變慢。然而,與火箭方程不同,我們可以做得更好。Kubernetes 在 v1.0 版本中支援多達 100 個節點的叢集。然而,我們的目標是到 2015 年底將支援的節點數量增加 10 倍。這篇博文將介紹我們目前的情況以及我們打算如何實現更高水平的效能。
我們測量什麼?
我們需要回答的第一個問題是:“Kubernetes 能夠管理一個 N 節點叢集意味著什麼?”使用者期望它能“合理快速地”處理所有操作,但我們需要一個精確的定義。我們決定根據以下兩個指標來定義效能和可擴充套件性目標:
1.“API 響應時間”:我們 99% 的 API 呼叫在 1 秒內返回。
2.“Pod 啟動時間”:99% 的 Pod(預拉取映象)在 5 秒內啟動。
請注意,對於“Pod 啟動時間”,我們明確假設執行 Pod 所需的所有映象都已預先拉取到將要執行的機器上。在我們的實驗中,不同映象之間存在高度可變性(網路吞吐量、映象大小等),這些變異與 Kubernetes 的整體效能關係不大。
選擇這些指標的決定是基於我們在 Google 每週啟動 20 億個容器的經驗。我們明確希望測量面向使用者流程的延遲,因為這才是客戶真正關心的。
我們如何測量?
為了監控效能改進和檢測迴歸,我們建立了持續測試基礎設施。每隔 2-3 小時,我們就會從 HEAD 建立一個 100 節點叢集,並在其上執行我們的可擴充套件性測試。我們使用一臺 GCE n1-standard-4(4 核,15GB 記憶體)機器作為主節點,使用 n1-standard-1(1 核,3.75GB 記憶體)機器作為節點。
在可擴充套件性測試中,我們明確只關注滿叢集情況(滿 N 節點叢集是指執行著 30 * N 個 Pod 的叢集),這是從效能角度來看最苛刻的場景。為了重現客戶可能實際操作的情況,我們執行以下步驟:
填充 Pod 和副本控制器以填滿叢集
生成一些負載(建立/刪除額外的 Pod 和/或副本控制器,擴充套件現有 Pod 和/或副本控制器等),並記錄效能指標
停止所有正在執行的 Pod 和副本控制器
抓取指標並檢查它們是否符合我們的預期
值得強調的是,測試的主要部分是在滿叢集(每節點 30 個 Pod,共 100 個節點)上進行的——在一個空叢集中啟動 Pod,即使它有 100 個節點,也會快得多。
為了測量 Pod 啟動延遲,我們使用了非常簡單的 Pod,只有一個容器執行“gcr.io/google_containers/pause:go”映象,該映象啟動後會一直休眠。該容器保證已預拉取到節點上(我們將其用作所謂的 Pod 基礎設施容器)。
效能資料
下表包含 100 節點叢集中 Pod 啟動時間的百分位數(第 50、90 和 99 百分位),叢集負載分別為 10%、25%、50% 和 100%。
10% 滿 | 25% 滿 | 50% 滿 | 100% 滿 | |
---|---|---|---|---|
第 50 百分位 | .90 秒 | 1.08 秒 | 1.33 秒 | 1.94 秒 |
第 90 百分位 | 1.29 秒 | 1.49 秒 | 1.72 秒 | 2.50 秒 |
第 99 百分位 | 1.59 秒 | 1.86 秒 | 2.56 秒 | 4.32 秒 |
至於 API 響應時間,以下圖表顯示了按操作型別和資源型別分組的 API 呼叫延遲的第 50、90 和 99 百分位。但是請注意,這還包括內部系統 API 呼叫,而不僅僅是使用者發出的呼叫(在本例中由測試本身發出)。
某些資源僅出現在特定圖表上,具體取決於該操作期間正在執行的內容(例如,當時沒有放置名稱空間)。
從結果中可以看出,我們 100 節點叢集的目標提前完成了,即使在滿載叢集中,Pod 啟動時間在第 99 百分位也比 5 秒快 14%。值得指出的是,LIST Pod 比任何其他操作都要慢得多。這是有道理的:在一個滿載的叢集中,有 3000 個 Pod,每個 Pod 大約幾千位元組資料,這意味著每次 LIST 操作都需要處理數兆位元組的資料。
#####已完成的工作和未來計劃
使 100 節點叢集足夠穩定以執行任何測試的初始效能工作涉及許多小的修復和調整,包括增加 apiserver 中的檔案描述符限制以及在不同請求之間重用與 etcd 的 tcp 連線。
然而,構建一個穩定的效能測試只是將叢集支援的節點數量增加十倍的第一步。作為這項工作的結果,我們已經付出了巨大的努力來消除未來的瓶頸,包括:
將控制器重寫為基於 watch 的:以前它們每隔幾秒重新列出給定型別的物件,這給 apiserver 帶來了巨大的負載。
使用程式碼生成器生成轉換和深複製函式:雖然使用 Go 反射的預設實現非常方便,但事實證明它們非常慢,與生成的程式碼相比慢了 10 倍。
在 apiserver 中新增快取,以避免多次讀取 etcd 中的相同資料時進行反序列化
降低更新狀態的頻率:考慮到狀態變化緩慢的性質,僅在狀態發生變化時更新 Pod 狀態,並且僅每 10 秒更新一次節點狀態才有意義。
在 apiserver 中實現 watch 而不是將請求重定向到 etcd:我們希望避免多次從 etcd watch 相同的資料,因為在許多情況下,無論如何它都會在 apiserver 中被過濾掉。
展望我們 1000 節點叢集的目標,建議的改進包括:
將事件從 etcd 移出:它們更像是系統日誌,既不是系統狀態的一部分,也不是 Kubernetes 正常工作的關鍵。
使用更好的 JSON 解析器:Go 中實現的預設解析器非常慢,因為它基於反射。
重寫排程器以使其更高效和併發
提高 apiserver 和 Kubelet 之間通訊的效率:特別是,我們計劃減少每次更新節點狀態時傳送的資料大小。
這絕不是一個詳盡的列表。我們將根據執行現有可擴充套件性測試和新建立的測試時觀察到的瓶頸新增新元素(或刪除現有元素)。如果您有特定的用例或場景希望我們解決,請加入我們!
- 我們每週四太平洋時間上午 11 點舉行 Kubernetes 規模特別興趣小組會議,討論正在進行的問題以及效能跟蹤和改進計劃。
- 如果您在此之前有特定的效能或可擴充套件性問題,請加入我們在 Slack 上的可擴充套件性特別興趣小組:https://kubernetes.slack.com/messages/sig-scale
- 一般問題?隨時加入我們在 Slack 上的 Kubernetes 社群:https://kubernetes.slack.com/messages/kubernetes-users/
- 提交拉取請求或提出問題!您可以在我們的 GitHub 倉庫中完成此操作。我們也熱情鼓勵大家透過自己的實驗(及其結果)或拉取請求貢獻來改進 Kubernetes。