Taints和Tolerations(污點和容忍)
上面介紹的NodeAffinity節點親和性,是在pod上定義的一種屬性,是pod能夠被排程到某些node上運作(優先選擇或強制要求)。Taint則正好相反,使用kubectl taint指令可以給某個Node節點設定污點,Node被設定上污點之後就和Pod之間存在了一種相斥的關系,可以讓Node拒絕Pod的排程執行,甚至将Node已經存在的Pod驅逐出去。
Taint需要和Toleration配合使用,讓pod避開那些不适合的node。在node上設定一個或多個Taint之後,除非pod明确聲明能夠容忍這些污點,否則無法在這些node上運作。Toleration是pod的屬性,讓pod能夠(注意,隻是能夠,而非必須)運作在标注了Taint的node上。
每個污點的組成如下:
key=value:effect
每個污點有一個 key 和 value 作為污點的标簽,其中 value 可以為空,effect 描述污點的作用。
目前 taint effect 支援如下三個選項:
NoSchedule :表示k8s不會将新的不能容忍的Pod排程到具有該污點的Node上,但是之前運作在node節點中的Pod不受影響
PreferNoSchedule :表示k8s将盡量避免将Pod排程到具有該污點的Node上
NoExecute :表示k8s不會将新的不能容忍的Pod排程到具有該污點的Node上,同時會将Node上已經存在的Pod驅逐出去
可以用kubectl taint 指令為Node設定Taint資訊:
kubectl taint nodes node1 env=pro:NoSchedule
檢視所有Node上的污點
kubectl describe no|grep -e Hostname -e Taints|grep -v none|grep -A 1 "Taints"
檢視指定node上的污點
kubectl describe no nodename|grep Taints
删除taint:
kubectl taint nodes node1 env:NoSchedule-
這個設定為node1加上一個Taint。該Taint的鍵為key,值為value,Taint的效果是NoSchedule。這意味着除非pod明确聲明可以容忍這個Taint,否則就不會被排程到node1上。
然後,需要在pod上聲明 Toleration。下面的兩個Toleration都被設定為可以容忍(Toleration)具有該Taint的node,使得pod能夠被排程到node1上:
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
containers:
- name: pod-toleration
image: gcr.io/google_containers/pause:2.0
或者
tolerations:
operator: "Exists"
pod的toleration聲明中的key和effect需要與Taint的設定保持一緻,并且滿足以下條件之一。
operator的值是Exists(無需指定value)
operator的值是Equal 并且value相等。
如果不指定operator,則預設值為Equal。
另外,有如下兩個特例:
空的key配合Exists操作符能夠比對所有的鍵和值。
空的effect比對所有的effect。
系統允許在同一個Node上設定多個Taint,也可以在Pod上設定多個Toleration。Kubernetes排程器處理多個Taint和Toleration的邏輯順序為:首先列出節點中所有的Taint,然後忽略Pod的Toleration能夠比對的部分,剩下的沒有忽略的Taint就是對Pod的效果了。下面是幾種特殊情況。
如果在剩餘的Taint中存在effect=NoSchedule,則排程器不會把該Pod排程到這一節點上。
如果在剩餘的Taint中沒有NoSchedule效果,但是有PreferNoSchedule效果,則排程器會嘗試不把這個Pod指派給這個節點。
如果在剩餘的Taint中有NoExecute效果,并且這個Pod已經在該節點上運作,則會被驅逐;如果沒有在該節點上運作,則也不會再被排程到該節點上。
例如,我們這樣對一個節點進行Taint設定:
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
然後在Pod上設定兩個Toleration:
- key: "key1"
value: "value1"
effect: "NoExecute"
這樣的結果是該Pod無法被排程到node1上,這是因為第3個Taint沒有比對的Toleration。但是如果該Pod已經在node1上運作了,那麼在運作時設定第3個Taint,它還能繼續在node1上運作,這是因為Pod可以容忍前兩個Taint。
一般來說,如果給Node加上effect=NoExecute的Taint,那麼在該Node上正在運作的所有無對應Toleration的Pod都會被立刻驅逐,而具有相應Toleration的Pod永遠不會被驅逐。不過,系統允許給具有NoExecute效果的Toleration加入一個可選的tolerationSeconds字段,這個設定表明Pod可以在Taint添加到Node之後還能在這個Node上運作多久(機關為s):
tolerations:
tolerationSeconds: 3600
上述定義的意思是,如果Pod正在運作,所在節點都被加入一個比對的Taint,則這個Pod會持續在這個節點上存活3600s後被逐出。如果在這個寬限期内Taint被移除,則不會觸發驅逐事件。
Taint和Toleration是一種處理節點并且讓Pod進行規避或者驅逐Pod的彈性處理方式,下面列舉一些常見的用例。
1.專用節點(dedicated)
如果想要拿出一部分節點專門給一些特定應用使用,則可以為節點添加這樣的Taint:
kubectl taint nodes nodename dedicated=groupName:NoSchedule
然後給這些應用的Pod加入對應的Toleration。這樣,帶有合适Toleration的Pod就會被允許同使用其他節點一樣使用有Taint的節點。
通過自定義Admission Controller也可以實作這一目标。如果希望讓這些應用獨占一批節點,并且確定它們隻能使用這些節點,則還可以給這些Taint節點加入類似的标簽dedicated=groupName,然後Admission Controller需要加入節點親和性設定,要求Pod隻會被排程到具有這一标簽的節點上。
2.具有特殊(special)硬體裝置的節點
在叢集裡可能有一小部分節點安裝了特殊的硬體裝置(如GPU晶片),使用者自然會希望把不需要占用這類硬體的Pod排除在外,以確定對這類硬體有需求的Pod能夠被順利排程到這些節點。
可以用下面的指令為節點設定Taint:
kubectl taint nodes nodename special=true:NoSchedule
kubectl taint nodes nodename special=true:PreferNoSchedule
然後在Pod中利用對應的Toleration來保障特定的Pod能夠使用特定的硬體。
和上面的獨占節點的示例類似,使用Admission Controller來完成這一任務會更友善。例如,Admission Controller使用Pod的一些特征來判斷這些Pod,如果可以使用這些硬體,就添加Toleration來完成這一工作。要保障需要使用特殊硬體的Pod隻被排程到安裝這些硬體的節點上,則還需要一些額外的工作,比如将這些特殊資源使用opaque-int-resource的方式對自定義資源進行量化,然後在PodSpec中進行請求;也可以使用标簽的方式來标注這些安裝有特别硬體的節點,然後在Pod中定義節點親和性來實作這個目标。
3.基于污點的驅逐,以應對節點故障
前面提到的NoExecute這個Taint效果對節點上正在運作的Pod有以下影響。
◎ 沒有設定Toleration的Pod會被立刻驅逐。
◎ 配置了對應Toleration的Pod,如果沒有為tolerationSeconds指派,則會一直留在這一節點中。
◎ 配置了對應Toleration的Pod且指定了tolerationSeconds值,則會在指定時間後驅逐。
◎ 把節點故障标記為Taint
當某種條件為真時,節點控制器會自動給節點添加一個污點。目前内置的污點包括:
node.kubernetes.io/not-ready:節點未準備好。這相當于節點狀态 Ready 的值為 "False"。
node.kubernetes.io/unreachable:節點控制器通路不到節點. 這相當于節點狀态 Ready 的值為 "Unknown"。
node.kubernetes.io/out-of-disk:節點磁盤耗盡。
node.kubernetes.io/memory-pressure:節點存在記憶體壓力。
node.kubernetes.io/disk-pressure:節點存在磁盤壓力。
node.kubernetes.io/network-unavailable:節點網絡不可用。
node.kubernetes.io/unschedulable: 節點不可排程。
node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 啟動時指定了一個 "外部" 雲平台驅動, 它将給目前節點添加一個污點将其标志為不可用。在 cloud-controller-manager 的一個控制器初始化這個節點後,kubelet 将删除這個污點。
在節點被驅逐時,節點控制器或者 kubelet 會添加帶有 NoExecute 效應的相關污點。 如果異常狀态恢複正常,kubelet 或節點控制器能夠移除相關的污點。
說明: 為了保證由于節點問題引起的 Pod 驅逐 速率限制行為正常, 系統實際上會以限定速率的方式添加污點。在像主要節點與工作節點間通信中斷等場景下, 這樣做可以避免 Pod 被大量驅逐。
例如,一個包含很多本地狀态的應用可能需要在網絡發生故障時,還能持續在節點上運作,期望網絡能夠快速恢複,進而避免被從這個節點上驅逐。
Pod的Toleration可以這樣定義:
tolerations :
- key : "node.kubernetes.io/unreachable"
operator:"Exists"
effect : "NoExecute"
tolerationSeconds : 6000
說明:
如果沒有為Pod指定node.kubernetes.io/not-ready的Toleration,那麼Kubernetes會自動為Pod加入tolerationSeconds=300 的node.kubernetes.io/not-ready類型的Toleration。
同樣,如果Pod沒有定義node.kubernetes.io/unreachable的Toleration,那麼系統會自動為其加入tolerationSeconds=300 的node.kubernetes.io/unreachable類型的Toleration。
這些系統自動設定的toleration在Node發現問題時,能夠為Pod確定驅逐前再運作5min。這兩個預設的Toleration由AdmissionController“DefaultTolerationSeconds”自動加入。
DaemonSet 中的 Pod 被建立時, 針對以下污點自動添加的 NoExecute 的容忍度将不會指定 tolerationSeconds:
node.kubernetes.io/unreachable
node.kubernetes.io/not-ready
這保證了出現上述問題時 DaemonSet 中的 Pod 永遠不會被驅逐。