天天看點

Taints和Tolerations(污點和容忍)

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 永遠不會被驅逐。

繼續閱讀