1. 程式人生 > 其它 >k8s資源需求和限制, 以及pod驅逐策略

k8s資源需求和限制, 以及pod驅逐策略

容器的資源需求和資源限制

  • requests:需求,最低保障, 保證被排程的節點上至少有的資源配額
  • limits:限制,硬限制, 容器可以分配到的最大資源配額

apiVersion: v1
kind: Pod
metadata:
    name: pod-demo
    labels:
        app: myapp
        tier: fronted
spec:
    containers:
    - name: myapp
      image: ikubernetes/stress-ng
      command: ["/usr/bin/stress-ng", "-m 1", "-c 1", "--metrics-brief"]
      resources:
        requests:
          cpu: "200m"
          memory: "128Mi"
        limits:
          cpu: "500m"
          memory: "200Mi"
kubectl exec pod-demo -- top

這是一個CPU為2核的節點, 分配給容器500m的CPU, 也就是0.5個CPU, 所以看到的程序CPU佔用率約為26%

QoS Classes分類

Guaranteed

如果Pod中所有Container的所有Resource的limitrequest都相等且不為0,則這個Pod的QoS Class就是Guaranteed。

注意,如果一個容器只指明瞭limit,而未指明request,則表明request的值等於limit的值。

containers:
    name: foo
        resources:
            limits:
                cpu: 10m
                memory: 1Gi
    name: bar
        resources:
            limits:
                cpu: 100m
                memory: 100Mi
            requests:
                cpu: 100m
                memory: 100Mi

Burstable

至少有一個容器設定CPU或記憶體資源的requests屬性

Best-Effort

如果Pod中所有容器的所有Resource的request和limit都沒有賦值,則這個Pod的QoS Class就是Best-Effort.

containers:
    name: foo
        resources:
    name: bar
        resources:

kubernetes之node資源緊缺時pod驅逐機制

Qos Class優先順序排名

Guaranteed > Burstable > Best-Effort

可壓縮資源與不可壓縮資源

Pod 使用的資源最重要的是 CPU、記憶體和磁碟 IO,這些資源可以被分為可壓縮資源(CPU)不可壓縮資源(記憶體,磁碟 IO)

  • 可壓縮資源(CPU)不會導致pod被驅逐

    因為當 Pod 的 CPU 使用量很多時,系統可以通過重新分配權重來限制 Pod 的 CPU 使用

  • 不可壓縮資源(記憶體)則會導致pod被驅逐

    於不可壓縮資源來說,如果資源不足,也就無法繼續申請資源(記憶體用完就是用完了),此時 Kubernetes 會從該節點上驅逐一定數量的 Pod,以保證該節點上有充足的資源。

儲存資源不足

下面是 kubelet 預設的關於節點儲存的驅逐觸發條件:

  • nodefs.available<10%(容器 volume 使用的檔案系統的可用空間,包括檔案系統剩餘大小和 inode 數量)
  • imagefs.available<15%(容器映象使用的檔案系統的可用空間,包括檔案系統剩餘大小和 inode 數量)

imagefs 使用量達到閾值時,kubelet 會嘗試刪除不使用的映象來清理磁碟空間。

nodefs 使用量達到閾值時,kubelet 就會拒絕在該節點上執行新 Pod,並向 API Server 註冊一個 DiskPressure condition。然後 kubelet 會嘗試刪除死亡的 Pod 和容器來回收磁碟空間,如果此時 nodefs 使用量仍然沒有低於閾值,kubelet 就會開始驅逐 Pod。kubelet 驅逐 Pod 的過程中不會參考 Pod 的 QoS,只是根據 Pod 的 nodefs 使用量來進行排名,並選取使用量最多的 Pod 進行驅逐。所以即使 QoS 等級為 Guaranteed 的 Pod 在這個階段也有可能被驅逐(例如 nodefs 使用量最大)。如果驅逐的是 Daemonset,kubelet 會阻止該 Pod 重啟,直到 nodefs 可用量超過閾值。

如果一個 Pod 中有多個容器,kubelet 會根據 Pod 中所有容器的 nodefs 使用量之和來進行排名。即所有容器的 container_fs_usage_bytes 指標值之和。

舉例

Pod NamePod QoSnodefs usage
ABest Effort800M
BGuaranteed1.3G
CBurstable1.2G
DBurstable700M
EBest Effort500M
FGuaranteed1G

當 nodefs 的使用量超過閾值時,kubelet 會根據 Pod 的 nodefs 使用量來對 Pod 進行排名,首先驅逐使用量最多的 Pod。排名如下圖所示:

Pod NamePod QoSnodefs usage
BGuaranteed1.3G
CBurstable1.2G
FGuaranteed1G
ABest Effort800M
DBurstable700M
EBest Effort500M

