本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 PriorityClass 保護您的任務關鍵型 Pod 免於驅逐

Pod 優先順序和搶佔有助於確保在資源吃緊時,任務關鍵型 Pod 能夠正常運作,方法是決定排程和驅逐的順序。

Kubernetes 已被廣泛採用,許多組織將其用作事實上的協調引擎,以執行需要頻繁建立和刪除的工作負載。

因此,適當的 Pod 排程是確保應用程式 Pod 在 Kubernetes 叢集中正常運作且沒有任何問題的關鍵。本文深入探討資源管理的使用案例,方法是利用 PriorityClass 物件來保護任務關鍵型或高優先順序的 Pod 免於被驅逐,並確保應用程式 Pod 正常運作、執行並提供流量服務。

Kubernetes 中的資源管理

控制平面由多個組件組成,其中排程器(通常是內建的 kube-scheduler)是負責將節點指派給 Pod 的組件之一。

每當建立 Pod 時,它會進入「擱置中」狀態,之後排程器會判斷哪個節點最適合放置新的 Pod。

在背景中,排程器以無限迴圈的方式執行,尋找未設定 nodeName準備好進行排程 的 Pod。對於每個需要排程的 Pod,排程器會嘗試決定哪個節點應該執行該 Pod。

如果排程器找不到任何節點,Pod 將保持擱置中狀態,這並非理想情況。

下圖從第 1 點到第 4 點,說明了請求流程

A diagram showing the scheduling of three Pods that a client has directly created.

Kubernetes 中的排程

典型使用案例

以下是一些實際情境,可能需要控制 Pod 的排程和驅逐。

  1. 假設您計劃部署的 Pod 至關重要,而且您有一些資源限制。基礎架構組件(如 Grafana Loki)的 DaemonSet 就是一個例子。Loki Pod 必須在每個節點上的其他 Pod 之前執行。在這種情況下,您可以透過手動識別和刪除不需要的 Pod,或透過在叢集中新增節點來確保資源可用性。這兩種方法都不合適,因為前者執行起來會很繁瑣,而後者可能會涉及時間和金錢的支出。

  2. 另一個使用案例可能是一個單一叢集,其中包含以下具有相關優先順序的環境的 Pod

    • 生產環境 (prod):最高優先順序
    • 預生產環境 (preprod):中等優先順序
    • 開發環境 (dev):最低優先順序

    在叢集中資源消耗量高的情況下,節點上的 CPU 和記憶體資源會發生競爭。雖然叢集層級的自動擴充可能會新增更多節點,但這需要時間。在此期間,如果沒有更多節點可以擴充叢集,則某些 Pod 可能會保持在擱置中狀態,或者服務可能會因爭奪資源而降級。如果 kubelet 從節點驅逐 Pod,則驅逐將是隨機的,因為 kubelet 沒有關於要驅逐哪些 Pod 以及保留哪些 Pod 的任何特殊資訊。

  3. 第三個例子可能是一個微服務,由佇列應用程式或資料庫支援,遇到資源吃緊,導致佇列或資料庫被驅逐。在這種情況下,所有其他服務都將變得無用,直到資料庫可以再次提供流量服務。

在其他情境中,您可能也希望控制 Pod 的排程順序或驅逐順序。

Kubernetes 中的 PriorityClass

PriorityClass 是 Kubernetes 中叢集範圍的 API 物件,並且是 scheduling.k8s.io/v1 API 群組的一部分。它包含 PriorityClass 名稱(在 .metadata.name 中定義)和整數值(在 .value 中定義)的對應。這表示排程器用來決定 Pod 相對優先順序的值。

此外,當您使用 kubeadm 或託管 Kubernetes 服務(例如 Azure Kubernetes Service)建立叢集時,Kubernetes 會使用 PriorityClass 來保護託管在控制平面節點上的 Pod。這確保了即使資源受到限制,CoreDNS 和 kube-proxy 等關鍵叢集組件也可以執行。

Pod 的這種可用性是透過使用特殊的 PriorityClass 來實現的,該 PriorityClass 確保 Pod 正常運作,並且整體叢集不受影響。

$ kubectl get priorityclass
NAME                      VALUE        GLOBAL-DEFAULT   AGE
system-cluster-critical   2000000000   false            82m
system-node-critical      2000001000   false            82m

下圖準確地顯示了它的運作方式,並透過一個範例來說明,該範例將在後續章節中詳細介紹。

A flow chart that illustrates how the kube-scheduler prioritizes new Pods and potentially preempts existing Pods

Pod 排程和搶佔

Pod 優先順序和搶佔

