(譯文)Why you should keep using CPU limits on Kubernetes

筆記一下K8S的CPU限制管理相關的議題,這個就是接續前一篇譯文反對設置CPU limits,這邊就是持相反意見的文章翻譯

ZONGRU Li
12 min readOct 3, 2022

原文出處:Why you should keep using CPU limits on Kubernetes Or why staying away from unused CPU may be good for your containers

這篇文章是透過smalltown的FB社群上看到的,也是原本想翻譯的,但是關連到前一篇翻譯的文章,剛好就兩篇連續一併翻譯

以下直接翻譯原文內容

作者寫下這篇文章就是要寫一些對立於我翻譯的前一篇"For the love of god, stop using CPU limits on Kubernetes"文章內容

作者認為上述連結文章閱讀得到的內容是極好的.作者相當同意其建議的設定Memory的要求(requests)與限制(limits),也同意其建議的一定要設置CPU的要求(requests)

唯一不認同點在於,明顯完全對立於其標題,關於不設置CPU限制的建議部分.

光速回顧Kubernetes的容器資源管理:一個CPU的"要求"是定義一個容器在運行時最小可使用的CPU量,並且CPU的限制是容器可使用的最大CPU量.

(有張圖略過,可以點原文去看看)

關於移除CPU限制的想法根本概念是設置CPU限制,可以解決了避免容器去調用node上那些不可以使用(或是不給預定)的CPU資源的負面影響,使得容器不可避免地受到不必要的CPU節約的損害.

然而,在更廣泛的研究中寫到"容器與Kubernetes的無限擴展",作者認為大多時候仍有一些好理由應該去設置CPU限制,一些反向的意見如下介紹.

9/30原作者更新:作者說他撤回了"理由#6:考慮namespace資源配給"這整個部分.他遺漏了添加到namespace的所有pod都首先具有CPU限制,因此對於使用與不使用CPU限制的辯論就沒有意義.

*對於那些想深入了解Kubernetes資源如何工作的人,作者推薦 Stefanie Lai 的Layer-by-Layer Cgroup in Kubernetes以及Shon Lev-Ran and Shir Monether的3部分系列的數據補充.

理由#1:節留下的彈性

9/30原作者更新:原始標題為"容器探針不是可壓縮"這掩蓋了本節的要點:開發帶有CPU限制的容器,使它們在節流事件下更具彈性和更好的表現.建立存活容器探針(liveness container probes)使得在節流的情況下更有彈性,同樣節流下就緒探針(readiness probes)也有更好準確性,這是唯二兩個設置CPU限制下較正面的好處.

當容器嘗試使用比可用的量更多的CPU時候 — 並且忽略初步調度等級的相關措施像是重整所有nodes上的pods — node會節約容器的CPU使用率,造成它運作更緩慢,這也是為什麼Kubernetes文件指出CPUs是"可壓縮(compressible)"資源.

(我自己個人理解上面的壓縮指的是node上假設有4 core,當給出限制只能調度其中2 core,這時候應用程式在此就是容器能用的就是剩餘的另外2 core,這就是CPU被壓縮(能用的量減少))

因此可以大膽假設容器可以在節約的情況下運行.然而這個假設面對Kubernetes探針(probes)則有意外的結果.

當kubelet節約容器時,節約的狀況會影響到容器內的所有執行緒,這當中包含了process的liveness(存活)與readiness(就緒)探針.

(K8S Probe這方面對應到我自己的CKA筆記的第63篇內容,不過這邊說的是kubelet本身自己確認容器存活的部分)

即便容器設置了CPU要求,容器中突發增長的工作負載也可能消耗到不成比例的份額,從而造成減緩探針的回應速度.

readiness(就緒)探針的連續失敗或超時會導致kubelet將流量從容器中轉移,而liveness(存活)的連續失敗或超時導致kubelet將終止該容器

(因為kubelet會以為這容器的主要啟動process已經GG了)

(這邊作者有做一張圖解,我也自己畫一張:)

控制CPU使用率對容器也有好處.當容器受到限制時,該容器失控的執行緒會干擾到其他執行緒的CPU分配,這同時使得容器存活(這邊應該是說liveness探針的結果)受到風險

作者寫了很多關於設計容器探針相關的不錯的實踐文章,在壓力下時很大程度限制了容器回應kubelet的探針的機率,不過這些機率降低與容器超過原有的CPU要求本身的CPU使用率有關.

