047.叢集管理-資源及配額管理
阿新 • • 發佈:2020-04-07
一 資源管理
1.1 資源排程機制
對於Kubernetes資源,有兩個重要引數:CPU Request與Memory Request。 通常在定義Pod時並沒有定義這兩個引數,此時Kubernetes會認為該Pod所需的資源很少,並可以將其排程到任何可用的Node上。因此,當叢集中的計算資源不很充足時,如果叢集中的Pod負載突然增大,就會使某個Node的資源嚴重不足。為了避免Node系統掛掉,該Node會選擇“清理”某些Pod來釋放資源,此時每個Pod都可能被“清理”。若其中某些Pod非常重要,比如與資料儲存相關的、與登入相關的、與查詢餘額相關的,需要在系統資源嚴重不足時,也得保障這些Pod的存活。 Kubernetes中該保障機制的核心如下:- 通過資源限額來確保不同的Pod只能佔用指定的資源。
- 允許叢集的資源被超額分配,以提高叢集的資源利用率。
- 為Pod劃分等級,確保不同等級的Pod有不同的服務質量(QoS),資源不足時,低等級的Pod會被清理,以確保高等級的Pod穩定執行。
- spec.container[].resources.requests.cpu
- spec.container[].resources.limits.cpu
- spec.container[].resources.requests.memory
- spec.container[].resources.limits.memory
1.2 批量設定
二 計算資源管理
2.1 Requests和Limits
以CPU為例,下圖顯示了未設定Limits和設定了Requests、Limits的CPU使用率的區別。儘管Requests和Limits只能被設定到容器上,但是設定Pod級別的Requests和Limits能大大提高管理Pod的便利性和靈活性,因此在Kubernetes中提供了對Pod級別的Requests和Limits的配置。對於CPU和記憶體而言,Pod的Requests或Limits是指該Pod中所有容器的Requests或Limits的總和(對於Pod中沒有設定Requests或Limits的容器,該項的值被當作0或者按照叢集配置的預設值來計算)。
2.2 CPU和Memory計算
CPU的Requests和Limits是通過CPU數(cpus)來度量的。CPU的資源值是絕對值,而不是相對值,比如0.1CPU在單核或多核機器上是一樣的,都嚴格等於0.1CPUcore。 Memory記憶體的Requests和Limits計量單位是位元組數。使用整數或者定點整數加上國際單位制來表示記憶體值。國際單位制包括十進位制的E、P、T、G、M、K、m,或二進位制的Ei、Pi、Ti、Gi、Mi、Ki。KiB與MiB是以二進位制表示的位元組單位,常見的KB與MB則是以十進位制表示的位元組單位,比如:- 1KB(KiloByte)= 1000Bytes = 8000Bits
- 1KiB(KibiByte)= 2^10Bytes = 1024Bytes = 8192Bits
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 name: frontend 5 spec: 6 continers: 7 - name: db 8 image: mysql 9 resources: 10 requests: 11 memory: "64Mi" 12 cpu: "250m" 13 limits: 14 memory: "128Mi" 15 cpu: "500m" 16 - name: wp 17 image: wordpress 18 resources: 19 requests: 20 memory: "64Mi" 21 cpu: "250m" 22 limits: 23 memory: "128Mi" 24 cpu: "500m" 25解讀:如上所示,該Pod包含兩個容器,每個容器配置的Requests都是0.25CPU和64MiB(226 Bytes)記憶體,而配置的Limits都是0.5CPU和 128MiB(227 Bytes)記憶體。 這個Pod的Requests和Limits等於Pod中所有容器對應配置的總和,所以Pod的Requests是0.5CPU和128MiB(227 Bytes)記憶體,Limits是1CPU和256MiB(228 Bytes)記憶體。
2.3 Requests和Limits的Pod排程機制
當一個Pod建立成功時,Kubernetes排程器(Scheduler)會為該Pod選擇一個節點來執行。對於每種計算資源(CPU和Memory)而言,每個節點都有一個能用於執行Pod的最大容量值。排程器在排程時,首先要確保排程後該節點上所有Pod的CPU和記憶體的Requests總和,不超過該節點能提供給Pod使用的CPU和Memory的最大容量值。 例如,某個節點上的CPU資源充足,而記憶體為4GB,其中3GB可以執行Pod,而某Pod的Memory Requests為1GB、Limits為2GB,那麼在這個節點上最多可以執行3個這樣的Pod。假設該節點已經啟動3個此Pod例項,而這3個Pod的實際記憶體使用都不足500MB,那麼理論上該節點的可用記憶體應該大於1.5GB。但是由於該節點的Pod Requests總和已經達到節點的可用記憶體上限,因此Kubernetes不會再將任何Pod例項排程到該節點上。 注意:可能某節點上的實際資源使用量非常低,但是已執行Pod配置的Requests值的總和非常高,再加上需要排程的Pod的Requests值,會超過該節點提供給Pod的資源容量上限,這時Kubernetes仍然不會將Pod排程到該節點上。如果Kubernetes將Pod排程到該節點上,之後該節點上執行的Pod又面臨服務峰值等情況,就可能導致Pod資源短缺。2.4 Requests和Limits機制
kubelet在啟動Pod的某個容器時,會將容器的Requests和Limits值轉化為相應的容器啟動引數傳遞給容器執行器(Docker或者rkt)。如果容器的執行環境是Docker,那麼會傳遞如下4個引數給Docker容器:- spec.container[].resources.requests.cpu
- spec.container[].resources.limits.cpu
- spec.container[].resources.requests.memory
- spec.container[].resources.limits.memory
2.5 計算資源使用情況監控
Pod的資源用量會作為Pod的狀態資訊一同上報給Master。如果在叢集中配置了Heapster來監控叢集的效能資料,那麼還可以從Heapster中檢視Pod的資源用量資訊。2.6 計算資源排程常見問題
- Pod狀態為Pending,錯誤資訊為FailedScheduling
- 新增更多的節點到叢集中;
- 停止一些不必要的執行中的Pod,釋放資源;
- 檢查Pod的配置,錯誤的配置可能導致該Pod永遠無法被排程執行。比如整個叢集中所有節點都只有1CPU,而Pod配置的CPURequests為2,該Pod就不會被排程執行。
- 容器被強行終止(Terminated)
三 資源配置範圍管理(LimitRange)
3.1 LimitRange
在預設情況下,Kubernetes不會對Pod加上CPU和記憶體限制,這意味著Kubernetes系統中任何Pod都可以使用其所在節點的所有可用的CPU和記憶體。通過配置Pod的計算資源Requests和Limits,可以限制Pod的資源使用,但對於Kubernetes叢集管理員而言,配置每一個Pod的Requests和Limits是煩瑣的,而且很受限制。更多時候,需要對叢集內Requests和Limits的配置做一個全侷限制。 常見的配置場景如下:- 叢集中的每個節點都有2GB記憶體,叢集管理員不希望任何Pod申請超過2GB的記憶體:因為在整個叢集中都沒有任何節點能滿足超過2GB記憶體的請求。如果某個Pod的記憶體配置超過2GB,那麼該Pod將永遠都無法被排程到任何節點上執行。為了防止這種情況的發生,叢集管理員希望能在系統管理功能中設定禁止Pod申請超過2GB記憶體。
- 叢集由同一個組織中的兩個團隊共享,分別執行生產環境和開發環境。生產環境最多可以使用8GB記憶體,而開發環境最多可以使用512MB記憶體。叢集管理員希望通過為這兩個環境建立不同的名稱空間,併為每個名稱空間設定不同的限制來滿足這個需求。
- 使用者建立Pod時使用的資源可能會剛好比整個機器資源的上限稍小,而恰好剩下的資源大小非常尷尬:不足以執行其他任務但整個叢集加起來又非常浪費。因此,叢集管理員希望設定每個Pod都必須至少使用叢集平均資源值(CPU和記憶體)的20%,這樣叢集能夠提供更好的資源一致性的排程,從而減少了資源浪費。
示例1: [root@k8smaster01 study]# kubectl create namespace limit-example #建立namespace [root@k8smaster01 study]# vi limits.yaml #建立limitrange
1 apiVersion: v1 2 kind: LimitRange 3 metadata: 4 name: mylimits 5 spec: 6 limits: 7 - max: 8 cpu: "4" 9 memory: 2Gi 10 min: 11 cpu: 200m 12 memory: 6Mi 13 maxLimitRequestRatio: 14 cpu: 3 15 memory: 2 16 type: Pod 17 - default: 18 cpu: 300m 19 memory: 200Mi 20 defaultRequest: 21 cpu: 200m 22 memory: 100Mi 23 max: 24 cpu: "2" 25 memory: 1Gi 26 min: 27 cpu: 100m 28 memory: 3Mi 29 maxLimitRequestRatio: 30 cpu: 5 31 memory: 4 32 type: Container 33[root@k8smaster01 study]# kubectl create -f limits.yaml --namespace=limit-example #為Namespace“limit-example”建立LimitRange [root@k8smaster01 study]# kubectl get limitranges -n limit-example [root@k8smaster01 study]# kubectl describe limitranges mylimits -n limit-example 解讀:
- 不論是CPU還是記憶體,在LimitRange中,Pod和Container都可以設定Min、Max和MaxLimit/RequestsRatio引數。Container還可以設定Default Request和Default Limit引數,而Pod不能設定Default Request和DefaultLimit引數。
- 對Pod和Container的引數解釋如下:
- Container的Min(如上圖100m和3Mi)是Pod中所有容器的Requests值下限;Container的Max(如上圖2和1Gi)是Pod中所有容器的Limits值上限;Container的Default Request(如上圖200m和100Mi)是Pod中所有未指定Requests值的容器的預設Requests值;Container的DefaultLimit(如上圖300m和200Mi)是Pod中所有未指定Limits值的容器的預設Limits值。對於同一資源型別,這4個引數必須滿足以下關係:Min ≤ Default Request ≤ Default Limit ≤ Max。
- Pod的Min(如上圖200m和6Mi)是Pod中所有容器的Requests值的總和下限;Pod的Max(如上圖4和2Gi)是Pod中所有容器的Limits值的總和上限。當容器未指定Requests值或者Limits值時,將使用Container的Default Request值或者Default Limit值。
- Container的Max Limit/Requests Ratio(如上圖5和4)限制了Pod中所有容器的Limits值與Requests值的比例上限;而Pod的MaxLimit/RequestsRatio(如上圖3和2)限制了Pod中所有容器的Limits值總和與Requests值總和的比例上限。
- 如果設定了Container的Max,那麼對於該類資源而言,整個叢集中的所有容器都必須設定Limits,否則無法成功建立。Pod內的容器未配置Limits時,將使用Default Limit的值(本例中的300mCPU和200MiB記憶體),如果也未配置Default,則無法成功建立。
- 如果設定了Container的Min,那麼對於該類資源而言,整個叢集中的所有容器都必須設定Requests。如果建立Pod的容器時未配置該類資源的Requests,那麼在建立過程中會報驗證錯誤。Pod裡容器的Requests在未配置時,可以使用預設值default Request(本例中的200mCPU和100MiB記憶體);如果未配置而又沒有使用預設值default Request,那麼會預設等於該容器的Limits;如果此時Limits也未定義,就會報錯。
- 對於任意一個Pod而言,該Pod中所有容器的Requests總和必須大於或等於6MiB,而且所有容器的Limits總和必須小於或等於1GiB;同樣,所有容器的CPU Requests總和必須大於或等於200m,而且所有容器的CPU Limits總和必須小於或等於2。
- Pod裡任何容器的Limits與Requests的比例都不能超過Container的MaxLimit/RequestsRatio;Pod裡所有容器的Limits總和與Requests的總和的比例不能超過Pod的MaxLimit/RequestsRatio。
[root@k8smaster01 study]# kubectl run nginx --image=nginx --replicas=1 --namespace=limit-example [root@k8smaster01 study]# kubectl get pods --namespace=limit-example NAME READY STATUS RESTARTS AGE nginx-7bb7cd8db5-mzcvb 1/1 Running 0 54s 解讀:名稱空間中LimitRange只會在Pod建立或者更新時執行檢查。如果手動修改LimitRange為一個新的值,那麼這個新的值不會去檢查或限制之前已經在該名稱空間中建立好的Pod。如果在建立Pod時配置的資源值(CPU或者記憶體)超過了LimitRange的限制,那麼該建立過程會報錯,在錯誤資訊中會說明詳細的錯誤原因。 [root@k8smaster01 study]# kubectl get pods nginx-7bb7cd8db5-mzcvb --namespace=limit-example -o yaml | grep resources -C 6 #檢視該Pod的resource
1 uid: 5fd37e03-ea08-44f3-a2c7-30ad31c7ab4a 2 spec: 3 containers: 4 - image: nginx 5 imagePullPolicy: Always 6 name: nginx 7 resources: 8 limits: 9 cpu: 300m 10 memory: 200Mi 11 requests: 12 cpu: 200m 13 memory: 100Mi 14解讀:由於該Pod未配置資源Requests和Limits,所以使用了namespace limit-example中的預設CPU和記憶體定義的Requests和Limits值。 [root@k8smaster01 study]# vi invalid-pod.yaml
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 name: invalid-pod 5 spec: 6 containers: 7 - name: kubernetes-serve-hostname 8 image: gcr.azk8s.cn/google_containers/server_hostname 9 resources: 10 limits: 11 cpu: "3" 12 memory: 100Mi 13[root@k8smaster01 study]# kubectl create -f invalid-pod.yaml --namespace=limit-example Error from server (Forbidden): error when creating "invalid-pod.yaml": pods "invalid-pod" is forbidden: maximum cpu usage per Container is 2, but limit is 3 解讀:建立該Pod,會出現系統報錯了,並且提供的錯誤原因為超過資源限制。 [root@k8smaster01 study]# vi limit-test-nginx.yaml
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 name: limit-test-nginx 5 labels: 6 name: limit-test-nginx 7 spec: 8 containers: 9 - name: limit-test-nginx 10 image: nginx 11 resources: 12 limits: 13 cpu: "1" 14 memory: 512Mi 15 requests: 16 cpu: "0.8" 17 memory: 250Mi 18[root@k8smaster01 study]# kubectl create -f limit-test-nginx.yaml -n limit-example Error from server (Forbidden): error when creating "limit-test-nginx.yaml": pods "limit-test-nginx" is forbidden: memory max limit to request ratio per Pod is 2, but provided ratio is 2.048000 解讀:由於limit-test-nginx這個Pod的全部記憶體Limits總和與Requests總和的比例為512∶250,大於在LimitRange中定義的Pod的最大比率2(maxLimitRequestRatio.memory=2),因此建立失敗。 [root@k8smaster01 study]# vi valid-pod.yaml
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 name: valid-pod 5 labels: 6 name: valid-pod 7 spec: 8 containers: 9 - name: kubernetes-serve-hostname 10 image: gcr.io/google_containers/serve_hostname 11 resources: 12 limits: 13 cpu: "1" 14 memory: 512Mi 15[root@k8smaster01 study]# kubectl create -f valid-pod.yaml -n limit-example [root@k8smaster01 study]# kubectl get pods valid-pod -n limit-example -o yaml | grep resources -C 6 #檢視該Pod的資源資訊
1 uid: 59e3d05a-8c09-479e-a3ad-1a4dbfd8e946 2 spec: 3 containers: 4 - image: gcr.io/google_containers/serve_hostname 5 imagePullPolicy: Always 6 name: kubernetes-serve-hostname 7 resources: 8 limits: 9 cpu: "1" 10 memory: 512Mi 11 requests: 12 cpu: "1" 13 memory: 512Mi 14解讀:該Pod配置了明確的Limits和Requests,因此該Pod不會使用在namespace limit-example中定義的default和default Request。 注意:CPU Limits強制配置這個選項在Kubernetes叢集中預設是開啟的;除非叢集管理員在部署kubelet時,通過設定引數--cpucfs-quota=false來關閉該限制:如果叢集管理員希望對整個叢集中容器或者Pod配置的Requests和Limits做限制,那麼可以通過配置Kubernetes名稱空間中的LimitRange來達到該目的。在Kubernetes叢集中,如果Pod沒有顯式定義Limits和Requests,那麼Kubernetes系統會將該Pod所在的名稱空間中定義的LimitRange的default和default Requests配置到該Pod上。
四 資源服務質量管理(Resource QoS)
4.1 服務資源質量
Kubernetes會根據Pod的Requests和Limits配置來實現針對Pod的不同級別的資源服務質量控制(QoS)。在Kubernetes的資源QoS體系中,需要保證高可靠性的Pod可以申請可靠資源,而一些不需要高可靠性的Pod可以申請可靠性較低或者不可靠的資源。 容器的資源配置分為Requests和Limits,其中Requests是Kubernetes排程時能為容器提供的完全可保障的資源量(最低保障),而Limits是系統允許容器執行時可能使用的資源量的上限(最高上限)。Pod級別的資源配置是通過計算Pod內所有容器的資源配置的總和得出來的。 Kubernetes中Pod的Requests和Limits資源配置有如下特點:- 如果Pod配置的Requests值等於Limits值,那麼該Pod可以獲得的資源是完全可靠的。
- 如果Pod的Requests值小於Limits值,那麼該Pod獲得的資源可分成兩部分:
- 完全可靠的資源,資源量的大小等於Requests值;
- 不可靠的資源,資源量最大等於Limits與Requests的差額,這份不可靠的資源能夠申請到多少,取決於當時主機上容器可用資源的餘量。
4.2 Requests和Limits限制機制
容器的資源配置滿足以下兩個條件:- Requests <= 節點可用資源
- Requests <= Limits
- 可壓縮資源
- 不可壓縮資源
4.3 對排程策略的影響
Kubernetes的kubelet通過計算Pod中所有容器的Requests的總和來決定對Pod的排程。 不管是CPU還是記憶體,Kubernetes排程器和kubelet都會確保節點上所有Pod的Requests的總和不會超過在該節點上可分配給容器使用的資源容量上限。4.4 服務質量等級(QoSClasses)
在一個超用(Over Committed,容器Limits總和大於系統容量上限)系統中,由於容器負載的波動可能導致作業系統的資源不足,最終可能導致部分容器被殺掉。在這種情況下,理想是優先殺掉那些不太重要的容器。Kubernetes將容器劃分成3個QoS等級來衡量重要程度: Guaranteed(完全可靠的)、Burstable(彈性波動、較可靠的)和BestEffort(盡力而為、不太可靠的),這三種優先順序依次遞減。 QoS等級和優先順序的關係從理論上來說,QoS級別應該作為一個單獨的引數來提供API,並由使用者對Pod進行配置,這種配置應該與Requests和Limits無關。但在當前版本的Kubernetes的設計中,為了簡化模式及避免引入太多的複雜性,QoS級別直接由Requests和Limits來定義。在Kubernetes中容器的QoS級別等於容器所在Pod的QoS級別,而Kubernetes的資源配置定義了Pod的如上三種QoS級別。- Guaranteed
1 containers: 2 name: foo 3 resources: 4 limits: 5 cpu: 10m 6 memory: 1Gi 7 name: bar 8 resources: 9 limits: 10 cpu: 100m 11 memory: 100Mi 12解讀:如上未定義Requests值,所以其預設等於Limits值。 示例2:
1 containers: 2 name: foo 3 resources: 4 requests: 5 cpu: 10m 6 memory: 1Gi 7 limits: 8 cpu: 10m 9 memory: 1Gi 10 name: bar 11 resources: 12 requests: 13 cpu: 10m 14 memory: 1Gi 15 limits: 16 cpu: 100m 17 memory: 100Mi 18解讀:該定義的Requests和Limits的值完全相同。
- BestEffort
1 containers: 2 name: foo 3 resources: 4 name: bar 5 resources: 6解讀:該容器都未定義資源配置。
- Burstable
- 第1種情況:Pod中的一部分容器在一種或多種資源型別的資源配置中定義了Requests值和Limits值(都不為0),且Requests值小於Limits值;
- 第2種情況:Pod中的一部分容器未定義資源配置(Requests和Limits都未定義)。
1 containers: 2 name: foo 3 resources: 4 requests: 5 cpu: 5m 6 memory: 1Gi 7 limits: 8 cpu: 10m 9 memory: 1Gi 10 name: bar 11 resources: 12 requests: 13 cpu: 5m 14 memory: 1Gi 15 limits: 16 cpu: 100m 17 memory: 100Mi 18示例5:容器bar未定義資源配置而容器foo定義了資源配置。
1 containers: 2 name: foo 3 resources: 4 requests: 5 cpu: 10m 6 memory: 1Gi 7 limits: 8 cpu: 10m 9 memory: 1Gi 10 name: bar 11示例6:容器foo未定義CPU,而容器bar未定義記憶體。
1 containers: 2 name: foo 3 resources: 4 limits: 5 memory: 1Gi 6 name: bar 7 resources: 8 limits: 9 cpu: 100m 10示例7:容器bar未定義資源配置,而容器foo未定義Limits值。 containers: name: foo resources: requests: cpu: 5m memory: 1Gi name: bar
4.5 Kubernetes QoS的工作特點
Pod的CPU Requests無法得到滿足(比如節點的系統級任務佔用過多的CPU導致無法分配足夠的CPU給容器使用)時,容器得到的CPU會被壓縮限流。由於記憶體是不可壓縮的資源,所以針對記憶體資源緊缺的情況,會按照以下邏輯進行處理。- BestEffort Pod的優先順序最低,在這類Pod中執行的程序會在系統記憶體緊缺時被第一優先殺掉。當然,從另外一個角度來看,BestEffort Pod由於沒有設定資源Limits,所以在資源充足時,它們可以充分使用所有的閒置資源。
- Burstable Pod的優先順序居中,這類Pod初始時會分配較少的可靠資源,但可以按需申請更多的資源。當然,如果整個系統記憶體緊缺,又沒有BestEffort容器可以被殺掉以釋放資源,那麼這類Pod中的程序可能會被殺掉。
- Guaranteed Pod的優先順序最高,而且一般情況下這類Pod只要不超過其資源Limits的限制就不會被殺掉。當然,如果整個系統記憶體緊缺,又沒有其他更低優先順序的容器可以被殺掉以釋放資源,那麼這類Pod中的程序也可能會被殺掉。