Kubernetes Pod優(yōu)先級和搶占

2022-05-26 10:34 更新

Pod 優(yōu)先級和搶占

FEATURE STATE: Kubernetes v1.14 [stable]

Pod 可以有 優(yōu)先級。 優(yōu)先級表示一個 Pod 相對于其他 Pod 的重要性。 如果一個 Pod 無法被調度,調度程序會嘗試搶占(驅逐)較低優(yōu)先級的 Pod, 以使懸決 Pod 可以被調度。

Warning:
在一個并非所有用戶都是可信的集群中,惡意用戶可能以最高優(yōu)先級創(chuàng)建 Pod, 導致其他 Pod 被驅逐或者無法被調度。 管理員可以使用 ResourceQuota 來阻止用戶創(chuàng)建高優(yōu)先級的 Pod。

如何使用優(yōu)先級和搶占

要使用優(yōu)先級和搶占:

  1. 新增一個或多個 PriorityClass。
  2. 創(chuàng)建 Pod,并將其 ?priorityClassName ?設置為新增的 PriorityClass。 當然你不需要直接創(chuàng)建 Pod;通常,你將會添加 ?priorityClassName ?到集合對象(如 Deployment) 的 Pod 模板中。

Note:
Kubernetes 已經提供了 2 個 PriorityClass: ?system-cluster-critical? 和 ?system-node-critical?。 這些是常見的類,用于確保始終優(yōu)先調度關鍵組件。

PriorityClass

PriorityClass 是一個無名稱空間對象,它定義了從優(yōu)先級類名稱到優(yōu)先級整數值的映射。 名稱在 PriorityClass 對象元數據的 ?name ?字段中指定。 值在必填的 ?value ?字段中指定。值越大,優(yōu)先級越高。 PriorityClass 對象的名稱必須是有效的 DNS 子域名, 并且它不能以 ?system-? 為前綴。

PriorityClass 對象可以設置任何小于或等于 10 億的 32 位整數值。 較大的數字是為通常不應被搶占或驅逐的關鍵的系統(tǒng) Pod 所保留的。 集群管理員應該為這類映射分別創(chuàng)建獨立的 PriorityClass 對象。

PriorityClass 還有兩個可選字段:?globalDefault ?和 ?description?。 ?globalDefault ?字段表示這個 PriorityClass 的值應該用于沒有 ?priorityClassName ?的 Pod。 系統(tǒng)中只能存在一個 ?globalDefault ?設置為 true 的 PriorityClass。 如果不存在設置了 ?globalDefault ?的 PriorityClass, 則沒有 ?priorityClassName ?的 Pod 的優(yōu)先級為零。

?description ?字段是一個任意字符串。 它用來告訴集群用戶何時應該使用此 PriorityClass。

關于 PodPriority 和現(xiàn)有集群的注意事項

  • 如果你升級一個已經存在的但尚未使用此特性的集群,該集群中已經存在的 Pod 的優(yōu)先級等效于零。
  • 添加一個將 ?globalDefault ?設置為 ?true ?的 PriorityClass 不會改變現(xiàn)有 Pod 的優(yōu)先級。 此類 PriorityClass 的值僅用于添加 PriorityClass 后創(chuàng)建的 Pod。
  • 如果你刪除了某個 PriorityClass 對象,則使用被刪除的 PriorityClass 名稱的現(xiàn)有 Pod 保持不變, 但是你不能再創(chuàng)建使用已刪除的 PriorityClass 名稱的 Pod。

PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "此優(yōu)先級類應僅用于 XYZ 服務 Pod。"

非搶占式 PriorityClass 

FEATURE STATE: Kubernetes v1.24 [stable]

配置了 ?preemptionPolicy: Never? 的 Pod 將被放置在調度隊列中較低優(yōu)先級 Pod 之前, 但它們不能搶占其他 Pod。等待調度的非搶占式 Pod 將留在調度隊列中,直到有足夠的可用資源, 它才可以被調度。非搶占式 Pod,像其他 Pod 一樣,受調度程序回退的影響。 這意味著如果調度程序嘗試這些 Pod 并且無法調度它們,它們將以更低的頻率被重試, 從而允許其他優(yōu)先級較低的 Pod 排在它們之前。

非搶占式 Pod 仍可能被其他高優(yōu)先級 Pod 搶占。

