汙點和容忍
節點親和性(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` 且值應相等。
注意
有兩種特殊情況:
如果 `key` 為空,則 `operator` 必須為 `Exists`,它會匹配所有鍵和值。請注意,`effect` 仍然需要同時匹配。
空的 `effect` 匹配所有帶有鍵 `key1` 的效果。
上面的例子使用了 `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
注意
Kubernetes 會自動為 `node.kubernetes.io/not-ready` 和 `node.kubernetes.io/unreachable` 新增 `tolerationSeconds=300` 的容忍度,除非你或控制器明確設定了這些容忍度。
這些自動新增的容忍度意味著 Pod 在檢測到這些問題後,將在節點上保持繫結 5 分鐘。
DaemonSet Pod 會自動帶有以下針對汙點(不含 `tolerationSeconds`)的 `NoExecute` 容忍度:
node.kubernetes.io/unreachablenode.kubernetes.io/not-ready
這確保了 DaemonSet Pod 永遠不會因這些問題而被驅逐。
注意
以前,節點控制器負責向節點新增汙點和驅逐 Pod。但在 1.29 版本之後,基於汙點的驅逐實現已從節點控制器中移出,成為一個獨立的元件,稱為 taint-eviction-controller。使用者可以選擇透過在 kube-controller-manager 中設定 `--controllers=-taint-eviction-controller` 來停用基於汙點的驅逐。根據條件汙點節點
控制平面使用節點 控制器,自動為 節點條件 建立具有 `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-pressurenode.kubernetes.io/disk-pressure- `node.kubernetes.io/pid-pressure` (1.14 或更高版本)
- `node.kubernetes.io/unschedulable` (1.10 或更高版本)
- `node.kubernetes.io/network-unavailable` (*僅限主機網路*)
新增這些容忍度可確保向後相容性。你還可以向 DaemonSet 新增任意容忍度。
裝置汙點和容忍度
當叢集使用 動態資源分配 來管理特殊硬體時,管理員還可以 對單個裝置進行汙點設定,而不是對整個節點進行汙點設定。這樣做的好處是,汙點可以精確地針對有故障或需要維護的硬體。也支援容忍度,可以在請求裝置時指定。像汙點一樣,它們適用於所有共享相同已分配裝置的 Pod。