汙點和容忍

節點親和性(Node affinity)Pod 的一個屬性,它會**吸引** Pod 到一組 節點(作為首選項或硬性要求)。**汙點(Taints)** 則恰恰相反——它們允許節點排斥一組 Pod。

**容忍度(Tolerations)** 應用於 Pod。容忍度允許排程器排程帶有匹配汙點的 Pod。容忍度允許排程但不保證排程:排程器還會**評估其他引數**作為其功能的一部分。

汙點和容忍度協同工作,以確保 Pod 不被排程到不合適的節點上。一個或多個汙點應用於節點;這標誌著節點不應該接受任何不容忍這些汙點的 Pod。

概念

你可以使用 kubectl taint 向節點新增汙點。例如,

kubectl taint nodes node1 key1=value1:NoSchedule

在節點 `node1` 上設定了一個汙點。該汙點擁有鍵 `key1`、值 `value1`,以及汙點效果 `NoSchedule`。這意味著沒有 Pod 能夠排程到 `node1` 上,除非它具有匹配的容忍度。

要移除上面命令新增的汙點,你可以執行

kubectl taint nodes node1 key1=value1:NoSchedule-

你可以在 PodSpec 中為 Pod 指定容忍度。以下兩種容忍度都“匹配”上面 `kubectl taint` 命令建立的汙點,因此具有其中任何一種容忍度的 Pod 都能夠排程到 `node1` 上

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

當選擇一個節點來執行特定的 Pod 時,預設的 Kubernetes 排程器會考慮汙點和容忍度。但是,如果你手動為 Pod 指定了 `.spec.nodeName`,該操作會繞過排程器;然後 Pod 將繫結到你分配的節點上,即使該節點上存在你選擇的 `NoSchedule` 汙點。如果發生這種情況,並且節點也設定了 `NoExecute` 汙點,kubelet 將會驅逐該 Pod,除非設定了適當的容忍度。

這是一個定義了一些容忍度的 Pod 示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

`operator` 的預設值為 `Equal`。

如果鍵相同且效果相同,則容忍度“匹配”汙點,並且

  • `operator` 為 `Exists` (在這種情況下不應指定 `value`),或者
  • `operator` 為 `Equal` 且值應相等。

上面的例子使用了 `NoSchedule` 的 `effect`。另外,你也可以使用 `PreferNoSchedule` 的 `effect`。

`effect` 欄位允許的值為

NoExecute
這會影響已在節點上執行的 Pod,具體如下:
  • 不容忍該汙點的 Pod 會立即被驅逐。
  • 在容忍度規約中未指定 `tolerationSeconds` 但容忍該汙點的 Pod 會永遠繫結在節點上。
  • 容忍該汙點並指定了 `tolerationSeconds` 的 Pod 會在指定的時間內保持繫結。該時間結束後,節點生命週期控制器會將 Pod 從節點中驅逐。
NoSchedule
除非有匹配的容忍度,否則新的 Pod 將不會被排程到被汙染的節點上。當前正在節點上執行的 Pod **不會**被驅逐。
PreferNoSchedule
`PreferNoSchedule` 是 `NoSchedule` 的“偏好”或“軟性”版本。控制平面**會盡量**避免將不容忍汙點的 Pod 放置到節點上,但這不作保證。

你可以在同一個節點上設定多個汙點,在同一個 Pod 上設定多個容忍度。Kubernetes 處理多個汙點和容忍度的方式就像一個過濾器:從節點的所有汙點開始,然後忽略 Pod 具有匹配容忍度的那些汙點;剩餘的未被忽略的汙點會對 Pod 產生指示的效果。具體來說:

  • 如果至少有一個未被忽略的汙點具有 `NoSchedule` 效果,那麼 Kubernetes 將不會將 Pod 排程到該節點。
  • 如果沒有未被忽略的汙點具有 `NoSchedule` 效果,但至少有一個未被忽略的汙點具有 `PreferNoSchedule` 效果,那麼 Kubernetes 將**嘗試**不將 Pod 排程到該節點上。
  • 如果至少有一個未被忽略的汙點具有 `NoExecute` 效果,那麼 Pod 將從節點中被驅逐(如果它已經在節點上執行),並且不會被排程到該節點上(如果它尚未在節點上執行)。