?preemptionPolicy ?默認為 ?PreemptLowerPriority?, 這將允許該 PriorityClass 的 Pod 搶占較低優(yōu)先級的 Pod(現(xiàn)有默認行為也是如此)。 如果 ?preemptionPolicy ?設置為 ?Never?,則該 PriorityClass 中的 Pod 將是非搶占式的。

數據科學工作負載是一個示例用例。用戶可以提交他們希望優(yōu)先于其他工作負載的作業(yè), 但不希望因為搶占運行中的 Pod 而導致現(xiàn)有工作被丟棄。 設置為 ?preemptionPolicy: Never? 的高優(yōu)先級作業(yè)將在其他排隊的 Pod 之前被調度, 只要足夠的集群資源“自然地”變得可用。

非搶占式 PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."

Pod 優(yōu)先級

在你擁有一個或多個 PriorityClass 對象之后, 你可以創(chuàng)建在其規(guī)約中指定這些 PriorityClass 名稱之一的 Pod。 優(yōu)先級準入控制器使用 ?priorityClassName ?字段并填充優(yōu)先級的整數值。 如果未找到所指定的優(yōu)先級類,則拒絕 Pod。

以下 YAML 是 Pod 配置的示例,它使用在前面的示例中創(chuàng)建的 PriorityClass。 優(yōu)先級準入控制器檢查 Pod 規(guī)約并將其優(yōu)先級解析為 1000000。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

Pod 優(yōu)先級對調度順序的影響 

當啟用 Pod 優(yōu)先級時,調度程序會按優(yōu)先級對懸決 Pod 進行排序, 并且每個懸決的 Pod 會被放置在調度隊列中其他優(yōu)先級較低的懸決 Pod 之前。 因此,如果滿足調度要求,較高優(yōu)先級的 Pod 可能會比具有較低優(yōu)先級的 Pod 更早調度。 如果無法調度此類 Pod,調度程序將繼續(xù)并嘗試調度其他較低優(yōu)先級的 Pod。

搶占 

Pod 被創(chuàng)建后會進入隊列等待調度。 調度器從隊列中挑選一個 Pod 并嘗試將它調度到某個節(jié)點上。 如果沒有找到滿足 Pod 的所指定的所有要求的節(jié)點,則觸發(fā)對懸決 Pod 的搶占邏輯。 讓我們將懸決 Pod 稱為 P。搶占邏輯試圖找到一個節(jié)點, 在該節(jié)點中刪除一個或多個優(yōu)先級低于 P 的 Pod,則可以將 P 調度到該節(jié)點上。 如果找到這樣的節(jié)點,一個或多個優(yōu)先級較低的 Pod 會被從節(jié)點中驅逐。 被驅逐的 Pod 消失后,P 可以被調度到該節(jié)點上。

用戶暴露的信息

當 Pod P 搶占節(jié)點 N 上的一個或多個 Pod 時, Pod P 狀態(tài)的 ?nominatedNodeName ?字段被設置為節(jié)點 N 的名稱。 該字段幫助調度程序跟蹤為 Pod P 保留的資源,并為用戶提供有關其集群中搶占的信息。

請注意,Pod P 不一定會調度到“被提名的節(jié)點(Nominated Node)”。 調度程序總是在迭代任何其他節(jié)點之前嘗試“指定節(jié)點”。 在 Pod 因搶占而犧牲時,它們將獲得體面終止期。 如果調度程序正在等待犧牲者 Pod 終止時另一個節(jié)點變得可用, 則調度程序可以使用另一個節(jié)點來調度 Pod P。 因此,Pod 規(guī)約中的 ?nominatedNodeName ?和 ?nodeName ?并不總是相同。 此外,如果調度程序搶占節(jié)點 N 上的 Pod,但隨后比 Pod P 更高優(yōu)先級的 Pod 到達, 則調度程序可能會將節(jié)點 N 分配給新的更高優(yōu)先級的 Pod。 在這種情況下,調度程序會清除 Pod P 的 ?nominatedNodeName?。 通過這樣做,調度程序使 Pod P 有資格搶占另一個節(jié)點上的 Pod。

搶占的限制 

被搶占犧牲者的體面終止

