溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么深入解析kubernetes資源管理

發(fā)布時(shí)間:2021-12-28 16:05:15 來(lái)源:億速云 閱讀:140 作者:柒染 欄目:云計(jì)算

今天就跟大家聊聊有關(guān)怎么深入解析kubernetes資源管理,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

資源,無(wú)論是計(jì)算資源、存儲(chǔ)資源、網(wǎng)絡(luò)資源,對(duì)于容器管理調(diào)度平臺(tái)來(lái)說(shuō)都是需要關(guān)注的一個(gè)核心問(wèn)題。

Kubernetes 資源模型

01 Kubernetes 是怎么定義資源的呢?

在 kubernetes 中,任何可以被申請(qǐng)、分配,最終被使用的對(duì)象,都是 kubernetes 中的資源,比如 CPU、內(nèi)存。

而且針對(duì)每一種被 kubernetes 所管理的資源,都會(huì)被賦予一個(gè)【資源類型名】,這些名字都是符合 RFC 1123 規(guī)則的,比如 CPU,它對(duì)應(yīng)的資源名全稱為 kubernetes.io/cpu(在展示時(shí)一般直接簡(jiǎn)寫為 cpu);GPU 資源對(duì)應(yīng)的資源名為 alpha.kubernetes.io/nvidia-gpu。

除了名字以外,針對(duì)每一種資源還會(huì)對(duì)應(yīng)有且僅有一種【基本單位】。這個(gè)基本單位一般是在 kubernetes 組件內(nèi)部統(tǒng)一用來(lái)表示這種資源的數(shù)量的,比如 memory 的基本單位就是字節(jié)。

但是為了方便開(kāi)發(fā)者使用,在開(kāi)發(fā)者通過(guò) yaml 文件或者 kubectl 同 kubernetes 進(jìn)行交互時(shí)仍可以使用多種可讀性更好的資源單位,比如內(nèi)存的 Gi。而當(dāng)這些信息進(jìn)入 kubernetes 內(nèi)部后,還是會(huì)被顯式的轉(zhuǎn)換為最基本單位。

所有的資源類型,又可以被劃分為兩大類:可壓縮(compressible)和不可壓縮(incompressible)的。其評(píng)判標(biāo)準(zhǔn)就在于: 如果系統(tǒng)限制或者縮小容器對(duì)可壓縮資源的使用的話,只會(huì)影響服務(wù)對(duì)外的服務(wù)性能,比如 CPU 就是一種非常典型的可壓縮資源。

對(duì)于不可壓縮資源來(lái)說(shuō),資源的緊缺是有可能導(dǎo)致服務(wù)對(duì)外不可用的,比如內(nèi)存就是一種非常典型的不可壓縮資源。

02 Kubernetes 中有哪幾類資源呢?

目前 kubernetes 默認(rèn)帶有兩類基本資源

? CPU

? memory

其中 CPU,不管底層的機(jī)器是通過(guò)何種方式提供的(物理機(jī) or 虛擬機(jī)),一個(gè)單位的 CPU 資源都會(huì)被標(biāo)準(zhǔn)化為一個(gè)標(biāo)準(zhǔn)的 "Kubernetes Compute Unit" ,大致和 x86 處理器的一個(gè)單個(gè)超線程核心是相同的。

CPU 資源的基本單位是 millicores,因?yàn)?CPU 資源其實(shí)準(zhǔn)確來(lái)講,指的是 CPU 時(shí)間。所以它的基本單位為 millicores,1 個(gè)核等于 1000 millicores。也代表了 kubernetes 可以將單位 CPU 時(shí)間細(xì)分為 1000 份,分配給某個(gè)容器。

memory 資源的基本單位比較好理解,就是字節(jié)。

另外,kubernetes 針對(duì)用戶的自定制需求,還為用戶提供了 device plugin 機(jī)制,讓用戶可以將資源類型進(jìn)一步擴(kuò)充,比如現(xiàn)在比較常用的 nvidia gpu 資源。這個(gè)特性可以使用戶能夠在 kubernetes 中管理自己業(yè)務(wù)中特有的資源,并且無(wú)需修改 kubernetes 自身源碼。

而且 kubernetes 自身后續(xù)還會(huì)進(jìn)一步支持更多非常通用的資源類型,比如網(wǎng)絡(luò)帶寬、存儲(chǔ)空間、存儲(chǔ) iops 等等。

所以如上,我們就回答了 kubernetes 是如何定義資源的問(wèn)題。

Kubernetes 計(jì)算節(jié)點(diǎn)資源管理

了解了 kubernetes 是如何對(duì)資源進(jìn)行定義的,我們?cè)賮?lái)看

Kubernetes 如何確定可以使用的資源量呢?

kubernetes 所管理的集群中的每一臺(tái)計(jì)算節(jié)點(diǎn)都包含一定的資源,kubernetes 是如何獲知這臺(tái)計(jì)算節(jié)點(diǎn)有多少資源的呢?這些資源中又有多少可以被用戶容器所使用呢?

我們?cè)谑褂?kubernetes 時(shí)會(huì)發(fā)現(xiàn):每一個(gè) Node 對(duì)象的信息中,有關(guān)資源的描述部分如下述所示 (通過(guò) kubectl get node xxx -o yaml 命令可以得到)

allocatable: 
  cpu: "40" 
  memory: 263927444Ki 
  pods: "110" 
capacity: 
  cpu: "40" 
  memory: 264029844Ki 
  pods: "110"

其中 【capacity】 就是這臺(tái) Node 的【資源真實(shí)量】,比如這臺(tái)機(jī)器是 8核 32G 內(nèi)存,那么在 capacity 這一欄中就會(huì)顯示 CPU 資源有 8 核,內(nèi)存資源有 32G(內(nèi)存可能是通過(guò) Ki 單位展示的)。

