參考:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/taint-and-toleration/
節點親和性 是 Pod 的一種屬性,它使 Pod 被吸引到一類特定的節點 (這可能出於一種偏好,也可能是硬性要求)。 污點(Taint)則相反——它使節點能夠排斥一類特定的 Pod。
容忍度(Toleration)是應用於 Pod 上的,允許(但並不要求)Pod 調度到帶有與之匹配的污點的節點上。
污點和容忍度(Toleration)相互配合,可以用來避免 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:
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 ,則它們的 value 應該相等
說明:
存在兩種特殊情況:
如果一個容忍度的 key 為空且 operator 為 Exists, 表示這個容忍度與任意的 key 、value 和 effect 都匹配,即這個容忍度能容忍任意 taint。
如果 effect 為空,則可以與所有鍵名 key1 的效果相匹配。
上述例子中 effect 使用的值為 NoSchedule,您也可以使用另外一個值 PreferNoSchedule。 這是“優化”或“軟”版本的 NoSchedule —— 系統會 盡量 避免將 Pod 調度到存在其不能容忍污點的節點上, 但這不是強制的。effect 的值還可以設置為 NoExecute,下文會詳細描述這個值。
您可以給一個節點添加多個污點,也可以給一個 Pod 添加多個容忍度設置。 Kubernetes 處理多個污點和容忍度的過程就像一個過濾器:從一個節點的所有污點開始遍歷, 過濾掉那些 Pod 中存在與之相匹配的容忍度的污點。余下未被過濾的污點的 effect 值決定了 Pod 是否會被分配到該節點,特別是以下情況:
- 如果未被過濾的污點中存在至少一個 effect 值為 NoSchedule 的污點, 則 Kubernetes 不會將 Pod 分配到該節點。
- 如果未被過濾的污點中不存在 effect 值為 NoSchedule 的污點, 但是存在 effect 值為 PreferNoSchedule 的污點, 則 Kubernetes 會 嘗試 不將 Pod 分配到該節點。
- 如果未被過濾的污點中存在至少一個 effect 值為 NoExecute 的污點, 則 Kubernetes 不會將 Pod 分配到該節點(如果 Pod 還未在節點上運行), 或者將 Pod 從該節點驅逐(如果 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 已經在上述節點運行, 那么它還可以繼續運行在該節點上,因為第三個污點是三個污點中唯一不能被這個 Pod 容忍的。
通常情況下,如果給一個節點添加了一個 effect 值為 NoExecute 的污點, 則任何不能忍受這個污點的 Pod 都會馬上被驅逐, 任何可以忍受這個污點的 Pod 都不會被驅逐。 但是,如果 Pod 存在一個 effect 值為 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 添加一個相對應的 toleration(通過編寫一個自定義的 准入控制器,很容易就能做到)。 擁有上述容忍度的 Pod 就能夠被分配到上述專用節點,同時也能夠被分配到集群中的其它節點。 如果您希望這些 Pod 只能被分配到上述專用節點,那么您還需要給這些專用節點另外添加一個和上述 污點類似的 label (例如:dedicated=groupName),同時 還要在上述准入控制器中給 Pod 增加節點親和性要求上述 Pod 只能被分配到添加了 dedicated=groupName 標簽的節點上。
-
配備了特殊硬件的節點:在部分節點配備了特殊硬件(比如 GPU)的集群中, 我們希望不需要這類硬件的 Pod 不要被分配到這些特殊節點,以便為后繼需要這類硬件的 Pod 保留資源。 要達到這個目的,可以先給配備了特殊硬件的節點添加 taint (例如 kubectl taint nodes nodename special=true:NoSchedule 或 kubectl taint nodes nodename special=true:PreferNoSchedule), 然后給使用了這類特殊硬件的 Pod 添加一個相匹配的 toleration。 和專用節點的例子類似,添加這個容忍度的最簡單的方法是使用自定義 准入控制器。 比如,我們推薦使用擴展資源 來表示特殊硬件,給配置了特殊硬件的節點添加污點時包含擴展資源名稱, 然后運行一個 ExtendedResourceToleration 准入控制器。此時,因為節點已經被設置污點了,沒有對應容忍度的 Pod 不會被調度到這些節點。但當你創建一個使用了擴展資源的 Pod 時, ExtendedResourceToleration 准入控制器會自動給 Pod 加上正確的容忍度, 這樣 Pod 就會被自動調度到這些配置了特殊硬件件的節點上。 這樣就能夠確保這些配置了特殊硬件的節點專門用於運行需要使用這些硬件的 Pod, 並且您無需手動給這些 Pod 添加容忍度。
-
基於污點的驅逐: 這是在每個 Pod 中配置的在節點出現問題時的驅逐行為,接下來的章節會描述這個特性。
基於污點的驅逐
FEATURE STATE: Kubernetes v1.18 [stable]
前文提到過污點的 effect 值 NoExecute會影響已經在節點上運行的 Pod
- 如果 Pod 不能忍受 effect 值為 NoExecute 的污點,那么 Pod 將馬上被驅逐
- 如果 Pod 能夠忍受 effect 值為 NoExecute 的污點,但是在容忍度定義中沒有指定 tolerationSeconds,則 Pod 還會一直在這個節點上運行。
- 如果 Pod 能夠忍受 effect 值為 NoExecute 的污點,而且指定了 tolerationSeconds, 則 Pod 還能在這個節點上繼續運行這個指定的時間長度。
當某種條件為真時,節點控制器會自動給節點添加一個污點。當前內置的污點包括:
- node.kubernetes.io/not-ready:節點未准備好。這相當於節點狀態 Ready 的值為 "False"。
- node.kubernetes.io/unreachable:節點控制器訪問不到節點. 這相當於節點狀態 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 啟動時指定了一個 "外部" 雲平台驅動, 它將給當前節點添加一個污點將其標志為不可用。在 cloud-controller-manager 的一個控制器初始化這個節點后,kubelet 將刪除這個污點。
在節點被驅逐時,節點控制器或者 kubelet 會添加帶有 NoExecute 效應的相關污點。 如果異常狀態恢復正常,kubelet 或節點控制器能夠移除相關的污點。
說明: 為了保證由於節點問題引起的 Pod 驅逐 速率限制行為正常, 系統實際上會以限定速率的方式添加污點。在像主控節點與工作節點間通信中斷等場景下, 這樣做可以避免 Pod 被大量驅逐。
使用這個功能特性,結合 tolerationSeconds,Pod 就可以指定當節點出現一個 或全部上述問題時還將在這個節點上運行多長的時間。
比如,一個使用了很多本地狀態的應用程序在網絡斷開時,仍然希望停留在當前節點上運行一段較長的時間, 願意等待網絡恢復以避免被驅逐。在這種情況下,Pod 的容忍度可能是下面這樣的:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
說明:
Kubernetes 會自動給 Pod 添加一個 key 為 node.kubernetes.io/not-ready 的容忍度 並配置 tolerationSeconds=300,除非用戶提供的 Pod 配置中已經已存在了 key 為 node.kubernetes.io/not-ready 的容忍度。
同樣,Kubernetes 會給 Pod 添加一個 key 為 node.kubernetes.io/unreachable 的容忍度 並配置 tolerationSeconds=300,除非用戶提供的 Pod 配置中已經已存在了 key 為 node.kubernetes.io/unreachable 的容忍度。
這種自動添加的容忍度意味着在其中一種問題被檢測到時 Pod 默認能夠繼續停留在當前節點運行 5 分鍾。
DaemonSet 中的 Pod 被創建時, 針對以下污點自動添加的 NoExecute 的容忍度將不會指定 tolerationSeconds:
- node.kubernetes.io/unreachable
- node.kubernetes.io/not-ready
這保證了出現上述問題時 DaemonSet 中的 Pod 永遠不會被驅逐。
基於節點狀態添加污點
Node 生命周期控制器會自動創建與 Node 條件相對應的帶有 NoSchedule 效應的污點。 同樣,調度器不檢查節點條件,而是檢查節點污點。這確保了節點條件不會影響調度到節點上的內容。 用戶可以通過添加適當的 Pod 容忍度來選擇忽略某些 Node 的問題(表示為 Node 的調度條件)。
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 添加容忍度。