kubernetes之資源限制及QOS服務質量

2022-07-29 18:01:22

1.什麼是資源限制?

1.1在kubernetes叢集中,為了使得系統能夠穩定的執行,通常會對Pod的資源使用量進行限制。在kubernetes叢集中,如果有一個程式出現異常,並且佔用大量的系統資源,如果沒有對該Pod進行資源限制的話,可能會影響其他的Pod正常執行,從而造成業務的不穩定性。

1.2正常情況下我們在一個宿主機的核心之上,這個宿主機可能管理了一組硬體包括CPU、記憶體。而後在這個宿主機的核心之上,我們可以執行多個容器,這些容器將共用底層的同一個核心,很顯然,硬體是屬於核心的,因此任何容器中的每一個程序預設可以請求佔有這個核心管理的所有的可用硬體資源。
1.3尤其是多租戶的環境當中,有人惡意的執行了一個容器,這個容器可能會大量的佔用CPU和記憶體,進而會導致其他容器無法執行,Docker容器啟動的時候預設是在核心的名稱空間級別進行了隔離,在程序所執行的資源範圍上並沒有做太多的隔離操作。我們應該為每個應用設定內部程序執行最為合理的資源的最小保證量和最大保證量。
1.4官方檔案:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/

2.如何實現資源限制?

  • kubernetes通過Requests和Limits欄位來實現對Pod的資源限制;
  • Requests: 啟動Pod時申請分配的資源大小,即容器執行可能用不到這些額度的資源,但用到時必須確保有這麼多的資源使用;(Pod在排程的時候requests比較重要)
  • Limits: 限制Pod執行最大的可用的資源大小,即硬限制;(Pod在執行時limits比較重要)
  • 相比較來說,CPU屬於可壓縮(compressible)資源,即資源額度可按需收縮,而記憶體則是不可壓縮型資源,對其執行收縮操作可能會導致某種程度上的問題,如果超載,所有Pod記憶體加起來超過節點就會觸發OOM機制,殺死Pod,具體殺死哪個Pod,就要看QOS服務質量。優先把評分低的殺掉;
  • 正常情況下,Resources可以定義在Pod上也可以定義在Containers上。定義在Pod上是對Pod上所有容器都生效。一般而言我們定義資源是明確定義在每個容器上的,因為不同容器之間可能需求是不一樣的。
spec.containers[].resources.request.cpu     # Pod申請時的CPU,如果節點沒有足夠大,則Pod排程失敗
spec.containers[].resources.request.memory  # Pod申請時的記憶體
spec.containers[].resource.limits.cpu       # Pod最大可使用的CPU
spec.containers[].resource.limits.memory    # Pod最大可使用的記憶體

3.資源限制的目的

3.1我們為什麼要進行資源限制?

  • CPU: 為叢集中執行的容器設定CPU請求和限制,可以有效的利用叢集上可用的CPU資源;
    設定Pod CPU請求,設定在較低的數值,可以使得Pod更有機會被排程節點;
    通過設定CPU限制大於CPU請求,可以完成兩件事:
    1.當Pod碰到一些突發負載時,它可以合理利用可用的CPU資源。
    2.當Pod在突發流量期間,可使用的CPU被限制為合理的值,從而可用避免影響其他Pod的正常執行;
  • memory: 為叢集中執行的容器設定記憶體請求和限制,可以有效利用叢集節點上的可以的記憶體資源;
    通過將Pod的記憶體請求設定在較低的數值,可以有效的利用節點上可用的記憶體資源。通過讓記憶體限制大於記憶體請求,可以完成如下:
    1.當Pod碰到突發請求,可以更好的利用其主機上的可用記憶體;
    2.當Pod在突發負載期間可使用的記憶體被限制為合理的數值,從而可用避免影響其他Pod的執行;

4.資源限制單位

4.1CPU限制單位
1核CPU=1000毫核,當定義容器為0.5時,所需要的CPU資源是1核心CPU的一般,對於CPU單位,表示式0.1等價於表示式100m,可以看作是100millicpu;

1核=1000millicpu (1 Core=1000m)
0.5核=500millicpu  (0.5 Core=500m)

4.2記憶體限制單位
記憶體的基本單位是位元組數(Bytes),也可以使用E、P、T、G、M、K作為單位字尾,或Ei、Pi、Ti、Gi、Mi和Ki形式單位字尾;