當 Pod 被搶占時,犧牲者會得到他們的 體面終止期。 它們可以在體面終止期內完成工作并退出。如果它們不這樣做就會被殺死。 這個體面終止期在調度程序搶占 Pod 的時間點和待處理的 Pod (P) 可以在節(jié)點 (N) 上調度的時間點之間劃分出了一個時間跨度。 同時,調度器會繼續(xù)調度其他待處理的 Pod。當犧牲者退出或被終止時, 調度程序會嘗試在待處理隊列中調度 Pod。 因此,調度器搶占犧牲者的時間點與 Pod P 被調度的時間點之間通常存在時間間隔。 為了最小化這個差距,可以將低優(yōu)先級 Pod 的體面終止時間設置為零或一個小數字。

支持 PodDisruptionBudget,但不保證

PodDisruptionBudget (PDB) 允許多副本應用程序的所有者限制因自愿性質的干擾而同時終止的 Pod 數量。 Kubernetes 在搶占 Pod 時支持 PDB,但對 PDB 的支持是基于盡力而為原則的。 調度器會嘗試尋找不會因被搶占而違反 PDB 的犧牲者,但如果沒有找到這樣的犧牲者, 搶占仍然會發(fā)生,并且即使違反了 PDB 約束也會刪除優(yōu)先級較低的 Pod。

與低優(yōu)先級 Pod 之間的 Pod 間親和性

只有當這個問題的答案是肯定的時,才考慮在一個節(jié)點上執(zhí)行搶占操作: “如果從此節(jié)點上刪除優(yōu)先級低于懸決 Pod 的所有 Pod,懸決 Pod 是否可以在該節(jié)點上調度?”

Note: 搶占并不一定會刪除所有較低優(yōu)先級的 Pod。 如果懸決 Pod 可以通過刪除少于所有較低優(yōu)先級的 Pod 來調度, 那么只有一部分較低優(yōu)先級的 Pod 會被刪除。 即便如此,上述問題的答案必須是肯定的。 如果答案是否定的,則不考慮在該節(jié)點上執(zhí)行搶占。

如果懸決 Pod 與節(jié)點上的一個或多個較低優(yōu)先級 Pod 具有 Pod 間親和性, 則在沒有這些較低優(yōu)先級 Pod 的情況下,無法滿足 Pod 間親和性規(guī)則。 在這種情況下,調度程序不會搶占節(jié)點上的任何 Pod。 相反,它尋找另一個節(jié)點。調度程序可能會找到合適的節(jié)點, 也可能不會。無法保證懸決 Pod 可以被調度。

我們針對此問題推薦的解決方案是僅針對同等或更高優(yōu)先級的 Pod 設置 Pod 間親和性。

跨節(jié)點搶占

假設正在考慮在一個節(jié)點 N 上執(zhí)行搶占,以便可以在 N 上調度待處理的 Pod P。 只有當另一個節(jié)點上的 Pod 被搶占時,P 才可能在 N 上變得可行。 下面是一個例子:

  • 正在考慮將 Pod P 調度到節(jié)點 N 上。
  • Pod Q 正在與節(jié)點 N 位于同一區(qū)域的另一個節(jié)點上運行。
  • Pod P 與 Pod Q 具有 Zone 維度的反親和(?topologyKey:topology.kubernetes.io/zone?)。
  • Pod P 與 Zone 中的其他 Pod 之間沒有其他反親和性設置。
  • 為了在節(jié)點 N 上調度 Pod P,可以搶占 Pod Q,但調度器不會進行跨節(jié)點搶占。 因此,Pod P 將被視為在節(jié)點 N 上不可調度。

如果將 Pod Q 從所在節(jié)點中移除,則不會違反 Pod 間反親和性約束, 并且 Pod P 可能會被調度到節(jié)點 N 上。

如果有足夠的需求,并且如果我們找到性能合理的算法, 我們可能會考慮在未來版本中添加跨節(jié)點搶占。

故障排除

Pod 優(yōu)先級和搶占可能會產生不必要的副作用。以下是一些潛在問題的示例以及處理這些問題的方法。

Pod 被不必要地搶占

搶占在資源壓??力較大時從集群中刪除現(xiàn)有 Pod,為更高優(yōu)先級的懸決 Pod 騰出空間。 如果你錯誤地為某些 Pod 設置了高優(yōu)先級,這些無意的高優(yōu)先級 Pod 可能會導致集群中出現(xiàn)搶占行為。 Pod 優(yōu)先級是通過設置 Pod 規(guī)約中的 ?priorityClassName ?字段來指定的。 優(yōu)先級的整數值然后被解析并填充到 ?podSpec ?的 ?priority ?字段。

