本文將介紹 Kubernetes 的 resource limits 是如何工作的,、使用哪些 metrics 來設置正確的 limits 值,、以及使用哪些指標來定位 CPU 抑制的問題。 理解 Limits在配置 limits 時,,我們會告訴 Linux 節(jié)點在一個特定的周期內(nèi)一個容器應用的運行時長。這樣做是為了保護節(jié)點上的其余負載不受任意一組進程占用過多 CPU 周期的影響,。 limits 的核并不是主板上的物理核,,而是配置了單個容器內(nèi)的一組進程或線程在容器短暫暫停 (避免影響到其他應用) 前的運行時長。這句話有點違反直覺,,特別是在 Kubernetes 調(diào)度器級別上很容易出錯,,Kubernetes 調(diào)度器使用了物理核的概念。
Limits 其實是時間下面使用一個虛構的例子來解釋這個概念,。假設有一個單線程應用,,該應用需要 1 秒 CPU 運行時間來完成一個事務,此時將 limits 配置為 1 core 或 1000 millicores: Resources: 如果該應用需要完整的 1 秒 CPU 運行時間來服務一個 API 調(diào)用,,中間不能被停止或抑制,,即在容器被抑制前需要允許該應用運行 1000 毫秒 (ms) 或 1 CPU 秒。 由于 1000 毫秒等同于 1 秒 CPU 運行時間,,這就可以讓該應用每秒不受限地運行一個完整的 CPU 秒,,實際的工作方式更加微妙。我們將一個 CPU 秒稱為一個周期 (period),,用來衡量時間塊,。 Linux Accounting systemLimits 是一個記賬系統(tǒng) (Accounting system),用于跟蹤和限制一個容器在固定時間周期內(nèi)使用的總 vCPU 數(shù),,該值作為可用運行時的全局池進行跟蹤,,一個容器可以在該周期內(nèi)使用該池。上面陳述中有很多內(nèi)容,,下面對此進行分析,。 回到周期或記賬系統(tǒng)翻頁頻率的概念。我們需要跨多個 vCPU 申請運行時間,,這意味著需要將賬簿的每頁分為多個段,,稱為切片。Linux 內(nèi)核默認會將一個周期分為 20 個切片,。 假設我們需要運行半個周期,,此時只需要將配額配置為一半數(shù)目的切片即可,在一個周期之后,,記賬系統(tǒng)會重置切片,,并重啟進程。 類似于 requests 或 shares 可以轉換為表示 CPU 分配百分比的比率,也可以將 limits 轉換為一個百分比,。例如,,容器的配額設置為半個周期,則配置為:
開始時,,使用 1000 milliCPU 作為一個完整的 share,。當配置 500 milliCPU 時,使用了半個周期,,或 500m/1000m = 50%,。如果設置了 200m/1000m,則表示使用的 CPU 比率為 20%,,以此類推,。我們需要這些轉換數(shù)字來理解一些 prometheus 的指標輸出。 上面提到的記賬系統(tǒng)是按容器計算的,,下面看下指標 Linux 有一個配置,,稱為 撇開一些特殊場景不談,,在賬簿翻頁之前經(jīng)過的時間并不像被限制的 CPU 時間切片那樣重要,。 下面看下使用 多線程容器容器通常具有多個處理線程,,根據(jù)語言的不同,可能有數(shù)百個線程,。 當這些線程 / 進程運行時,它們會調(diào)度不同的 (可用)vCPU,,Linux 的記賬系統(tǒng)需要全局跟蹤誰在使用這些 vCPU,,以及需要將哪些內(nèi)容添加到賬簿中。 先不談周期的概念,,下面我們使用 如果總的 vCPU 時間小于 1 個 vCPU 秒會發(fā)生什么呢,?此時會在該時間幀內(nèi)抑制節(jié)點上該應用的其他線程的運行。 Global accounting上面討論了如何將一個 vCPU 秒切分為多個片,,然后就可以全局地在多個 vCPU 上申請時間片,。讓我們回到上述例子 (4 個線程運行在 4 個 vCPU 上),,進一步理解它們?nèi)绾芜\行的。 當一個 CPU 需要運行其隊列中的一個線程或進程時,,它首先會確認容器的全局配額中是否有 5ms 的時間片,,如果全局配額中有足夠的時間片,則會啟動線程,,否則,,該線程會被抑制并等待下一個周期。 真實場景下面假設一個實驗,,假如有 4 個線程,,每個線程需要 100ms 的 CPU 時間來完成一個任務,將所有所需的 vCPU 時間加起來,,總計需要 400ms 或 4000m,,因此可以以此為進程配置 limit 來避免被抑制。 不幸的是,,實際的負載并不是這樣的,。這些函數(shù)的線程可能運行重的或輕的 API 調(diào)用。應用所需的 CPU 時間是變化的,,因此不能將其認為是一個固定的值,。再進一步,4 個線程可能并不會同時各需要一個 vCPU,,有可能某些線程需要等待數(shù)據(jù)庫鎖或其他條件就緒,。 正因為如此,負載往往會突然爆發(fā),,因此延遲并不總是能夠成為設置 limits 的候選因素,。最新的一個特性--cpu.cfs_burst_us[1]允許將部分未使用的配額由一個周期轉至下一個周期。 有趣的是,,這并不是讓大多數(shù)客戶陷入麻煩的地方,。假設我們只是猜測了應用程序和測試需求,并且 1 個 CPU 秒聽起來差不多是正確的,。該容器的應用程序線程將分布到 4 個 vCPU 上,。這樣做的結果是將每個線程的全局配額分為 100ms/4 或 25ms 的運行時。 而實際的總配額為 (100ms 的配額) * (4 個線程) 或 400ms 的配額,。在 100 毫秒的現(xiàn)實時間里,,所有線程有 300 毫秒處于空閑狀態(tài)。因此,,這些線程總共被抑制了 300 毫秒,。 Latency 下面從應用的角度看下這些影響。單線程應用需要 100ms 來完成一個任務,當設置的配額為 100ms 或 1000 m/1000 m = 100%,,此時設置了一個合理的 limits,,且沒有抑制。 在第二個例子中,,我們猜測錯誤,,并將 limits 設置為 400m 或 400 m/1000 m = 40%,此時的配額為 100ms 周期中的 40ms,。下圖展示該配置了對該應用的延遲: 此時處理相同請求的時間翻倍 (220ms),。該應用在三個統(tǒng)計周期中的兩個周期內(nèi)受到了抑制。在這兩個周期中,,應用被抑制了 60ms,。更重要的是,如果沒有其他需要處理的線程,,vCPU 將會被浪費,,這不僅僅會降低應用的處理速度,也會降低 CPU 的利用率,。 與 limits 相關的最常見的指標 那么,如何知道 limits 應該增加多少呢,? Throttled seconds幸運的是,,cAdvisor 提供了一個指標 通過如下表達式可以找出 CPU 使用超過 100ms 的前三個 pods,。 topk(3, max by (pod, container)(rate(container_cpu_usage_seconds_total{image!='', instance='$instance'}[$__rate_interval]))) / 10 下面做一個實驗:使用
可以觀測到使用了 400ms 的 vCPU: 下面對該容器添加 limits 限制: resources: 可以看到總的 CPU 使用在 100ms 的現(xiàn)實時間中減少了一半,,這正是我們所期望的,。 PromQL 給出了每秒的抑制情況,每秒有 10 個周期 (每個周期默認 100ms),。為了得到每個周期的抑制情況,需要除以 10,。如果需要知道應該增加多少 limits,,則可以乘以 10(如 200ms * 10 = 2000m)。
告警設置可以基于 CPU 抑制時間或抑制比率來編寫告警表達式,其表達的也是 CPU 的飽和度信息: # CPU抑制時間超過1s時產(chǎn)生告警 總結本文介紹了 limits 是如何工作的,,以及可以使用哪些指標來設置正確的值,,使用哪些指標來進行抑制類型的問題定位。本文的實驗提出了一個觀點,,即過多地配置 limits 的 vCPU 數(shù)也可能會導致 vCPU 處于 idle 狀態(tài)而造成應用響應延遲,,但在現(xiàn)實的服務中,一般會包含語言自身 runtime 的線程 (如 go 和 java) 以及開發(fā)者自己啟動的線程,,一般設置較多的 vCPU 不會對應用的響應造成影響,,但會造成資源浪費。 參考
引用鏈接cpu.cfs_burst_us: https:///Articles/844976/ [2]Demystifying Kubernetes CPU Limits (and Throttling): https:///understanding-kubernetes-cpu-limits/ 來源: charlieroro 的博客,,原文:https://www.cnblogs.com/charlieroro/p/17074808.html |
|