而 【allocatable】 指的則是這臺(tái)機(jī)器【可以被容器所使用的資源量】。在任何情況下,allocatable 是一定小于等于 capacity 的。

比如剛剛提到的 8 核機(jī)器的,它的 CPU capacity 是 8 核,但是它的 cpu allocatable 量可以被調(diào)整為 6 核,也就是說(shuō)調(diào)度到這臺(tái)機(jī)器上面的容器一共最多可以使用 6 核 CPU,另外 2 核 CPU 可以被機(jī)器用來(lái)給其他非容器進(jìn)程使用。

Capacity 和 allocatable 信息都是如何確定的呢?

首先看 【capacity】 ,由于 capacity 反映的是這臺(tái)機(jī)器的資源真實(shí)數(shù)量,所以確認(rèn)這個(gè)信息的任務(wù)理所應(yīng)當(dāng)交給運(yùn)行在每臺(tái)機(jī)器上面的 kubelet 上。

kubelet 目前把 cadvisor 的大量代碼直接作為 vendor 引入。其實(shí)就相當(dāng)于在 kubelet 內(nèi)部啟動(dòng)了一個(gè)小的 cadvisor。在 kubelet 啟動(dòng)后,這個(gè)內(nèi)部的 cadvisor 子模塊也會(huì)相應(yīng)啟動(dòng),并且獲取這臺(tái)機(jī)器上面的各種信息。其中就包括了有關(guān)這臺(tái)機(jī)器的資源信息,而這個(gè)信息也自然作為這臺(tái)機(jī)器的真實(shí)資源信息,通過(guò) kubelet 再上報(bào)給 apiserver。

Allocatable 信息又是如何確認(rèn)的呢?

如果要介紹 allocatable 信息如何確認(rèn),首先必須介紹 Node Allocatable Resource 特性 [6]。 Node Allocatable Resource 特性是由 kubernetes 1.6 版本引進(jìn)的一個(gè)特性。主要解決的問(wèn)題就是:為每臺(tái)計(jì)算節(jié)點(diǎn)的【非容器進(jìn)程】預(yù)留資源。

在 kubernetes 集群中的每一個(gè)節(jié)點(diǎn)上,除了用戶容器還會(huì)運(yùn)行很多其他的重要組件,他們并不是以容器的方式來(lái)運(yùn)行的,這些組件的類型主要分為兩個(gè)大類:

kubernetes daemon:kubernetes 相關(guān)的 daemon 程序,比如 kubelet,dockerd 等等。 system daemon:和 kubernetes 不相關(guān)的其他系統(tǒng)級(jí) daemon 程序,比如 sshd 等等。

這兩種進(jìn)程對(duì)于整個(gè)物理機(jī)穩(wěn)定的重要性是毋庸置疑的。所以針對(duì)這兩種進(jìn)程,kubernetes 分別提供了 Kube-Reserved 和 System-Reserved 特性,可以分別為這兩組進(jìn)程設(shè)定一個(gè)預(yù)留的計(jì)算資源量,比如預(yù)留 2 核 4G 的計(jì)算資源給系統(tǒng)進(jìn)程用。

Kubernetes 是如何實(shí)現(xiàn)這一點(diǎn)的呢?

我們應(yīng)該都有了解,kubernetes 底層是通過(guò) cgroup 特性來(lái)實(shí)現(xiàn)資源的隔離與限制的,而這種資源的預(yù)留也是通過(guò) cgroup 技術(shù)來(lái)實(shí)現(xiàn)的。

在默認(rèn)情況下,針對(duì)每一種基本資源(CPU、memory),kubernetes 首先會(huì)創(chuàng)建一個(gè)根 cgroup 組,作為所有容器 cgroup 的根,名字叫做 kubepods。這個(gè) cgroup 就是用來(lái)限制這臺(tái)計(jì)算節(jié)點(diǎn)上所有 pod 所使用的資源的。默認(rèn)情況下這個(gè) kubepods cgroup 組所獲取的資源就等同于該計(jì)算節(jié)點(diǎn)的全部資源。

但是,當(dāng)開(kāi)啟 Kube-Reserved 和 System-Reserved 特性時(shí),kubernetes 則會(huì)為 kubepods cgroup 再創(chuàng)建兩個(gè)同級(jí)的兄弟 cgroup,分別叫做 kube-reserved 和 system-reserved,分別用來(lái)為 kubernetes daemon、system daemon 預(yù)留一定的資源并且與 kubepods cgroup 共同分配這個(gè)機(jī)器的資源。所以 kubepods 能夠被分配到的資源勢(shì)必就會(huì)小于機(jī)器的資源真實(shí)量了,這樣從而就達(dá)到了為 kubernetes daemon,system daemon 預(yù)留資源的效果。

所以如果開(kāi)啟了 Kube-Reserved 和 System-Reserved 的特性的話,在計(jì)算 Node Allocatable 資源數(shù)量時(shí)必須把這兩個(gè) cgroup 占用的資源先減掉。

所以,假設(shè)當(dāng)前機(jī)器 CPU 資源的 Capacity 是 32 核,然后我們分別設(shè)置 Kube-Reserved 為 2 核,System-Reserved 為 1 核,那么這臺(tái)機(jī)器真正能夠被容器所使用的 CPU 資源只有 29 核。

但是在計(jì)算 memory 資源的 allocatable 數(shù)量時(shí),除了 Kube-Reserved,System-Reserved 之外,還要考慮另外一個(gè)因素。那就是 Eviction Threshold。

什么是 Eviction Threshold 呢?

Eviction Threshold 對(duì)應(yīng)的是 kubernetes 的 eviction policy 特性。該特性也是 kubernetes 引入的用于保護(hù)物理節(jié)點(diǎn)穩(wěn)定性的重要特性。當(dāng)機(jī)器上面的【內(nèi)存】以及【磁盤資源】這兩種不可壓縮資源嚴(yán)重不足時(shí),很有可能導(dǎo)致物理機(jī)自身進(jìn)入一種不穩(wěn)定的狀態(tài),這個(gè)很顯然是無(wú)法接受的。

