本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
使用 PriorityClass 保護您的任務關鍵型 Pod 免於驅逐
Kubernetes 已被廣泛採用,許多組織將其用作事實上的協調引擎,以執行需要頻繁建立和刪除的工作負載。
因此,適當的 Pod 排程是確保應用程式 Pod 在 Kubernetes 叢集中正常運作且沒有任何問題的關鍵。本文深入探討資源管理的使用案例,方法是利用 PriorityClass 物件來保護任務關鍵型或高優先順序的 Pod 免於被驅逐,並確保應用程式 Pod 正常運作、執行並提供流量服務。
Kubernetes 中的資源管理
控制平面由多個組件組成,其中排程器(通常是內建的 kube-scheduler)是負責將節點指派給 Pod 的組件之一。
每當建立 Pod 時,它會進入「擱置中」狀態,之後排程器會判斷哪個節點最適合放置新的 Pod。
在背景中,排程器以無限迴圈的方式執行,尋找未設定 nodeName
且 準備好進行排程 的 Pod。對於每個需要排程的 Pod,排程器會嘗試決定哪個節點應該執行該 Pod。
如果排程器找不到任何節點,Pod 將保持擱置中狀態,這並非理想情況。
注意
舉例來說,nodeSelector
、taints and tolerations
、nodeAffinity
、基於可用資源(例如 CPU 和記憶體)的節點排名以及其他幾個標準,都用於決定 Pod 的放置位置。下圖從第 1 點到第 4 點,說明了請求流程
Kubernetes 中的排程
典型使用案例
以下是一些實際情境,可能需要控制 Pod 的排程和驅逐。
假設您計劃部署的 Pod 至關重要,而且您有一些資源限制。基礎架構組件(如 Grafana Loki)的 DaemonSet 就是一個例子。Loki Pod 必須在每個節點上的其他 Pod 之前執行。在這種情況下,您可以透過手動識別和刪除不需要的 Pod,或透過在叢集中新增節點來確保資源可用性。這兩種方法都不合適,因為前者執行起來會很繁瑣,而後者可能會涉及時間和金錢的支出。
另一個使用案例可能是一個單一叢集,其中包含以下具有相關優先順序的環境的 Pod
- 生產環境 (
prod
):最高優先順序 - 預生產環境 (
preprod
):中等優先順序 - 開發環境 (
dev
):最低優先順序
在叢集中資源消耗量高的情況下,節點上的 CPU 和記憶體資源會發生競爭。雖然叢集層級的自動擴充可能會新增更多節點,但這需要時間。在此期間,如果沒有更多節點可以擴充叢集,則某些 Pod 可能會保持在擱置中狀態,或者服務可能會因爭奪資源而降級。如果 kubelet 從節點驅逐 Pod,則驅逐將是隨機的,因為 kubelet 沒有關於要驅逐哪些 Pod 以及保留哪些 Pod 的任何特殊資訊。
- 生產環境 (
第三個例子可能是一個微服務,由佇列應用程式或資料庫支援,遇到資源吃緊,導致佇列或資料庫被驅逐。在這種情況下,所有其他服務都將變得無用,直到資料庫可以再次提供流量服務。
在其他情境中,您可能也希望控制 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
下圖準確地顯示了它的運作方式,並透過一個範例來說明,該範例將在後續章節中詳細介紹。
Pod 排程和搶佔
Pod 優先順序和搶佔
Pod 搶佔 是 Kubernetes 的一項功能,允許叢集根據優先順序搶佔 Pod(移除現有的 Pod 以支持新的 Pod)。Pod 優先順序 指出 Pod 相對於其他 Pod 在排程時的重要性。如果沒有足夠的資源來執行所有目前的 Pod,排程器會嘗試驅逐低優先順序的 Pod,而不是高優先順序的 Pod。
此外,當健康的叢集遇到節點故障時,通常會搶佔低優先順序的 Pod,以便在可用節點上為高優先順序的 Pod 騰出空間。即使叢集可以自動啟動新節點,也會發生這種情況,因為 Pod 建立通常比啟動新節點快得多。
PriorityClass 需求
在設定 PriorityClass 之前,有幾件事需要考慮。
- 決定需要哪些 PriorityClass。例如,根據環境、Pod 類型、應用程式類型等。
- 叢集的預設 PriorityClass 資源。沒有
priorityClassName
的 Pod 將被視為優先順序 0。 - 對所有 PriorityClass 使用一致的命名慣例。
- 確保工作負載的 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。
注意
新增globalDefault
設定為 true
的 PriorityClass 並不表示它會對已在執行的現有 Pod 應用相同的設定。這僅適用於在建立 PriorityClass 之後出現的 Pod。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,依此類推。在任何叢集中,您都可以使用外部專案(例如 Kyverno 或 Gatekeeper)透過驗證准入 Webhook 來強制執行類似的控制。
無論您如何執行,Kubernetes 都為您提供了多種選項,以確保 PriorityClass 按照您想要的方式使用,或者只是在使用者選擇不合適的選項時 警告 使用者。
摘要
以上範例及其事件向您展示了 Kubernetes 的此功能帶來的好處,以及您可以使用此功能的幾個情境。重申一下,這有助於確保任務關鍵型 Pod 正常運作並可用於提供流量服務,並且在資源吃緊的情況下,決定叢集行為。
它讓您有權力決定 Pod 的排程順序和 搶佔 順序。因此,您需要合理地定義 PriorityClass。例如,如果您有叢集自動擴充器來按需新增節點,請務必以 system-cluster-critical
PriorityClass 執行它。您不希望陷入自動擴充器已被搶佔且沒有新節點上線的情況。
如果您有任何疑問或意見回饋,請隨時在 LinkedIn 上與我聯繫。