假若節流對於探針是不好的,則設置CPU限制也就會更慘,因為它還會增加節流事件的數量.

這個想法並不是在說於生產環境中運行有CPU限制的容器會讓它們對於節流的情況更具有彈性.理所當然地並非如此.這裡關鍵的地方是在整個開發階段的一開始就有開發具有CPU限制的容器.

持續觀察節留下的不良行為(假設是透過系統方法在資源受限的節點進行高負載下運行容器) — 為重要的設計與實作提供資訊.

例如,這些觀察可以進而改進以下:

  • 從容器的就緒探針中取得更多可利用的容器狀態資訊,來幫助kubelet將流量轉移到其他工作負載較小的分裂實體上.
  • 重新審視內部執行緒池的設計與微調.
  • 向系統管理員展現更多微調用的參數

另一方面,如果你開發容器但沒有設置CPU限制,它們的行為將會受制於其運行的節點上可調用的CPU週期.在使用率低的開發叢集中運行一個容器即便運作良好,在當它運行在相同規格但是沒有預留的CPU資源時,其行為表現會差距極大.

註:當設置了CPU限制並進行開發與基準測試容器時,當kubelet把CPU拿走,它保護的是整個節點(node),而不是你的工作負載(workload).

理由#2:失去"保證(Guaranteed)"狀態

更新於9/28:這部分標題是"高爆發對應高懲罰"並錯誤地指出超過CPU要求僅僅只是節點壓力驅逐演算法中的一個因素.這僅適用於Memory與disk資源.

這只是一個膚淺的理由,沒有任何意義.

在pod中容器的資源要求(request)與限制(limit)的設置,決定了pod的服務質量(Quality of Service).

  • 不設置任何要求(request)與限制(limit)時,pod將被歸類為"最大努力BestEffort",這是由於最初假設所有容器都有Memory與CPU的要求(request),但是這故事中排除了服務品質(QoS:Quality of Service)的考慮.
  • 容器設置CPU要求(request),但不設置CPU限制(limit)時,pod被歸類在"可爆發(Burstable)".
  • 唯一可以讓pod達到"有保證(Guaranteed)"的QoS,就是在pod中的每一個容器都設置完全相同的CPU與Memory的要求與限制.

QoS最值得注意的情境是"節點壓力驅逐(node-pressure eviction)",這是當kubelet在評估哪些pod需要移出該節點,以舒緩資源的使用率.

有保證(Guaranteed)的pods將會是最後被考慮驅逐的 — 這是基於相同的pod的優先等級 — 而可爆發的pod往往會先(這邊應該是說優先被驅逐).

註記,驅逐評估只有考慮Memory,disk以及filesystem在node中的使用率.在這基礎下,可以技術上的認為,當驅逐的狀況來臨時,且可爆發的pod超過原有的CPU要求時,這些pod仍會被歸類在有保證(Guaranteed)的pods.

一個不常見(或是很常見)的例子是CPU管理策略,其中"靜態策略(Static policy)"的好處僅適用於有保證(Guaranteed)的pod(並且是使用整數的CPU要求)你可以閱讀更多關於使用CPU管理器來了解這其中針對工作負載帶來的好處.

註記:再次強調,努力管理容器CPU的使用率.若不設置CPU限制將導致降低可爆發的pod的QoS,並且造成某些調度與節約的場景中受到不合理的優待狀況.

理由#3:CPU與Memory的使用率是有相關的時候

無論好壞,在計算機中CPU與Memory的密不可分的關聯著.這也是為何沒有雲端供應商在提供VM時候會提供不對襯的16x2節點(16 CPUs與2 Gigabytes的RAM).最普遍的CPU的比例與前者反過來,通常是CPU數量是Memory的1/4或一半,例如8x16,16x64還有32x64.

這類相關性有許多實際成因,但總得來說:

  • 讀寫越多Memory會占用大量CPU
  • 使用大量CPU也代表使用大量執行緒(同時伴隨著執行緒stacks堆疊在Memory中)並且過程產生大量資料,而這些資料又要儲存在Memory之中(即便快取存在穩定的storage中)

可以推測許多CPUs將其主線輸出到disk中的場景(不考慮在Memory中的快取資料?).當你完成開發的作業,你很難想像如何有效地從2個CPU之中卻只要求200ms的CPU以及256Mb的RAM的容器(也就是CPU實際數量是要求的十倍)中受益,而無需使用更多的Memory(即便不是十倍)