Pod 搶佔 是 Kubernetes 的一項功能,允許叢集根據優先順序搶佔 Pod(移除現有的 Pod 以支持新的 Pod)。Pod 優先順序 指出 Pod 相對於其他 Pod 在排程時的重要性。如果沒有足夠的資源來執行所有目前的 Pod,排程器會嘗試驅逐低優先順序的 Pod,而不是高優先順序的 Pod。

此外,當健康的叢集遇到節點故障時,通常會搶佔低優先順序的 Pod,以便在可用節點上為高優先順序的 Pod 騰出空間。即使叢集可以自動啟動新節點,也會發生這種情況,因為 Pod 建立通常比啟動新節點快得多。

PriorityClass 需求

在設定 PriorityClass 之前,有幾件事需要考慮。

  1. 決定需要哪些 PriorityClass。例如,根據環境、Pod 類型、應用程式類型等。
  2. 叢集的預設 PriorityClass 資源。沒有 priorityClassName 的 Pod 將被視為優先順序 0。
  3. 對所有 PriorityClass 使用一致的命名慣例。
  4. 確保工作負載的 Pod 以正確的 PriorityClass 執行。

PriorityClass 實作範例

假設有 3 個應用程式 Pod:一個用於生產環境、一個用於預生產環境,一個用於開發環境。以下是每個環境的三個範例 YAML manifest 檔案。

---
# development
apiVersion: v1
kind: Pod
metadata:
  name: dev-nginx
  labels:
    env: dev
spec:
  containers:
  - name: dev-nginx
    image: nginx
    resources:
      requests:
        memory: "256Mi"
        cpu: "0.2"
      limits:
        memory: ".5Gi"
        cpu: "0.5"
---
# preproduction
apiVersion: v1
kind: Pod
metadata:
  name: preprod-nginx
  labels:
    env: preprod
spec:
  containers:
  - name: preprod-nginx
    image: nginx
    resources:
      requests:
        memory: "1.5Gi"
        cpu: "1.5"
      limits:
        memory: "2Gi"
        cpu: "2"
---
# production
apiVersion: v1
kind: Pod
metadata:
  name: prod-nginx
  labels:
    env: prod
spec:
  containers:
  - name: prod-nginx
    image: nginx
    resources:
      requests:
        memory: "2Gi"
        cpu: "2"
      limits:
        memory: "2Gi"
        cpu: "2"

您可以使用 kubectl create -f <FILE.yaml> 命令建立這些 Pod,然後使用 kubectl get pods 命令檢查其狀態。您可以查看它們是否已啟動並準備好提供流量服務

$ kubectl get pods --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
dev-nginx       1/1     Running   0          55s   env=dev
preprod-nginx   1/1     Running   0          55s   env=preprod
prod-nginx      0/1     Pending   0          55s   env=prod

壞消息。生產環境的 Pod 仍然處於擱置中狀態,並且沒有提供任何流量服務。

讓我們看看為何會發生這種情況

$ kubectl get events
...
...
5s          Warning   FailedScheduling   pod/prod-nginx      0/2 nodes are available: 1 Insufficient cpu, 2 Insufficient memory.

在本範例中,只有一個工作節點,而且該節點存在資源吃緊的情況。

現在,讓我們看看 PriorityClass 如何在此情況下提供協助,因為應該給予生產環境比其他環境更高的優先順序。

PriorityClass API

在根據這些需求建立 PriorityClass 之前,讓我們先看看 PriorityClass 的基本 manifest 看起來如何,並概述一些先決條件

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: PRIORITYCLASS_NAME
value: 0 # any integer value between -1000000000 to 1000000000 
description: >-
  (Optional) description goes here!  
globalDefault: false # or true. Only one PriorityClass can be the global default.

以下是 PriorityClass 的一些先決條件

  • PriorityClass 的名稱必須是有效的 DNS 子網域名稱。
  • 當您建立自己的 PriorityClass 時,名稱不應以 system- 開頭,因為這些名稱由 Kubernetes 本身保留(例如,它們用於兩個內建的 PriorityClass)。
  • 其絕對值應介於 -1000000000 到 1000000000(10 億)之間。
  • 較大的數字由 PriorityClass 保留,例如 system-cluster-critical(此 Pod 對叢集至關重要)和 system-node-critical(節點嚴重依賴此 Pod)。system-node-critical 的優先順序高於 system-cluster-critical,因為叢集關鍵型 Pod 只有在其執行的節點滿足所有節點層級的關鍵需求時才能良好運作。
  • 有兩個選填欄位
    • globalDefault:當為 true 時,此 PriorityClass 用於未指定 priorityClassName 的 Pod。叢集中只能存在一個 globalDefault 設定為 true 的 PriorityClass。
      如果沒有 PriorityClass 將 globalDefault 設定為 true,則所有未定義 priorityClassName 的 Pod 都將以 0 優先順序(即最低優先順序)處理。
    • description:具有有意義值的字串,以便人們知道何時使用此 PriorityClass。