為了解決這個問題,你可以將這些 Pod 的 ?priorityClassName ?更改為使用較低優(yōu)先級的類, 或者將該字段留空。默認情況下,空的 ?priorityClassName ?解析為零。

當 Pod 被搶占時,集群會為被搶占的 Pod 記錄事件。只有當集群沒有足夠的資源用于 Pod 時, 才會發(fā)生搶占。在這種情況下,只有當懸決 Pod(搶占者)的優(yōu)先級高于受害 Pod 時才會發(fā)生搶占。 當沒有懸決 Pod,或者懸決 Pod 的優(yōu)先級等于或低于犧牲者時,不得發(fā)生搶占。 如果在這種情況下發(fā)生搶占,請?zhí)岢鰡栴}。

有 Pod 被搶占,但搶占者并沒有被調度

當 Pod 被搶占時,它們會收到請求的體面終止期,默認為 30 秒。 如果受害 Pod 在此期限內沒有終止,它們將被強制終止。 一旦所有犧牲者都離開,就可以調度搶占者 Pod。

在搶占者 Pod 等待犧牲者離開的同時,可能某個適合同一個節(jié)點的更高優(yōu)先級的 Pod 被創(chuàng)建。 在這種情況下,調度器將調度優(yōu)先級更高的 Pod 而不是搶占者。

這是預期的行為:具有較高優(yōu)先級的 Pod 應該取代具有較低優(yōu)先級的 Pod。

優(yōu)先級較高的 Pod 在優(yōu)先級較低的 Pod 之前被搶占

調度程序嘗試查找可以運行懸決 Pod 的節(jié)點。如果沒有找到這樣的節(jié)點, 調度程序會嘗試從任意節(jié)點中刪除優(yōu)先級較低的 Pod,以便為懸決 Pod 騰出空間。 如果具有低優(yōu)先級 Pod 的節(jié)點無法運行懸決 Pod, 調度器可能會選擇另一個具有更高優(yōu)先級 Pod 的節(jié)點(與其他節(jié)點上的 Pod 相比)進行搶占。 犧牲者的優(yōu)先級必須仍然低于搶占者 Pod。

當有多個節(jié)點可供執(zhí)行搶占操作時,調度器會嘗試選擇具有一組優(yōu)先級最低的 Pod 的節(jié)點。 但是,如果此類 Pod 具有 PodDisruptionBudget,當它們被搶占時, 則會違反 PodDisruptionBudget,那么調度程序可能會選擇另一個具有更高優(yōu)先級 Pod 的節(jié)點。

當存在多個節(jié)點搶占且上述場景均不適用時,調度器會選擇優(yōu)先級最低的節(jié)點。

Pod 優(yōu)先級和服務質量之間的相互作用

Pod 優(yōu)先級和 QoS 類 是兩個正交特征,交互很少,并且對基于 QoS 類設置 Pod 的優(yōu)先級沒有默認限制。 調度器的搶占邏輯在選擇搶占目標時不考慮 QoS。 搶占會考慮 Pod 優(yōu)先級并嘗試選擇一組優(yōu)先級最低的目標。 僅當移除優(yōu)先級最低的 Pod 不足以讓調度程序調度搶占式 Pod, 或者最低優(yōu)先級的 Pod 受 PodDisruptionBudget 保護時,才會考慮優(yōu)先級較高的 Pod。

kubelet 使用優(yōu)先級來確定 節(jié)點壓力驅逐 Pod 的順序。 你可以使用 QoS 類來估計 Pod 最有可能被驅逐的順序。kubelet 根據以下因素對 Pod 進行驅逐排名:

  1. 對緊俏資源的使用是否超過請求值
  2. Pod 優(yōu)先級
  3. 相對于請求的資源使用量

當某 Pod 的資源用量未超過其請求時,kubelet 節(jié)點壓力驅逐不會驅逐該 Pod。 如果優(yōu)先級較低的 Pod 沒有超過其請求,則不會被驅逐。 另一個優(yōu)先級高于其請求的 Pod 可能會被驅逐。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號