所以 kubernetes 官方引入了 eviction policy 特性,該特性允許用戶為每臺(tái)機(jī)器針對(duì)【內(nèi)存】、【磁盤】這兩種不可壓縮資源分別指定一個(gè) eviction hard threshold, 即資源量的閾值。

比如我們可以設(shè)定內(nèi)存的 eviction hard threshold 為 100M,那么當(dāng)這臺(tái)機(jī)器的內(nèi)存可用資源不足 100M 時(shí),kubelet 就會(huì)根據(jù)這臺(tái)機(jī)器上面所有 pod 的 QoS 級(jí)別(Qos 級(jí)別下面會(huì)介紹),以及他們的內(nèi)存使用情況,進(jìn)行一個(gè)綜合排名,把排名最靠前的 pod 進(jìn)行遷移,從而釋放出足夠的內(nèi)存資源。

所以針對(duì)內(nèi)存資源,它的 allocatable 應(yīng)該是 [capacity] - [kube-reserved] - [system-reserved] - [hard-eviction]

所以,如果仔細(xì)觀察你會(huì)發(fā)現(xiàn):如果你的集群中沒(méi)有開(kāi)啟 kube-reserved,system-reserved 特性的話,通過(guò) kubectl get node -o yaml 會(huì)發(fā)現(xiàn)這臺(tái)機(jī)器的 CPU Capacity 是等于 CPU Allocatable 的,但是 Memory Capacity 卻始終大于 Memory Allocatable。主要是因?yàn)?eviction 機(jī)制,在默認(rèn)情況下,會(huì)設(shè)定一個(gè) 100M 的 memory eviction hard threshold,默認(rèn)情況下,memory capacity 和 memory allocatable 之間相差的就是這 100M。

Kubernetes pod 資源管理與分配

通過(guò)上面我們了解了 kubernetes 如何定義并且確認(rèn)在當(dāng)前集群中有多少可以被利用起來(lái)的資源后,下面就來(lái)看一下 kubernetes 到底如何將這些資源分配給每一個(gè) pod 的。

在介紹如何把資源分配給每一個(gè) pod 之前,我們首先要看一下 pod 是如何申請(qǐng)資源的。這個(gè)對(duì)于大多數(shù)已經(jīng)使用過(guò) kubernetes 的童鞋來(lái)講,并不是很陌生。

01 Kubernetes pod 資源申請(qǐng)方式

kubernetes 中 pod 對(duì)資源的申請(qǐng)是以容器為最小單位進(jìn)行的,針對(duì)每個(gè)容器,它都可以通過(guò)如下兩個(gè)信息指定它所希望的資源量:

? request

? limit

比如 :

resources: 
 requests: 
   cpu: 2.5 
   memory: "40Mi" 
 limits: 
   cpu: 4.0 
   memory: "99Mi"

那么 request,limit 分別又代表了什么含義呢?

? request:

request 指的是針對(duì)這種資源,這個(gè)容器希望能夠保證能夠獲取到的最少的量。

但是在實(shí)際情況下,CPU request 是可以通過(guò) cpu.shares 特性能夠?qū)崿F(xiàn)的。

但是內(nèi)存資源,由于它是不可壓縮的,所以在某種場(chǎng)景中,是有可能因?yàn)槠渌?memory limit 設(shè)置比 request 高的容器對(duì)內(nèi)存先進(jìn)行了大量使用導(dǎo)致其他 pod 連 request 的內(nèi)存量都有可能無(wú)法得到滿足。

? limit:

limit 對(duì)于 CPU,還有內(nèi)存,指的都是容器對(duì)這個(gè)資源使用的上限。

但是這兩種資源在針對(duì)容器使用量超過(guò) limit 所表現(xiàn)出的行為也是不同的。

對(duì) CPU 來(lái)說(shuō),容器使用 CPU 過(guò)多,內(nèi)核調(diào)度器就會(huì)切換,使其使用的量不會(huì)超過(guò) limit。 對(duì)內(nèi)存來(lái)說(shuō),容器使用內(nèi)存超過(guò) limit,這個(gè)容器就會(huì)被 OOM kill 掉,從而發(fā)生容器的重啟。 在容器沒(méi)有指定 request 的時(shí)候,request 的值和 limit 默認(rèn)相等。

而如果容器沒(méi)有指定 limit 的時(shí)候,request 和 limit 會(huì)被設(shè)置成的值則根據(jù)不同的資源有不同的策略。

02 Kubernetes pod QoS 分類

kubernetes 支持用戶容器通過(guò) request、limit 兩個(gè)字段指定自己的申請(qǐng)資源信息。那么根據(jù)容器指定資源的不同情況,Pod 也被劃分為 3 種不同的 QoS 級(jí)別。分別為:

? Guaranteed

? Burstable

? BestEffort

不同的 QoS 級(jí)別會(huì)在很多方面發(fā)揮作用,比如調(diào)度,eviction。

Guaranteed 級(jí)別的 pod 主要需要滿足兩點(diǎn)要求:

? pod 中的每一個(gè) container 都必須包含內(nèi)存資源的 limit、request 信息,并且這兩個(gè)值必須相等

? pod 中的每一個(gè) container 都必須包含 CPU 資源的 limit、request 信息,并且這兩個(gè)信息的值必須相等

Burstable 級(jí)別的 pod 則需要滿足兩點(diǎn)要求:

? 資源申請(qǐng)信息不滿足 Guaranteed 級(jí)別的要求 ? pod 中至少有一個(gè) container 指定了 cpu 或者 memory 的 request 信息

BestEffort 級(jí)別的 pod 需要滿足:

? pod 中任何一個(gè) container 都不能指定 cpu 或者 memory 的 request,limit 信息

所以通過(guò)上面的描述也可以看出來(lái),