1MB=1000KB=1000000Bytes
1Mi=1024KB=1048576Bytes

5.資源限制設定案例

5.1CPU設定;

5.1.1設定容器的CPU請求與限制;

需安裝Metrics-Server才能用top命令否則無法使用;
# 建立一個具有一個容器的Pod,容器將請求0.5個CPU,最多限制1個CPU;
root@kubernetes-master01:~# cat cpu-requests-limits.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: cpu-test-cpu-limits
spec:
  containers:
  - name: cpu-test-stress
    image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest
    args:
    - -cpus         # 容器嘗試使用2核CPU
    - "2"
    resources:
       requests:  #  限制Pod最多可申請0.5核CPU
         cpu: "500m"  
       limits:     # 限制Pod最大可使用1核CPU
         cpu: "1000m"
root@kubernetes-master01:~# kubectl apply -f cpu-requests-limits.yaml 
pod/cpu-test-cpu-limits created

# 檢視資源限制情況,容器設定去嘗試使用2個CPU,但是容器只能被允許使用1個CPU;
root@kubernetes-master01:~# kubectl top pods cpu-test-cpu-limits
NAME                  CPU(cores)   MEMORY(bytes)   
cpu-test-cpu-limits   999m        1Mi    

5.1.2建立一個Pod,設定該Pod中容器請求為100核,這個值會大於叢集中所有記憶體的總和;

root@kubernetes-master01:~# cat cpu-requests-limits.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: cpu-test-cpu-limits
spec:
  containers:
  - name: cpu-demo-ctr
    image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest
    args:
    - -cpus
    - "2"
    resources:
       requests:
         cpu: "100"   # 設定Pod申請100GB,我們叢集上沒有100GB的資源
       limits:
         cpu: "100"   # 最大使用100GB,叢集資源不足,無法建立;
root@kubernetes-master01:~# kubectl apply -f cpu-requests-limits.yaml 
pod/cpu-test-cpu-limits created

# 檢視Pod的狀態,為Pending,Pod未被排程到任何節點上;
root@kubernetes-master01:~# kubectl get pods
cpu-test-cpu-limits                          0/1     Pending   0          4s

# 檢視Events,輸出顯示由於3節點的CPU資源不足,無法進行排程;
root@kubernetes-master01:~# kubectl describe pods cpu-test-cpu-limits
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  113s  default-scheduler  0/3 nodes are available: 3 Insufficient cpu.
  Warning  FailedScheduling  113s  default-scheduler  0/3 nodes are available: 3 Insufficient cpu.

5.1.3如果不指定CPU的Limits?
如果沒有為容器設定CPU限制,那麼容器在可以使用的CPU資源是沒有上限的,因而可以使用所在節點上的所有可用CPU資源,這樣會造成某一個Pod佔用了大量的CPU,可能會影響其他Pod的正常允許,從而造成業務的不穩定性。

5.2記憶體設定;

5.2.1設定容器的記憶體請求與限制;
建立一個1個容器Pod,容器會將請求100MB記憶體,並且最大可用記憶體為200MB以內;

root@kubernetes-master01:~# cat memory-requests-limits.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-memory-resource
spec:
  containers:
  - name: momory-demo-stress
    image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1" ]
    resources:
      requests:
        memory: "100Mi"   # 設定最小請求為100Mi
      limits:
        memory: "200Mi"   # 最大可使用200Mi
root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml 
pod/pod-memory-resource created

# 獲取Pod的記憶體使用資訊,輸出結果顯示Pod正在使用的記憶體約為150Mi,大於Pod請求的100Mi,但是在Pod限制的200Mi之內。
root@kubernetes-master01:~# kubectl top po pod-memory-resource
NAME                  CPU(cores)   MEMORY(bytes)   
pod-memory-resource   43m          151Mi 

# 也可用通過Yaml檔案的方式來看limits最大可使用為200Mi
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi

5.2.2執行超過容器記憶體限制的應用
當節點擁有足夠的可用記憶體時,容器可用使用其請求的記憶體,但是,容器不允許使用超過其限制的記憶體。如果容器分配的記憶體超過其限制,該容器會成為被終止的候選容器。如果容器繼續消耗超過其限制的記憶體,則終止容器。如果終止的容器可以被重啟,則kubelet會重新啟動它。