PriorityClass 實際運作

這是一個範例。接下來,建立一些環境特定的 PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: dev-pc
value: 1000000
globalDefault: false
description: >-
  (Optional) This priority class should only be used for all development pods.  
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: preprod-pc
value: 2000000
globalDefault: false
description: >-
  (Optional) This priority class should only be used for all preprod pods.  
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: prod-pc
value: 4000000
globalDefault: false
description: >-
  (Optional) This priority class should only be used for all prod pods.  

使用 kubectl create -f <FILE.YAML> 命令建立 pc,並使用 kubectl get pc 檢查其狀態。

$ kubectl get pc
NAME                      VALUE        GLOBAL-DEFAULT   AGE
dev-pc                    1000000      false            3m13s
preprod-pc                2000000      false            2m3s
prod-pc                   4000000      false            7s
system-cluster-critical   2000000000   false            82m
system-node-critical      2000001000   false            82m

新的 PriorityClass 現在已就位。Pod manifest 或 Pod 範本(在 ReplicaSet 或 Deployment 中)需要進行少量變更。換句話說,您需要在 .spec.priorityClassName(這是字串值)中指定優先順序類別名稱。

首先更新先前的生產環境 Pod manifest 檔案以指派 PriorityClass,然後刪除生產環境 Pod 並重新建立它。您無法編輯已存在 Pod 的優先順序類別。

在我的叢集中,當我嘗試這樣做時,發生了以下情況。首先,該變更似乎成功了;Pod 的狀態已更新

$ kubectl get pods --show-labels
NAME            READY   STATUS    	RESTARTS   AGE   LABELS
dev-nginx       1/1     Terminating	0          55s   env=dev
preprod-nginx   1/1     Running   	0          55s   env=preprod
prod-nginx      0/1     Pending   	0          55s   env=prod

dev-nginx Pod 正在終止。一旦成功終止,並且有足夠的資源可供生產環境 Pod 使用,控制平面就可以排程生產環境 Pod

Warning   FailedScheduling   pod/prod-nginx    0/2 nodes are available: 1 Insufficient cpu, 2 Insufficient memory.
Normal    Preempted          pod/dev-nginx     by default/prod-nginx on node node01
Normal    Killing            pod/dev-nginx     Stopping container dev-nginx
Normal    Scheduled          pod/prod-nginx    Successfully assigned default/prod-nginx to node01
Normal    Pulling            pod/prod-nginx    Pulling image "nginx"
Normal    Pulled             pod/prod-nginx    Successfully pulled image "nginx"
Normal    Created            pod/prod-nginx    Created container prod-nginx
Normal    Started            pod/prod-nginx    Started container prod-nginx

強制執行

當您設定 PriorityClass 時,它們的存在方式與您定義的方式相同。但是,對您的叢集進行變更的人員(和工具)可以自由設定任何 PriorityClass,或完全不設定任何 PriorityClass。但是,您可以使用其他 Kubernetes 功能來確保實際應用您想要的優先順序。

作為 alpha 功能,您可以定義 ValidatingAdmissionPolicy 和 ValidatingAdmissionPolicyBinding,以便例如,進入 prod 命名空間的 Pod 必須使用 prod-pc PriorityClass。透過另一個 ValidatingAdmissionPolicyBinding,您可以確保 preprod 命名空間使用 preprod-pc PriorityClass,依此類推。在任何叢集中,您都可以使用外部專案(例如 KyvernoGatekeeper)透過驗證准入 Webhook 來強制執行類似的控制。

無論您如何執行,Kubernetes 都為您提供了多種選項,以確保 PriorityClass 按照您想要的方式使用,或者只是在使用者選擇不合適的選項時 警告 使用者。

摘要

以上範例及其事件向您展示了 Kubernetes 的此功能帶來的好處,以及您可以使用此功能的幾個情境。重申一下,這有助於確保任務關鍵型 Pod 正常運作並可用於提供流量服務,並且在資源吃緊的情況下,決定叢集行為。

它讓您有權力決定 Pod 的排程順序和 搶佔 順序。因此,您需要合理地定義 PriorityClass。例如,如果您有叢集自動擴充器來按需新增節點,請務必以 system-cluster-critical PriorityClass 執行它。您不希望陷入自動擴充器已被搶佔且沒有新節點上線的情況。

如果您有任何疑問或意見回饋,請隨時在 LinkedIn 上與我聯繫。