? Guaranteed level 的 Pod 是優(yōu)先級(jí)最高的,系統(tǒng)管理員一般對(duì)這類 Pod 的資源占用量比較明確。

? Burstable level 的 Pod 優(yōu)先級(jí)其次,管理員一般知道這個(gè) Pod 的資源需求的最小量,但是當(dāng)機(jī)器資源充足的時(shí)候,還是希望他們能夠使用更多的資源,所以一般 limit > request。

? BestEffort level 的 Pod 優(yōu)先級(jí)最低,一般不需要對(duì)這個(gè) Pod 指定資源量。所以無(wú)論當(dāng)前資源使用如何,這個(gè) Pod 一定會(huì)被調(diào)度上去,并且它使用資源的邏輯也是見(jiàn)縫插針。當(dāng)機(jī)器資源充足的時(shí)候,它可以充分使用,但是當(dāng)機(jī)器資源被 Guaranteed、Burstable 的 Pod 所搶占的時(shí)候,它的資源也會(huì)被剝奪,被無(wú)限壓縮。

03 Kubernetes pod 資源分配原理

我們?cè)谏厦鎯蓚€(gè)小節(jié)介紹了:

? pod 申請(qǐng)資源的方式

? pod 申請(qǐng)資源的方式對(duì)應(yīng)的是什么 QoS 級(jí)別

最終,kubelet 就是基于 【pod 申請(qǐng)的資源】 + 【pod 的 QoS 級(jí)別】來(lái)最終為這個(gè) pod 分配資源的。

而分配資源的根本方法就是基于 cgroup 的機(jī)制。

kubernetes 在拿到一個(gè) pod 的資源申請(qǐng)信息后,針對(duì)每一種資源,他都會(huì)做如下幾件事情:

?對(duì) pod 中的每一個(gè)容器,都創(chuàng)建一個(gè) container level cgroup(注:這一步真實(shí)情況是 kubernetes 向 docker daemon 發(fā)送命令完成的)。

? 然后為這個(gè) pod 創(chuàng)建一個(gè) pod level cgroup ,它會(huì)成為這個(gè) pod 下面包含的所有 container level cgroup 的父 cgroup。

? 最終,這個(gè) pod level cgroup 最終會(huì)根據(jù)這個(gè) pod 的 QoS 級(jí)別,可能被劃分到某一個(gè) QoS level cgroup 中,成為這個(gè) QoS level cgroup 的子 cgroup。

? 整個(gè) QoS level cgroup 還是所有容器的根 cgroup - kubepods 的子 cgroup。

所以這個(gè)嵌套關(guān)系通過(guò)下述圖片可以比較清晰的展示出來(lái)。

怎么深入解析kubernetes資源管理

圖中代表了一個(gè) kubernetess 計(jì)算節(jié)點(diǎn)的 cgroup 的層次結(jié)構(gòu)(對(duì)于 cpu、memory 來(lái)說(shuō) cgroup 層次結(jié)構(gòu)是完全一致的)??梢?jiàn),所有 pod 的 cgroup 配置都位于 kubepods 這個(gè)大的 cgroup 下,而之前介紹的 kube-reserved cgroup 和 system-reserved cgroup 和 kubepods cgroup 位于一級(jí),他們 3 個(gè)會(huì)共享機(jī)器的計(jì)算資源。

我們首先看如何確定這個(gè) container 對(duì)應(yīng)的 container level cgroup 和它所在的 pod level cgroup。

Container level cgroup

首先,每一個(gè) container 的 cgroup 的配置則是根據(jù)這個(gè) container 對(duì)這種資源的 request、limit 信息來(lái)配置的。

我們分別看一下,針對(duì) cpu、memory 兩種資源,kubernetes 是如何為每一個(gè)容器創(chuàng)建 container 級(jí)別的 cgroup 的。

CPU 資源

首先是 CPU 資源,我們先看一下 CPU request。

CPU request 是通過(guò) cgroup 中 CPU 子系統(tǒng)中的 cpu.shares 配置來(lái)實(shí)現(xiàn)的。

當(dāng)你指定了某個(gè)容器的 CPU request 值為 x millicores 時(shí),kubernetes 會(huì)為這個(gè) container 所在的 cgroup 的 cpu.shares 的值指定為 x * 1024 / 1000。即:

cpu.shares = (cpu in millicores * 1024) / 1000

舉個(gè)栗子,當(dāng)你的 container 的 CPU request 的值為 1 時(shí),它相當(dāng)于 1000 millicores,所以此時(shí)這個(gè) container 所在的 cgroup 組的 cpu.shares 的值為 1024。

這樣做希望達(dá)到的最終效果就是:

即便在極端情況下,即所有在這個(gè)物理機(jī)上面的 pod 都是 CPU 繁忙型的作業(yè)的時(shí)候(分配多少 CPU 就會(huì)使用多少 CPU),仍舊能夠保證這個(gè) container 的能夠被分配到 1 個(gè)核的 CPU 計(jì)算量。

其實(shí)就是保證這個(gè) container 的對(duì) CPU 資源的最低需求。

所以可見(jiàn) cpu.request 一般代表的是這個(gè) container 的最低 CPU 資源需求。但是其實(shí)僅僅通過(guò)指定 cpu.shares 還是無(wú)法完全達(dá)到上面的效果的,還需要對(duì) QoS level 的 cgroup 進(jìn)行同步的修改。至于具體實(shí)現(xiàn)原理我們?cè)诤竺鏁?huì)詳細(xì)介紹。

而針對(duì) cpu limit,kubernetes 是通過(guò) CPU cgroup 控制模塊中的 cpu.cfs_period_us,cpu.cfs_quota_us 兩個(gè)配置來(lái)實(shí)現(xiàn)的。kubernetes 會(huì)為這個(gè) container cgroup 配置兩條信息:

cpu.cfs_period_us = 100000 (i.e. 100ms) cpu.cfs_quota_us = quota = (cpu in millicores * 100000) / 1000

在 cgroup 的 CPU 子系統(tǒng)中,可以通過(guò)這兩個(gè)配置,嚴(yán)格控制這個(gè) cgroup 中的進(jìn)程對(duì) CPU 的使用量,保證使用的 CPU 資源不會(huì)超過(guò) cfs_quota_us/cfs_period_us,也正好就是我們一開(kāi)始申請(qǐng)的 limit 值。

可見(jiàn)通過(guò) cgroup 的這個(gè)特性,就實(shí)現(xiàn)了限制某個(gè)容器的 CPU 最大使用量的效果。

Memory

針對(duì)內(nèi)存資源,其實(shí) memory request 信息并不會(huì)在 container level cgroup 中有體現(xiàn)。kubernetes 最終只會(huì)根據(jù) memory limit 的值來(lái)配置 cgroup 的。

在這里 kubernetes 使用的 memory cgroup 子系統(tǒng)中的 memory.limit_in_bytes 配置來(lái)實(shí)現(xiàn)的。配置方式如下:

memory.limit_in_bytes = memory limit bytes

memory 子系統(tǒng)中的 limit_in_bytes 配置,可以限制一個(gè) cgroup 中的所有進(jìn)程可以申請(qǐng)使用的內(nèi)存的最大量,如果超過(guò)這個(gè)值,那么根據(jù) kubernetes 的默認(rèn)配置,這個(gè)容器會(huì)被 OOM killed,容器實(shí)例就會(huì)發(fā)生重啟。

可見(jiàn)如果是這種實(shí)現(xiàn)方式的話,其實(shí) kubernetes 并不能保證 pod 能夠真的申請(qǐng)到它指定的 memory.request 那么多的內(nèi)存量,這也可能是讓很多使用 kubernetes 的童鞋比較困惑的地方。因?yàn)?kubernetes 在對(duì) pod 進(jìn)行調(diào)度的時(shí)候,只是保證一臺(tái)機(jī)器上面的 pod 的 memory.request 之和小于等于 node allocatable memory 的值。所以如果有一個(gè) pod 的 memory.limit 設(shè)置的比較高,甚至沒(méi)有設(shè)置,就可能會(huì)出現(xiàn)一種情況,就是這個(gè) pod 使用了大量的內(nèi)存(大于它的 request,但是小于它的 limit),此時(shí)鑒于內(nèi)存資源是不可壓縮的,其他的 pod 可能就沒(méi)有足夠的內(nèi)存余量供其進(jìn)行申請(qǐng)了。當(dāng)然,這個(gè)問(wèn)題也可以通過(guò)一個(gè)特性在一定程度進(jìn)行緩解,這個(gè)會(huì)在下面介紹。

當(dāng)然讀者可能會(huì)有一個(gè)問(wèn)題,如果 pod 沒(méi)有指定 request 或者 limit 會(huì)怎樣配置呢?

如果沒(méi)有指定 limit 的話,那么 cfs_quota_us 將會(huì)被設(shè)置為 -1,即沒(méi)有限制。而如果 limit 和 request 都沒(méi)有指定的話,cpu.shares 將會(huì)被指定為 2,這個(gè)是 cpu.shares 允許指定的最小數(shù)值了??梢?jiàn)針對(duì)這種 pod,kubernetes 只會(huì)給他分配最少的 CPU 資源。

而對(duì)于內(nèi)存來(lái)說(shuō),如果沒(méi)有 limit 的指定的話,memory.limit_in_bytes 將會(huì)被指定為一個(gè)非常大的值,一般是 2^64 ,可見(jiàn)含義就是不對(duì)內(nèi)存做出限制。

針對(duì)上面對(duì) container level cgroup 的介紹,我們舉個(gè)具體的栗子,假設(shè)一個(gè) pod 名字叫做 pod-burstable-1 是由兩個(gè)業(yè)務(wù)容器構(gòu)成的 container1 container2,這兩個(gè) container 的資源配置分別如下:

-  image: image1 
   name: container1 
   resources: 
     limits: 
       cpu: 1 
       memory: 1Gi 
     requests: 
       cpu: 1 
       memory: 1Gi
-  image: image2 
   name: container2 
   resources: 
     limits: 
       cpu: 2 
       memory: 2Gi 
     requests: 
       cpu: 1 
       memory: 1Gi

所以可見(jiàn)這個(gè) pod 所有容器的 request,limit 都已經(jīng)被指定,但是 request 和 limit 并不完全相等,所以這個(gè) pod 的 QoS 級(jí)別為 Burstable。

另外還有一個(gè) pod 名字叫做 pod-guaranteed-1,它由一個(gè) container 構(gòu)成,資源配置如下:

-  image: image3 
   name: container3 
   resources: 
     limits: 
       cpu: 1 
       memory: 1Gi 
     requests: 
       cpu: 1 
       memory: 1Gi

通過(guò)這個(gè)配置可見(jiàn),它是一個(gè) Guaranteed 級(jí)別的 Pod。

另外還有一個(gè) pod 叫做 pod-besteffort-1 它有一個(gè) container 構(gòu)成,資源配置信息完全為空,那么這個(gè) pod 就是 besteffort 級(jí)別的 pod。

所以通過(guò)上述描述的 cgroup 配置方式,這 3 個(gè) pod 會(huì)創(chuàng)建 4 個(gè) container cgroup 如下圖所示:

怎么深入解析kubernetes資源管理

Pod level cgroup

創(chuàng)建完 container level 的 cgroup 之后,kubernetes 就會(huì)為同屬于某個(gè) pod 的 containers 創(chuàng)建一個(gè) pod level cgroup。作為它們的父 cgroup。至于為何要引入 pod level cgroup,主要是基于幾點(diǎn)原因:

? 方便對(duì) pod 內(nèi)的容器資源進(jìn)行統(tǒng)一的限制

? 方便對(duì) pod 使用的資源進(jìn)行統(tǒng)一統(tǒng)計(jì)

所以對(duì)于我們上面舉的栗子,一個(gè) pod 名稱為 pod-burstable-1,它包含兩個(gè) container:container1、container2,那么這個(gè) pod cgroup 的目錄結(jié)構(gòu)如下:

pod-burstable-1 
  | 
  +- container1 
  | 
  +- container2

注:真實(shí)情況下 pod cgroup 的名稱是 pod,這里為了表示清楚,用 pod name 代替

那么為了保證這個(gè) pod 內(nèi)部的 container 能夠獲取到期望數(shù)量的資源,pod level cgroup 也需要進(jìn)行相應(yīng)的 cgroup 配置。而配置的方式也基本滿足一個(gè)原則:

pod level cgroup 的資源配置應(yīng)該等于屬于它的 container 的資源需求之和。

但是,這個(gè)規(guī)則在不同的 QoS 級(jí)別的 pod 下也有些細(xì)節(jié)上的區(qū)別。所以針對(duì) Guaranteed 和 Burstable 級(jí)別的 Pod,每一個(gè) Pod 的 cgroup 配置都可以由下述 3 個(gè)公式來(lái)完成

cpu.shares=sum(pod.spec.containers.resources.requests\[cpu\])

cpu.cfs_quota_us=sum(pod.spec.containers.resources.limits\[cpu\]

memory.limit_in_bytes=sum(pod.spec.containers.resources.limits\[memory\])

從公式中可見(jiàn),pod level cgroup 的 cpu.shares、 cpu.cfs_quota_us、memory.limit_in_bytes 最終都等于屬于這個(gè) pod 的 container 的這 3 值的和。

當(dāng)然在 Burstable,Besteffort 的場(chǎng)景下,有可能 container 并沒(méi)有指定 cpu.limit、memory.limit,此時(shí) cpu.cfs_quota_us、memory.limit_in_bytes 將不會(huì)采用這個(gè)公式,因?yàn)榇藭r(shí)相當(dāng)于不會(huì)對(duì) pod 的 cpu,memory 的使用量做最大值的限制,所以此時(shí)這兩個(gè)配置也會(huì)參照上一節(jié)中說(shuō)道的“如果 container 如果沒(méi)有設(shè)置 request 和 limit 的話”處理的方式一樣。

所以針對(duì)我們?cè)谏厦娴呐e的例子 pod-burstable-1,就是一個(gè)非常典型的 burstable pod,根據(jù) burstable pod 的資源配置公式,kubernetes 會(huì)為這個(gè) pod 創(chuàng)建一個(gè) pod 級(jí)別的 cgroup。另外 pod-guaranteed-1 也會(huì)創(chuàng)建一個(gè) cgroup,這兩個(gè) pod level cgroup 的配置如下圖所示

怎么深入解析kubernetes資源管理

Besteffort pod cgroup

上面我們講到的 pod cgroup 配置規(guī)律是不能應(yīng)用于 besteffort pod 的。

因?yàn)檫@個(gè) QoS 級(jí)別的 pod 就像名字描述的那樣,kubernetes 只能盡可能的保證你的資源使用,在資源被極端搶占的情況,這種 pod 的資源使用量應(yīng)該被一定程度的限制,無(wú)論是 cpu,還是內(nèi)存(當(dāng)然這兩種限制的機(jī)制完全不同)。

所以針對(duì) besteffort 級(jí)別的 pod,由于這里面的所有容器都不包含 request,limit 信息,所以它的配置非常統(tǒng)一。

所以針對(duì) cpu,整個(gè) pod 的 cgroup 配置就只有下面:

cpu.shares = 2

可見(jiàn)這個(gè)配置的目標(biāo)就是能夠達(dá)到,它在機(jī)器 cpu 資源充足時(shí)能使用整個(gè)機(jī)器的 cpu,因?yàn)闆](méi)有指定 limit。但是在 cpu 資源被極端搶占時(shí),它能夠被分配的 cpu 資源很有限,相當(dāng)于 2 millicores。

針對(duì)內(nèi)存,沒(méi)有任何特殊配置,只是采用默認(rèn),雖然在這種情況下,這個(gè) pod 可能會(huì)使用整個(gè)機(jī)器那么多的內(nèi)存,但是 kubernetes eviction 機(jī)制會(huì)保證,內(nèi)存不足時(shí),優(yōu)先刪除 Besteffort 級(jí)別的 pod 騰出足夠的內(nèi)存資源。

QoS level cgroup

前面也提過(guò),在 kubelet 啟動(dòng)后,會(huì)在整個(gè) cgroup 系統(tǒng)的根目錄下面創(chuàng)建一個(gè)名字叫做 kubepods 子 cgroup,這個(gè) cgroup 下面會(huì)存放所有這個(gè)節(jié)點(diǎn)上面的 pod 的 cgroup。從而達(dá)到了限制這臺(tái)機(jī)器上所有 Pod 的資源的目的。

在 kubepods cgroup 下面,kubernetes 會(huì)進(jìn)一步再分別創(chuàng)建兩個(gè) QoS level cgroup,名字分別叫做:

? burstable

? besteffort

通過(guò)名字也可以推斷出,這兩個(gè) QoS level 的 cgroup 肯定是作為各自 QoS 級(jí)別的所有 Pod 的父 cgroup 來(lái)存在的。

那么問(wèn)題就來(lái)了:

? guaranteed 級(jí)別的 pod 的 pod cgroup 放在哪里了呢?

? 這兩個(gè) QoS level cgroup 存在的目的是什么?