例如,假設你像這樣汙染一個節點

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

一個 Pod 有兩個容忍度

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

在這種情況下,Pod 將無法排程到節點上,因為沒有容忍度能匹配第三個汙點。但如果它在汙點被新增時已經在節點上執行,它將能夠繼續執行,因為第三個汙點是三個汙點中唯一一個 Pod 不容忍的汙點。

通常,如果給節點添加了 `NoExecute` 效果的汙點,那麼任何不容忍該汙點的 Pod 都將立即被驅逐,而容忍該汙點的 Pod 則永遠不會被驅逐。然而,具有 `NoExecute` 效果的容忍度可以指定一個可選的 `tolerationSeconds` 欄位,該欄位規定了在汙點新增後 Pod 將繫結到節點上的時間長度。例如,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

意味著如果此 Pod 正在執行,並且向節點添加了一個匹配的汙點,則該 Pod 將在節點上繫結 3600 秒,然後被驅逐。如果在該時間之前汙點被移除,則 Pod 將不會被驅逐。

使用場景示例

汙點和容忍度是一種靈活的方式,可以將 Pod **引導離開**節點,或驅逐不應執行的 Pod。一些用例包括:

  • **專用節點**:如果你想將一組節點專門供特定使用者獨佔使用,你可以向這些節點新增汙點(例如,`kubectl taint nodes nodename dedicated=groupName:NoSchedule`),然後向他們的 Pod 新增相應的容忍度(透過編寫自定義准入控制器來完成最簡單)。具有容忍度的 Pod 將被允許使用被汙染的(專用)節點以及叢集中的任何其他節點。如果你想將節點專門供他們使用**並**確保他們**只**使用專用節點,那麼你應該另外向同一組節點新增一個類似於汙點的標籤(例如 `dedicated=groupName`),並且准入控制器應該另外新增一個節點親和性,要求 Pod 只能排程到帶有 `dedicated=groupName` 標籤的節點上。

  • **具有特殊硬體的節點**:在一個叢集中,如果一小部分節點具有專用硬體(例如 GPU),最好讓不需要專用硬體的 Pod 不在這些節點上執行,從而為稍後需要專用硬體的 Pod 留出空間。這可以透過汙點化具有專用硬體的節點(例如 `kubectl taint nodes nodename special=true:NoSchedule` 或 `kubectl taint nodes nodename special=true:PreferNoSchedule`),併為使用專用硬體的 Pod 新增相應的容忍度來實現。與專用節點用例一樣,最簡單的做法可能是使用自定義的 准入控制器 來應用容忍度。例如,建議使用 擴充套件資源 來表示特殊硬體,使用擴充套件資源名稱汙點你的特殊硬體節點,並執行 ExtendedResourceToleration 准入控制器。現在,由於節點被汙點化,沒有容忍度的 Pod 將不會排程到它們上。但是當你提交一個請求擴充套件資源的 Pod 時,`ExtendedResourceToleration` 准入控制器將自動為 Pod 新增正確的容忍度,並且該 Pod 將排程到特殊硬體節點上。這將確保這些特殊硬體節點專用於請求此類硬體的 Pod,並且你無需手動為你的 Pod 新增容忍度。

  • **基於汙點的驅逐**:節點出現問題時,Pod 可配置的逐出行為,這將在下一節中描述。

基於汙點的驅逐

特性狀態: Kubernetes v1.18 [stable]

