本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Kubernetes 資源限制的案例:可預測性與效率
已經有很多文章建議不使用 Kubernetes 資源限制可能相當有用(例如,《看在上帝的份上,別再在 Kubernetes 上使用 CPU 限制了》或 《Kubernetes:透過移除 CPU 限制讓你的服務更快》)。這些文章中的觀點完全有效——為那些因限制而無法使用的計算能力付費,或者人為增加延遲,確實沒有多大意義。本文旨在論證限制也有其合法用途。
作為 Grafana Labs 平臺團隊的站點可靠性工程師(SRE),我所在的團隊負責維護和改進產品團隊使用的內部基礎設施和工具,我的主要工作是盡力讓 Kubernetes 升級儘可能平滑。但我也花了很多時間深入研究各種有趣的 Kubernetes 問題。本文反映了我的個人觀點,社群中的其他人可能持有不同意見。
讓我們把問題反過來看。Kubernetes 叢集中的每個 Pod 都有其固有的資源限制——即它所執行的機器的實際 CPU、記憶體和其他資源。如果 Pod 達到了這些物理限制,它將經歷類似於達到 Kubernetes 限制所引起的節流。
問題所在
沒有限制(或限制寬鬆)的 Pod 可以輕易消耗節點上的額外資源。然而,這有一個隱藏的成本——可用額外資源的數量通常嚴重依賴於排程到特定節點上的 Pod 及其當時的實際負載。這些額外資源使得每個 Pod 在實際資源分配方面都成了一個特殊的“雪花”。更糟糕的是,要弄清楚 Pod 在任何給定時刻可用的資源相當困難——如果不透過對特定節點上執行的 Pod、它們的資源消耗等進行繁瑣的資料探勘,幾乎不可能做到。最後,即使我們克服了這個障礙,我們也只能以一定的頻率取樣資料,並且只能為我們的一部分呼叫獲取效能剖析。雖然這可以擴充套件,但生成的可觀測性資料量很容易達到收益遞減的程度。因此,沒有簡單的方法可以判斷一個 Pod 是否經歷了快速的峰值,並在短時間內使用了兩倍於平時的記憶體來處理請求突發。
現在,隨著“黑色星期五”和“網路星期一”的臨近,企業預計流量會激增。良好的歷史效能資料/基準測試使企業能夠為一些額外的容量進行規劃。但是,關於沒有限制的 Pod 的資料可靠嗎?由於記憶體或 CPU 的瞬時峰值由額外資源處理,根據過去的資料,一切可能看起來都很好。但一旦 Pod 的裝箱(bin-packing)方式改變,額外資源變得更加稀缺,一切可能就會變得不同——從請求延遲的微不足道的上升,到請求緩慢地滾雪球並導致 Pod OOM(記憶體不足)被殺死。雖然幾乎沒人關心前者,但後者是一個需要立即增加容量的嚴重問題。
配置限制
不使用限制是一種權衡——它在有額外可用資源時機會性地提高了效能,但降低了效能的可預測性,這可能會在未來帶來反噬。有幾種方法可以用來再次提高可預測性。讓我們選擇其中兩種進行分析:
- 將工作負載的 limits 配置為比 requests 高一個固定的(且較小的)百分比——我稱之為固定比例的餘量。這允許使用一些額外的共享資源,但保持了每個節點的超售(overcommit)是有界的,並且可以用來指導工作負載的最壞情況估算。請注意,limits 的百分比越大,工作負載之間可能出現的效能差異就越大。
- 將工作負載配置為
requests
=limits
。從某種角度來看,這相當於為每個 Pod 提供一個資源受限的微型虛擬機器;效能相當可預測。這也將 Pod 置於 Guaranteed QoS 類別中,這使得它只有在 BestEffort 和 Burstable 類的 Pod 被資源壓力下的節點驅逐後才會被驅逐(參見 Pod 的服務質量)。
可能還會考慮其他一些情況,但這兩種可能是最簡單的討論案例。
叢集資源經濟學
請注意,在上述兩種情況中,我們實際上是阻止工作負載使用其擁有的一些叢集資源,以換取更高的可預測性——這聽起來為了獲得更穩定的效能而付出的代價可能很高。讓我們試著量化其影響。
裝箱與叢集資源分配
首先,讓我們討論一下裝箱(bin-packing)和叢集資源分配。這裡存在一些固有的叢集低效率——在 Kubernetes 叢集中很難實現 100% 的資源分配。因此,總會有一部分資源未被分配。
當配置固定比例餘量的 limits 時,相應比例的未分配資源將可供 Pod 使用。如果叢集中未分配資源的百分比低於我們用於設定固定比例餘量限制的常數(見圖,第 2 行),所有 Pod 理論上能夠用完節點的所有資源;否則,就會有一些資源不可避免地被浪費(見圖,第 1 行)。為了消除不可避免的資源浪費,固定比例餘量限制的百分比應該配置為至少等於預期的未分配資源百分比。
對於 requests = limits(見圖,第 3 行),情況並非如此:除非我們能夠分配節點的所有資源,否則總會有一些不可避免的資源被浪費。在 requests/limits 方面沒有任何可調引數的情況下,唯一合適的方法是透過配置正確的機器規格來確保節點上的高效裝箱。這可以手動完成,也可以使用各種雲服務提供商的工具——例如 EKS 的 Karpenter 或 GKE 節點自動擴縮。
最佳化實際資源利用率
空閒資源也以其他 Pod 未使用的資源(預留的 CPU 利用率與實際的 CPU 利用率等)的形式存在,並且它們的可用性無法以任何合理的方式預測。配置 limits 使得利用這些資源幾乎不可能。從另一個角度看,如果一個工作負載浪費了它所請求的大量資源,那麼重新審視其自身的資源請求可能是一件合理的事情。檢視歷史資料並選擇更合適的資源請求可能有助於使裝箱更緊湊(儘管代價是效能變差——例如增加長尾延遲)。
結論
最佳化資源請求和限制是很困難的。雖然在設定 limits 時更容易出問題,但這些問題可能透過提供更多關於工作負載在邊界條件下如何行為的洞察,幫助防止以後發生災難。在某些情況下,設定 limits 的意義不大:批處理工作負載(對延遲不敏感——例如非即時影片編碼)、盡力而為(best-effort)的服務(不需要那麼高的可用性,可以被搶佔)、設計上就有大量備用資源的叢集(各種特殊工作負載的情況——例如設計用於處理峰值的服務)。
另一方面,不應不惜一切代價避免設定 limits——儘管找出 limits 的“正確”值更難,並且配置錯誤的值會導致更不寬容的情況。配置 limits 有助於你瞭解工作負載在極端情況下的行為,並且在推斷正確值時有一些簡單的策略可以幫助。這是在高效資源使用和效能可預測性之間的一種權衡,應該這樣來看待。
具有資源使用峰值的工作負載還有一個經濟方面的問題。手頭總是有“免費”資源並不能激勵產品團隊去提高效能。足夠大的峰值很容易引發效率問題,甚至在試圖捍衛產品的服務等級協議(SLA)時出現問題——因此,在評估任何風險時,這可能是一個值得提及的候選因素。