記憶體資源不足

下面是 kubelet 預設的關於節點記憶體資源的驅逐觸發條件:

  • memory.available<100Mi

當記憶體使用量超過閾值時,kubelet 就會向 API Server 註冊一個 MemoryPressure condition,此時 kubelet 不會接受新的 QoS 等級為 Best Effort 的 Pod 在該節點上執行,並按照以下順序來驅逐 Pod:

  • Pod 的記憶體使用量是否超過了 request 指定的值
  • 根據 priority 排序,優先順序低的 Pod 最先被驅逐
  • 比較它們的記憶體使用量與 request 指定的值之差。

按照這個順序,可以確保 QoS 等級為 Guaranteed 的 Pod 不會在 QoS 等級為 Best Effort 的 Pod 之前被驅逐,但不能保證它不會在 QoS 等級為 Burstable 的 Pod 之前被驅逐。

如果一個 Pod 中有多個容器,kubelet 會根據 Pod 中所有容器相對於 request 的記憶體使用量與之和來進行排名。即所有容器的 (container_memory_usage_bytes 指標值與 container_resource_requests_memory_bytes 指標值的差)之和。

舉例

Pod NamePod QoSMemory requestedMemory limitsMemory usage
ABest Effort00700M
BGuaranteed2Gi2Gi1.9G
CBurstable1Gi2Gi1.8G
DBurstable1Gi2Gi800M
EBest Effort00300M
FGuaranteed2Gi2Gi1G

當節點的記憶體使用量超過閾值時,kubelet 會根據 Pod 相對於 request 的記憶體使用量來對 Pod 進行排名。排名如下所示:

Pod NamePod QoSMemory requestedMemory limitsMemory usage記憶體相對使用量
CBurstable1Gi2Gi1.8G800M
ABest Effort00700M700M
EBest Effort00300M300M
BGuaranteed2Gi2Gi1.9G-100M
DBurstable1Gi2Gi800M-200M
FGuaranteed2Gi2Gi1G-1G

當記憶體資源不足時,kubelet 在驅逐 Pod 時只會考慮 requests 和 Pod 的記憶體使用量,不會考慮 limits。

Node OOM (Out Of Memory)

因為 kubelet 預設每 10 秒抓取一次 cAdvisor 的監控資料,所以有可能在 kubelet 驅逐 Pod 回收記憶體之前發生記憶體使用量激增的情況,這時就有可能觸發核心 OOM killer。這時刪除容器的權利就由kubelet 轉交到核心 OOM killer 手裡,但 kubelet 仍然會起到一定的決定作用,它會根據 Pod 的 QoS 來設定其 oom_score_adj 值:

QoSoom_score_adj
Guaranteed-998
Burstablemin(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)
pod-infra-container-998
kubelet, docker daemon, systemd service-999

如果該節點在 kubelet 通過驅逐 Pod 回收記憶體之前觸發了 OOM 事件,OOM killer 就會採取行動來降低系統的壓力,它會根據下面的公式來計算 oom_score 的值:

容器使用的記憶體佔系統記憶體的百分比 + oom_score_adj = oom_score>

OOM killer 會殺掉 oom_score_adj 值最高的容器,如果有多個容器的 oom_score_adj 值相同,就會殺掉記憶體使用量最多的容器(其實是因為記憶體使用量最多的容器的 oom_score 值最高)。關於 OOM 的更多內容請參考:Kubernetes 記憶體資源限制實戰

假設某節點執行著 4 個 Pod,且每個 Pod 中只有一個容器。每個 QoS 型別為 Burstable 的 Pod 配置的記憶體 requests 是 4Gi,節點的記憶體大小為 30Gi。每個 Pod 的 oom_score_adj 值如下所示:

Pod NamePod QoSoom_score_adj
ABest Effort1000
BGuaranteed-998
CBurstable867(根據上面的公式計算)
DBest Effort1000

當呼叫 OOM killer 時,它首先選擇 oom_score_adj 值最高的容器(1000),這裡有兩個容器的 oom_score_adj 值都是 1000,OOM killer 最終會選擇記憶體使用量最多的容器。

總結

  • 因為 kubelet 預設每 10 秒抓取一次 cAdvisor 的監控資料,所以可能在資源使用量低於閾值時,kubelet 仍然在驅逐 Pod。
  • kubelet 將 Pod 從節點上驅逐之後,Kubernetes 會將該 Pod 重新排程到另一個資源充足的節點上。但有時候 Scheduler 會將該 Pod 重新排程到與之前相同的節點上,比如設定了節點親和性,或者該 Pod 以 Daemonset 的形式執行。