5.2.2.1建立一個Pod,擁有一個Containers,該容器的記憶體請求為100Mi,記憶體限制為200Mi,嘗試分配超出其限制的記憶體;

root@kubernetes-master01:~# cat memory-requests-limits.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-memory-resource
spec:
  containers:
  - name: momory-demo-ctr
    image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1" ]  # 強行分配250M
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"  # 我們上限不能超過200,超過200Mi會發生OOM Kill
root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml

# 檢視Pod,此時容器被殺死;
root@kubernetes-master01:~# kubectl get pods -w
pod-memory-resource                          0/1     OOMKilled           0          9s
pod-memory-resource                          0/1     OOMKilled           1          11s
pod-memory-resource                          0/1     CrashLoopBackOff    1          12s

# 檢視更詳細的資訊,顯示為OOMKilled;
root@kubernetes-master01:~#kubectl get  pod pod-memory-resource -o yaml
 lastState:
      terminated:
        containerID: docker://3d6e53a12a101f474d24c00b8e9c5b1e6da2ef26735b6e2cb6790184c6d69cfa
        exitCode: 1
        finishedAt: "2022-07-27T06:40:07Z"
        reason: OOMKilled

5.2.3超過節點的記憶體分配
Pod的排程基於請求,只有當前節點擁有足夠滿足Pod記憶體請求的記憶體時,才會將Pod排程至該節點執行;
5.2.3.1建立一個Pod,其擁有一個請求1000GB記憶體的容器,超出了叢集任何一個節點所擁有的記憶體;

root@kubernetes-master01:~/cloud-Native/resource# cat memory-requests-limits.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-memory-resource
spec:
  containers:
  - name: momory-demo-ctr
    image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1" ]
    resources:
      requests:
        memory: "100Gi"
      limits:
        memory: "200Gi"
root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml

# 檢視Pod狀態,發現處於pending,這意味著該Pod未被排程至任何節點;
root@kubernetes-master01:~# kubectl get pods -w
pod-memory-resource                          0/1     Pending   0          6s

# 通過describe檢視更詳細資訊;顯示為由於節點記憶體不足,該容器無法被排程;
root@kubernetes-master01:~# kubectl describe pods pod-memory-resource
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  2m21s  default-scheduler  0/3 nodes are available: 3 Insufficient memory.
  Warning  FailedScheduling  2m20s  default-scheduler  0/3 nodes are available: 3 Insufficient memory.

5.2.4如果沒有指定記憶體限制;
如果為容器指定記憶體限制,容器可以無限的使用其所在節點的所有可用記憶體,進而可能導致該節點呼叫OOM Killer。此外如果發生OOM Kill,沒有設定資源限制的容器將被殺死的可能性會更大。

6.QOS服務質量

6.1什麼是QOS?

kubernetes允許節點資源對limits的過載使用,這意味著節點無法同時滿足其上的所有Pod物件以資源滿載的方式執行,於是在記憶體資源緊缺時,應該以何種次序先後終止哪些Pod物件?
kubernetes無法自行對此做出決策,它需要藉助於Pod物件的優先順序判定,根據Pod物件的Requests和Limits屬性,kubernetes將Pod物件歸類到BestEffort、Burstable和Guaranteed三個服務質量(Quality of Service,QOS);
6.1.1Guaranteed: Pod物件為每個容器都設定了CPU資源需求和資源限制,且兩者的值相同,還同時為每個容器設定了記憶體需求與記憶體限制,並且兩者的值相同,這類Pod物件具有最高階別服務質量。
6.1.2Burstable: 至少有一個容器設定了CPU或記憶體資源Requests屬性,但不滿足Guaranteed,這類Pod具有中級服務質量;
6.1.3BestEffort: 沒有為任何容器設定Requests和Limits屬性,這類Pod物件服務質量是最低階別。

1.當kubernetes叢集記憶體資源緊缺,優先殺死BestEffort類別的容器,因為系統不為該類資源提供任何服務保證,但是此類資源最大的好處就是能夠儘可能的使用資源。
2.如果系統中沒有BestEffort類別的容器,接下來就輪到Burstable類別的容器,如果有多個Burstable類別的容器,就看誰的資源佔用多就優先殺死誰。對於Guaranteed類別的容器擁有最高優先順序,他們不會被殺死,除非其記憶體需求超限,或OMM時沒有其他更低優先順序的Pod物件存在,才幹掉Guaranteed類容器;