當某些條件為真時,節點控制器會自動給節點打汙點。以下汙點是內建的:

  • `node.kubernetes.io/not-ready`:節點未就緒。這對應於 NodeCondition `Ready` 的狀態為“`False`”。
  • `node.kubernetes.io/unreachable`:節點無法從節點控制器訪問。這對應於 NodeCondition `Ready` 的狀態為“`Unknown`”。
  • `node.kubernetes.io/memory-pressure`:節點記憶體壓力過大。
  • `node.kubernetes.io/disk-pressure`:節點磁碟壓力過大。
  • `node.kubernetes.io/pid-pressure`:節點 PID 壓力過大。
  • `node.kubernetes.io/network-unavailable`:節點網路不可用。
  • `node.kubernetes.io/unschedulable`:節點不可排程。
  • `node.cloudprovider.kubernetes.io/uninitialized`:當 kubelet 啟動時使用了“外部”雲提供商,此汙點會設定在節點上以將其標記為不可用。當雲控制器管理器中的控制器初始化此節點後,kubelet 會移除此汙點。

在某些情況下,當節點無法訪問時,API 伺服器無法與節點上的 kubelet 通訊。在與 API 伺服器重新建立通訊之前,無法將刪除 Pod 的決定傳達給 kubelet。在此期間,計劃刪除的 Pod 可能會繼續在分割槽節點上執行。

在某些情況下,當節點無法訪問時,API 伺服器無法與節點上的 kubelet 通訊。在與 API 伺服器重新建立通訊之前,無法將刪除 Pod 的決定傳達給 kubelet。在此期間,計劃刪除的 Pod 可能會繼續在分割槽節點上執行。

你可以為 Pod 指定 `tolerationSeconds`,以定義該 Pod 在發生故障或無響應的節點上停留多長時間。

例如,你可能希望在網路分割槽的情況下,讓具有大量本地狀態的應用程式長時間繫結到節點,希望分割槽能夠恢復,從而避免 Pod 被驅逐。你為該 Pod 設定的容忍度可能看起來像:

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

DaemonSet Pod 會自動帶有以下針對汙點(不含 `tolerationSeconds`)的 `NoExecute` 容忍度:

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

這確保了 DaemonSet Pod 永遠不會因這些問題而被驅逐。

根據條件汙點節點

控制平面使用節點 控制器,自動為 節點條件 建立具有 `NoSchedule` 效果的汙點。

排程器在做出排程決策時會檢查汙點,而不是節點條件。這確保了節點條件不會直接影響排程。例如,如果 `DiskPressure` 節點條件處於活動狀態,控制平面會新增 `node.kubernetes.io/disk-pressure` 汙點,並且不會將新的 Pod 排程到受影響的節點上。如果 `MemoryPressure` 節點條件處於活動狀態,控制平面會新增 `node.kubernetes.io/memory-pressure` 汙點。

你可以透過新增相應的 Pod 容忍度來忽略新建立的 Pod 的節點條件。控制平面還會為 QoS 類 不是 `BestEffort` 的 Pod 新增 `node.kubernetes.io/memory-pressure` 容忍度。這是因為 Kubernetes 將 `Guaranteed` 或 `Burstable` QoS 類中的 Pod(即使是沒有設定記憶體請求的 Pod)視為能夠應對記憶體壓力,而新的 `BestEffort` Pod 則不會排程到受影響的節點。

DaemonSet 控制器會自動向所有守護程序新增以下 `NoSchedule` 容忍度,以防止 DaemonSet 損壞。

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • `node.kubernetes.io/pid-pressure` (1.14 或更高版本)
  • `node.kubernetes.io/unschedulable` (1.10 或更高版本)
  • `node.kubernetes.io/network-unavailable` (*僅限主機網路*)

新增這些容忍度可確保向後相容性。你還可以向 DaemonSet 新增任意容忍度。

裝置汙點和容忍度

當叢集使用 動態資源分配 來管理特殊硬體時,管理員還可以 對單個裝置進行汙點設定,而不是對整個節點進行汙點設定。這樣做的好處是,汙點可以精確地針對有故障或需要維護的硬體。也支援容忍度,可以在請求裝置時指定。像汙點一樣,它們適用於所有共享相同已分配裝置的 Pod。

下一步

上次修改時間為 2025 年 6 月 10 日太平洋標準時間下午 3:21:更新 SeparateTaintEvictionController 功能門控階段 (c13b557834)