註記,讓CPU使用率敞開大門地運行未經檢查(i.e.迫近整個節點的CPU限制)而不允許相對應地增加Memory的使用量,這不符合多數應用程式的使用模式.從系統角度來看,你應該要遵循下一節中的建議.

理由#4:Pod的自動擴展可以做得更好

讓容器超出其CPU限制的理由之一,可以是在節點中的CPU仍有許多容量未被使用情況下,不妨就允許pod調度這些額外的容量.

作者同意這個原則,但是同上一個理由所說的,額外的CPU調度往往需要更多Memory使用量來支持.

對於可預測且平衡的方式進行管理Job對應的Pod的可擴展容量,是Pod的自動擴展所擅長的.

  • HPAKPA可以增加pod的複製數量,從而為整個工作負載增加更多的聚合CPU與Memory的容量,而不會產生執行緒失控導致容器的探針被擠出去的副作用.
  • VPA可以提高CPU和Memory的總體限制,確保pod對其資源的要求合限制進行平衡調整

但是pod的自動擴展是透過所設置的要求值才能在資源使用率上取得優勢,而不是依據限制的值

這是正確的.此外,除此之外.

並非鼓勵仰賴CPU使用率來作為可以完成更多工作的機制並以此來完成為此目的所設計的元件

註記:Pod的自動擴展是設計來最大化平衡資源使用率.雖說讓容器超出其CPU預定值有時有助於一些情況,但是pod自動擴展在有目的性的方式增加資源限制上做得更好

(這邊我也補充我的一些看法,假設機台CPU資源總共有6 core可以供給給pod的情況,現在部署的應用程式pod平時運行各會佔據1 core(也就是request=1 core)並有四個這樣的pod運行,假設叫A,B,C,D四個pods,而A,B,C,D上的容器內提供的服務應用程式很恰好的尖峰時刻都是分開的,並且都會導致在各自的尖峰時刻都會額外需要佔據1~2 core情況,這時候機台原本的6 core中,其中4 core已經提供ABCD等基本運作外,剩餘2 core可以讓ABCD分別額外調度使用,這樣CPU資源的方式極具彈性)

理由#5:高度擴展需要容器限制

9/30原作者更新這邊 — 原始該部分的標題為"叢集自動擴展並不喜歡突然地作動"並且(錯誤地)指出CPU使用率(上面所述的CPU請求)會驅動自動擴展的發生.閱讀 Shir Monether,在註釋的部分正確地指出事實並非如此,所以原作者重寫了這部分僅保留了管理容器的服務部分.

這是個很膚淺的因素,但是我想我仍應該要列出來.

假若你的工作負載曾經超過單一叢集的容量並且你決定使用管理容器的服務來運作你的容器應用,例如Code Engine or ECS,這類服務都要求擬設置CPU(還有Memory)的限制.

該要求有助於供應商分配提供資源,亦對您有利,因為你按資源分配付費,無限使用意味著無限的成本.

結論

調用叢集中那些未被使用的CPU容量的優勢與我們對於這些備用容量的依賴程度成正比(可以想想我上面理由四自己補充的想法)

在沒有CPU限制情況下開發容器應用往往導致對於這些備用CPU週期的無形依賴,並且這種依賴關係可能在不同環境中無法使用(也就是無法重現),進而導致如容器探針等被排擠出CPU使用週期之外的狀況,(略微)增加了在資源短缺下pod被排擠的機會,以及(無限)CPU與(有限)Memory的不平衡的分配.

假若移除CPU限制的目標是為了最大化容量分配與最大化節點及叢集資源的使用率,那麼在投入各種元件也就是在設計的時候就應該把這些目標放在心上,例如pod甚至叢集的自動擴展經常會比增加Memory的分配來匹配CPU的增加做得更好.

設計容器來平衡資源的使用率,在開發過程中所有的容器資源限制設置的相近(或是相同),是一種合理的做法,尤其是配合有效的探針設計的使用的時候.

良好的容器限制與探針讓kubelet知道何時重新導流量到其他叢集中的pod,同時延遲pod的自動擴展來確定何時以及如何調整pod的工作負載的聚合容量.

作者放置的額外參考連結:

以上就是該文章的粗略翻譯,內容也穿插我自己的想法

--

--

ZONGRU Li
ZONGRU Li

Written by ZONGRU Li

2022/11/17 開源部分個人筆記給LINE "Java程式語言討論區"社群,希望能對社群的技術學習做一點點貢獻.(掩面....記得退訂閱!

No responses yet