首先第一個(gè)問(wèn)題,所有 guaranteed 級(jí)別的 pod 的 cgroup 其實(shí)直接位于 kubepods 這個(gè) cgroup 之下,和 burstable、besteffort QoS level cgroup 同級(jí)。主要原因在于 guaranteed 級(jí)別的 pod 有明確的資源申請(qǐng)量(request)和資源限制量(limit),所以并不需要一個(gè)統(tǒng)一的 QoS level 的 cgroup 進(jìn)行管理或限制。

針對(duì) burstable 和 besteffort 這兩種類型的 pod,在默認(rèn)情況下,kubernetes 則是希望能盡可能地提升資源利用率,所以并不會(huì)對(duì)這兩種 QoS 的 pod 的資源使用做限制。

但是在很多場(chǎng)景下,系統(tǒng)管理員還是希望能夠盡可能保證 guaranteed level pod 這種高 QoS 級(jí)別的 pod 的資源,尤其是不可壓縮資源(如內(nèi)存),不要被低 QoS 級(jí)別的 pod 提前使用,導(dǎo)致高 QoS 級(jí)別的 pod 連它 request 的資源量的資源都無(wú)法得到滿足。

所以,kubernetes 才引入了 QoS level cgroup,主要目的就是限制低 QoS 級(jí)別的 pod 對(duì)不可壓縮資源(如內(nèi)存)的使用量,為高 QoS 級(jí)別的 pod 做資源上的預(yù)留。三種 QoS 級(jí)別的優(yōu)先級(jí)由高到低為 guaranteed > burstable > besteffort。

那么到底如何實(shí)現(xiàn)這種資源預(yù)留呢?主要是通過(guò) kubelet 的 experimental-qos-reserved 參數(shù)來(lái)控制,這個(gè)參數(shù)能夠控制以怎樣的程度限制低 QoS 級(jí)別的 pod 的資源使用,從而對(duì)高級(jí)別的 QoS 的 pod 實(shí)現(xiàn)資源上的預(yù)留,保證高 QoS 級(jí)別的 pod 一定能夠使用到它 request 的資源。

目前只支持對(duì)內(nèi)存這種不可壓縮資源的預(yù)留情況進(jìn)行指定。比如 experimental-qos-reserved=memory=100%,代表我們要 100% 為高 QoS level 的 pod 預(yù)留資源。

所以針對(duì)這個(gè)場(chǎng)景,對(duì)于內(nèi)存資源來(lái)說(shuō),整個(gè) QoS Level cgroup 的配置規(guī)則如下:

burstable/memory.limit_in_bytes =
    Node.Allocatable - {(summation of memory requests of `Guaranteed` pods)_(reservePercent / 100)} besteffort/memory.limit_in_bytes =
    Node.Allocatable - {(summation of memory requests of all `Guaranteed` and `Burstable` pods)_(reservePercent / 100)}

從公式中可見(jiàn),burstable 的 cgroup 需要為比他等級(jí)高的 guaranteed 級(jí)別的 pod 的內(nèi)存資源做預(yù)留,所以默認(rèn)情況下,如果沒(méi)有指定這個(gè)參數(shù),burstable cgroup 中的 pod 可以把整個(gè)機(jī)器的內(nèi)存都占滿,但是如果開(kāi)啟這個(gè)特性,burstable cgroup 的內(nèi)存限制就需要?jiǎng)討B(tài)的根據(jù)當(dāng)前有多少 guaranteed 級(jí)別 pod 來(lái)進(jìn)行動(dòng)態(tài)調(diào)整了。

besteffort 也是類似,但是不一樣的地方在于 besteffort 不僅要為 guaranteed 級(jí)別的 pod 進(jìn)行資源預(yù)留,還要為 burstable 級(jí)別的 pod 也進(jìn)行資源的預(yù)留。

所以舉個(gè)栗子,當(dāng)前機(jī)器的 allocatable 內(nèi)存資源量為 8G,我們?yōu)檫@臺(tái)機(jī)器的 kubelet 開(kāi)啟 experimental-qos-reserved 參數(shù),并且設(shè)置為 memory=100%。如果此時(shí)創(chuàng)建了一個(gè)內(nèi)存 request 為 1G 的 guaranteed level 的 pod,那么此時(shí)這臺(tái)機(jī)器上面的 burstable QoS level cgroup 的 memory.limit_in_bytes 的值將會(huì)被設(shè)置為 7G,besteffort QoS level cgroup 的 memory.limit_in_bytes 的值也會(huì)被設(shè)置為 7G。

而如果此時(shí)又創(chuàng)建了一個(gè) burstable level 的 pod,它的內(nèi)存申請(qǐng)量為 2G,那么此時(shí) besteffort QoS level cgroup 的 memory.limit_in_bytes 的值也會(huì)被調(diào)整為 5G。

內(nèi)存雖然搞定了,但是對(duì)于 cpu 資源反而帶來(lái)一些麻煩。

針對(duì) besteffort 的 QoS,它的 cgroup 的 CPU 配置還是非常簡(jiǎn)單:

besteffort/cpu.shares = 2

但是針對(duì) burstable 的 QoS,由于所有的 burstable pod 現(xiàn)在都位于 kubepods 下面的 burstable 這個(gè)子組下面,根據(jù) cpu.shares 的背后實(shí)現(xiàn)原理,位于不同層級(jí)下面的 cgroup,他們看待同樣數(shù)量的 cpu.shares 配置可能最終獲得不同的資源量。比如在 Guaranteed 級(jí)別的 pod cgroup 里面指定的 cpu.shares=1024,和 burstable 下面的某個(gè) pod cgroup 指定 cpu.shares=1024 可能最終獲取的 cpu 資源并不完全相同。這個(gè)是因?yàn)?cpu.shares 自身機(jī)制導(dǎo)致的。

所以為了能夠解決這個(gè)問(wèn)題,kubernetes 也必須動(dòng)態(tài)調(diào)整 burstable cgroup 的 cpu.shares 的配置,如下文檔中描述的那樣:

burstable/cpu.shares = max(sum(Burstable pods cpu requests, 2)

來(lái)保證,相同的 cpu.shares 配置,對(duì)于 guaranteed 級(jí)別的 pod 和 burstable 的 pod 來(lái)說(shuō)是完全一樣的。

至于為什么這樣做我們將在后面進(jìn)行詳細(xì)的解釋。

所以對(duì)于我們上面的例子,3 個(gè) pod 分別為 guaranteed,burstable,besteffort 級(jí)別的,假設(shè)當(dāng)前機(jī)器的 kubelet 開(kāi)啟了 experimental-qos-reserved 參數(shù)并且指定為 memory=100%。假設(shè)這 3 pod 運(yùn)行在一臺(tái) 3 核 8G 內(nèi)存的機(jī)器上面,那么此時(shí)整個(gè)機(jī)器的 cgroup 配置將如下:

怎么深入解析kubernetes資源管理

通過(guò)這一系列的配置后,我們達(dá)到的效果就是:

? 在機(jī)器 cpu 資源空閑時(shí),pod-guaranteed-1 這個(gè) pod 最多可以使用 1 核 cpu 的計(jì)算力。pod-burstable-1 這個(gè) pod 最多可以使用 3 核的 cpu 計(jì)算力,pod-besteffort-1 這個(gè) pod 可以把機(jī)器剩余的計(jì)算力跑滿。

? 當(dāng)機(jī)器 cpu 資源被搶占的時(shí)候,比如 pod-guaranteed-1、pod-burstable-1 這兩種資源都在 100% 的使用 cpu 的時(shí)候,pod-guaranteed-1 仍舊可以獲取 1 核的 cpu 計(jì)算力,pod-burstable-1 仍舊可以獲取 2 核的計(jì)算力。但是 pod-besteffort-1 僅僅能獲取 2 milicores 的 cpu 計(jì)算力,是 pod-burstable-1 的千分之一。

? 該機(jī)器的 besteffort 級(jí)別的 pod,能夠使用的最大內(nèi)存量為[機(jī)器內(nèi)存 8G] - [sum of request of burstable 2G] - [sum of request of guaranteed 1G] = 5G

? 該機(jī)器的 burstable 級(jí)別的 pod,能夠使用的最大內(nèi)存量為 [機(jī)器內(nèi)存 8G] - [sum of request of guaranteed 1G] = 7G

CPU Request 的實(shí)現(xiàn)

CPU 資源的 request 量代表這個(gè)容器期望獲取的最小資源量。它是通過(guò) cgroup cpu.shares 特性來(lái)實(shí)現(xiàn)的。但是這個(gè) cpu.shares 真實(shí)代表的這個(gè) cgroup 能夠獲取機(jī)器 CPU 資源的【比重】,并非【絕對(duì)值】。

比如某個(gè) cgroup A 它的 cpu.shares = 1024 并不代表這個(gè) cgroup A 能夠獲取 1 核的計(jì)算資源,如果這個(gè) cgroup 所在機(jī)器一共有 2 核 CPU,除了這個(gè) cgroup 還有另外 cgroup B 的 cpu.shares 的值為 2048 的話,那么在 CPU 資源被高度搶占的時(shí)候,cgroup A 只能夠獲取 2 * (1024/(1024 + 2048)) 即 2/3 的 CPU 核資源。

那么 kubernetes 又是如何實(shí)現(xiàn),無(wú)論是什么 QoS 的 pod,只要它的某個(gè)容器的 cpu.shares = 1024,那么它就一定能夠獲取 1 核的計(jì)算資源的呢?

實(shí)現(xiàn)的方式其實(shí)就是通過(guò)合理的對(duì) QoS level cgroup,Pod level cgroup 進(jìn)行動(dòng)態(tài)配置來(lái)實(shí)現(xiàn)的。

我們還可以用上面的栗子繼續(xù)描述,假設(shè)目前這 3 個(gè) Pod 就位于一個(gè)有 3 個(gè) CPU 核的物理機(jī)上面。此時(shí)這臺(tái)機(jī)器的 CPU cgroup 的配置會(huì)變成下面的樣子

?kubepods cgroup 的 cpu.shares 將會(huì)被設(shè)置為 3072。

?pod-guaranteed-1 中的 pod cgroup 的 cpu.shares 將會(huì)被設(shè)置為 1024,pod cgroup 內(nèi)的 container3 的 container cgroup 的 cpu.shares 將會(huì)被設(shè)置為 1024。

?pod-burstable-1 所在 burstable QoS level cgroup cpu.shares 將會(huì)被設(shè)置為 2048

?pod-burstable-1 的 pod cgroup 的 cpu.shares 是 2048

?pod-burstable-1 中的 container1 的 cpu.shares 是 1024

?pod-burstable-1 中的 container2 的 cpu.shares 是 1024

所以此時(shí)的層次結(jié)構(gòu)如下圖:

怎么深入解析kubernetes資源管理

因?yàn)?besteffort 的 cpu.shares 的值僅僅為 2,可以忽略。

所以此時(shí)在計(jì)算 container1、container2、container3 在 CPU 繁忙時(shí)的 CPU 資源時(shí),就可以按照下述公式來(lái)進(jìn)行計(jì)算:

? container3 = (1024/1024) * (1024/(1024+2048)) * 3 = 1

? container1 = (1024/(1024+1024)) * (2048/2048) * (2048/1024+2048) * 3 = 1

? container2 = (1024/(1024+1024)) * (2048/2048) * (2048/1024+2048) * 3 = 1

可見(jiàn),kubernetes 是通過(guò)巧妙的設(shè)置 kubepods cgroup 的 cpu.shares,以及合理的更新 burstable QoS level cgroup 的配置來(lái)實(shí)現(xiàn) cpu.shares 就等于容器可以獲取的最小 CPU 資源的效果的。

看完上述內(nèi)容,你們對(duì)怎么深入解析kubernetes資源管理有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI