Kubernetes v1.33:流式 List 響應
隨著基礎設施的增長,管理 Kubernetes 叢集的穩定性變得越來越重要。在大規模叢集運營中最具挑戰性的方面之一,是處理獲取大量資料集的列表(List)請求——這是一個常見的操作,可能會意外地影響叢集的穩定性。
今天,Kubernetes 社群激動地宣佈一項重大的架構改進:列表響應的流式編碼。
問題:大型資源導致不必要的記憶體消耗
當前的 API 響應編碼器只是將整個響應序列化到一個連續的記憶體中,並執行一次 ResponseWriter.Write 呼叫將資料傳輸給客戶端。儘管 HTTP/2 能夠將響應分割成更小的幀進行傳輸,但底層的 HTTP 伺服器仍然將完整的響應資料作為一個單一的緩衝區持有。即使單個幀被傳輸給客戶端,與這些幀相關的記憶體也無法增量釋放。
當叢集規模擴大時,單個響應體可能會非常大——比如達到數百兆位元組。在大規模場景下,當前的方法變得尤其低效,因為它在傳輸過程中阻止了記憶體的增量釋放。想象一下,當發生網路擁塞時,那個大型響應體的記憶體塊會持續活躍數十秒甚至數分鐘。這個限制導致 kube-apiserver 程序中不必要地高且持久的記憶體消耗。如果同時發生多個大型列表請求,累積的記憶體消耗會迅速升級,可能導致記憶體不足(OOM)的情況,從而危及叢集的穩定性。
encoding/json 包使用 sync.Pool 來複用序列化過程中的記憶體緩衝區。雖然這對於一致的工作負載是高效的,但這種機制在處理偶發的大型列表響應時會產生挑戰。當處理這些大型響應時,記憶體池會顯著擴張。但由於 sync.Pool 的設計,這些過大的緩衝區在使用後仍然被保留。隨後的較小列表請求會繼續利用這些大型記憶體分配,從而阻止了垃圾回收,並導致即使在初始的大型響應完成後,kube-apiserver 的記憶體消耗仍然居高不下。
此外,Protocol Buffers 並非為處理大型資料集而設計。但它非常適合處理大型資料集中的**單個**訊息。這凸顯了需要基於流的方法,這種方法可以增量地處理和傳輸大型集合,而不是作為單一的整體塊。
作為一般經驗法則,如果您處理的訊息每個都超過一兆位元組,那麼可能是時候考慮一種替代策略了。
列表響應的流式編碼器
流式編碼機制專為列表響應設計,利用了它們常見的、定義良好的集合結構。其核心思想只專注於集合結構中的 **Items** 欄位,該欄位在大型響應中佔用了大部分記憶體。新的流式編碼器不是將整個 **Items** 陣列編碼為一個連續的記憶體塊,而是單獨處理和傳輸每一項,從而允許在幀或塊傳輸時逐步釋放記憶體。因此,逐個編碼項可以顯著減少 API 伺服器所需的記憶體佔用。
由於 Kubernetes 物件通常被限制在 1.5 MiB(來自 ETCD),流式編碼使得記憶體消耗保持可預測和可管理,無論列表響應中有多少物件。其結果是 API 伺服器的穩定性顯著提高,記憶體峰值減少,以及整體叢集效能得到改善——尤其是在可能同時發生多個大型列表操作的環境中。
為了確保完美的向後相容性,流式編碼器在啟用前會嚴格驗證 Go 結構體標籤,保證與原始編碼器實現位元組對位元組的一致性。標準編碼機制處理除 **Items** 之外的所有欄位,從而在整個過程中保持相同的輸出格式。這種方法無縫支援所有 Kubernetes 列表型別——從內建的 **\*List** 物件到自定義資源的 **UnstructuredList** 物件——無需任何客戶端修改,也無需客戶端知曉底層的編碼方法已經改變。
您會注意到的效能提升
- 減少記憶體消耗: 在處理大型**列表**請求時,顯著降低了 API 伺服器的記憶體佔用,尤其是在處理**大型資源**時。
- 提高可擴充套件性: 使 API 伺服器能夠處理更多的併發請求和更大的資料集,而不會耗盡記憶體。
- 增強穩定性: 降低了因記憶體不足(OOM)而被終止以及服務中斷的風險。
- 高效的資源利用: 優化了記憶體使用,提高了整體資源效率。
基準測試結果
為了驗證結果,Kubernetes 引入了一個新的**列表**基準測試,該測試併發執行 10 個**列表**請求,每個請求返回 1GB 的資料。
基準測試顯示效能提高了 20 倍,記憶體使用量從 70-80GB 減少到 3GB。

列表基準測試記憶體使用情況