Kubernetes 將Pod指派給節(jié)點

2022-05-26 09:45 更新

將 Pod 指派給節(jié)點

你可以約束一個 Pod 只能在特定的節(jié)點上運行。 有幾種方法可以實現(xiàn)這點,推薦的方法都是用 標簽選擇算符來進行選擇。 通常這樣的約束不是必須的,因為調(diào)度器將自動進行合理的放置(比如,將 Pod 分散到節(jié)點上, 而不是將 Pod 放置在可用資源不足的節(jié)點上等等)。但在某些情況下,你可能需要進一步控制 Pod 被部署到的節(jié)點。例如,確保 Pod 最終落在連接了 SSD 的機器上, 或者將來自兩個不同的服務(wù)且有大量通信的 Pods 被放置在同一個可用區(qū)。

你可以使用下列方法中的任何一種來選擇 Kubernetes 對特定 Pod 的調(diào)度:

  • 與節(jié)點標簽匹配的 nodeSelector
  • 親和性與反親和性
  • nodeName 字段

節(jié)點標簽 

與很多其他 Kubernetes 對象類似,節(jié)點也有標簽。 你可以手動地添加標簽。 Kubernetes 也會為集群中所有節(jié)點添加一些標準的標簽。 參見常用的標簽、注解和污點以了解常見的節(jié)點標簽。

Note:
這些標簽的取值是取決于云提供商的,并且是無法在可靠性上給出承諾的。 例如,?kubernetes.io/hostname? 的取值在某些環(huán)境中可能與節(jié)點名稱相同, 而在其他環(huán)境中會取不同的值。

節(jié)點隔離/限制 

通過為節(jié)點添加標簽,你可以準備讓 Pod 調(diào)度到特定節(jié)點或節(jié)點組上。 你可以使用這個功能來確保特定的 Pod 只能運行在具有一定隔離性,安全性或監(jiān)管屬性的節(jié)點上。

如果使用標簽來實現(xiàn)節(jié)點隔離,建議選擇節(jié)點上的 kubelet 無法修改的標簽鍵。 這可以防止受感染的節(jié)點在自身上設(shè)置這些標簽,進而影響調(diào)度器將工作負載調(diào)度到受感染的節(jié)點。

?NodeRestriction ?準入插件防止 kubelet 使用 ?node-restriction.kubernetes.io/? 前綴設(shè)置或修改標簽。

要使用該標簽前綴進行節(jié)點隔離:

  1. 確保你在使用節(jié)點鑒權(quán)機制并且已經(jīng)啟用了 NodeRestriction 準入插件。
  2. 將帶有 ?node-restriction.kubernetes.io/? 前綴的標簽添加到 Node 對象, 然后在節(jié)點選擇器中使用這些標簽。 例如,?example.com.node-restriction.kubernetes.io/fips=true? 或 ?example.com.node-restriction.kubernetes.io/pci-dss=true?。

nodeSelector

?nodeSelector ?是節(jié)點選擇約束的最簡單推薦形式。你可以將 ?nodeSelector ?字段添加到 Pod 的規(guī)約中設(shè)置你希望目標節(jié)點所具有的節(jié)點標簽。 Kubernetes 只會將 Pod 調(diào)度到擁有你所指定的每個標簽的節(jié)點上。

親和性與反親和性 

?nodeSelector ?提供了一種最簡單的方法來將 Pod 約束到具有特定標簽的節(jié)點上。 親和性和反親和性擴展了你可以定義的約束類型。使用親和性與反親和性的一些好處有:

  • 親和性、反親和性語言的表達能力更強。?nodeSelector ?只能選擇擁有所有指定標簽的節(jié)點。 親和性、反親和性為你提供對選擇邏輯的更強控制能力。
  • 你可以標明某規(guī)則是“軟需求”或者“偏好”,這樣調(diào)度器在無法找到匹配節(jié)點時仍然調(diào)度該 Pod。
  • 你可以使用節(jié)點上(或其他拓撲域中)運行的其他 Pod 的標簽來實施調(diào)度約束, 而不是只能使用節(jié)點本身的標簽。這個能力讓你能夠定義規(guī)則允許哪些 Pod 可以被放置在一起。

節(jié)點親和性 

節(jié)點親和性概念上類似于 ?nodeSelector?, 它使你可以根據(jù)節(jié)點上的標簽來約束 Pod 可以調(diào)度到哪些節(jié)點上。 節(jié)點親和性有兩種:

  • ?requiredDuringSchedulingIgnoredDuringExecution?: 調(diào)度器只有在規(guī)則被滿足的時候才能執(zhí)行調(diào)度。此功能類似于 ?nodeSelector?, 但其語法表達能力更強。
  • ?preferredDuringSchedulingIgnoredDuringExecution?: 調(diào)度器會嘗試尋找滿足對應(yīng)規(guī)則的節(jié)點。如果找不到匹配的節(jié)點,調(diào)度器仍然會調(diào)度該 Pod。
Note:
在上述類型中,?IgnoredDuringExecution ?意味著如果節(jié)點標簽在 Kubernetes 調(diào)度 Pod 時發(fā)生了變更,Pod 仍將繼續(xù)運行。

你可以使用 Pod 規(guī)約中的 ?.spec.affinity.nodeAffinity? 字段來設(shè)置節(jié)點親和性。 例如,考慮下面的 Pod 規(guī)約:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

在這一示例中,所應(yīng)用的規(guī)則如下:

  • 節(jié)點必須包含鍵名為 ?kubernetes.io/os? 的標簽,并且其取值為 ?linux?。
  • 節(jié)點 最好 具有鍵名為 ?another-node-label-key? 且取值為 ?another-node-label-value? 的標簽。

你可以使用 ?operator ?字段來為 Kubernetes 設(shè)置在解釋規(guī)則時要使用的邏輯操作符。 你可以使用 ?In?、?NotIn?、?Exists?、?DoesNotExist?、?Gt ?和 ?Lt ?之一作為操作符。

?NotIn ?和 ?DoesNotExist ?可用來實現(xiàn)節(jié)點反親和性行為。 你也可以使用節(jié)點污點 將 Pod 從特定節(jié)點上驅(qū)逐。

Note:
如果你同時指定了 ?nodeSelector ?和 ?nodeAffinity?,兩者 必須都要滿足, 才能將 Pod 調(diào)度到候選節(jié)點上。
如果你指定了多個與 ?nodeAffinity ?類型關(guān)聯(lián)的 ?nodeSelectorTerms?, 只要其中一個 ?nodeSelectorTerms ?滿足的話,Pod 就可以被調(diào)度到節(jié)點上。
如果你指定了多個與同一 ?nodeSelectorTerms ?關(guān)聯(lián)的 ?matchExpressions?, 則只有當所有 ?matchExpressions ?都滿足時 Pod 才可以被調(diào)度到節(jié)點上。

節(jié)點親和性權(quán)重 

你可以為 ?preferredDuringSchedulingIgnoredDuringExecution ?親和性類型的每個實例設(shè)置 ?weight ?字段,其取值范圍是 1 到 100。 當調(diào)度器找到能夠滿足 Pod 的其他調(diào)度請求的節(jié)點時,調(diào)度器會遍歷節(jié)點滿足的所有的偏好性規(guī)則, 并將對應(yīng)表達式的 ?weight ?值加和。

最終的加和值會添加到該節(jié)點的其他優(yōu)先級函數(shù)的評分之上。 在調(diào)度器為 Pod 作出調(diào)度決定時,總分最高的節(jié)點的優(yōu)先級也最高。

例如,考慮下面的 Pod 規(guī)約:

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

如果存在兩個候選節(jié)點,都滿足 ?requiredDuringSchedulingIgnoredDuringExecution ?規(guī)則, 其中一個節(jié)點具有標簽 ?label-1:key-1?,另一個節(jié)點具有標簽 ?label-2:key-2?, 調(diào)度器會考察各個節(jié)點的 ?weight ?取值,并將該權(quán)重值添加到節(jié)點的其他得分值之上。

Note:
如果你希望 Kubernetes 能夠成功地調(diào)度此例中的 Pod,你必須擁有打了 ?kubernetes.io/os=linux? 標簽的節(jié)點。

逐個調(diào)度方案中設(shè)置節(jié)點親和性 

FEATURE STATE: Kubernetes v1.20 [beta]

在配置多個調(diào)度方案時, 你可以將某個方案與節(jié)點親和性關(guān)聯(lián)起來,如果某個調(diào)度方案僅適用于某組特殊的節(jié)點時, 這樣做是很有用的。 要實現(xiàn)這點,可以在調(diào)度器配置中為 ?NodeAffinity ?插件的 ?args ?字段添加 ?addedAffinity?。例如:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

這里的 ?addedAffinity ?除遵從 Pod 規(guī)約中設(shè)置的節(jié)點親和性之外,還 適用于將 ?.spec.schedulerName? 設(shè)置為 ?foo-scheduler?。 換言之,為了匹配 Pod,節(jié)點需要滿足 ?addedAffinity ?和 Pod 的 ?.spec.NodeAffinity?。

由于 ?addedAffinity ?對最終用戶不可見,其行為可能對用戶而言是出乎意料的。 應(yīng)該使用與調(diào)度方案名稱有明確關(guān)聯(lián)的節(jié)點標簽。

Note:
DaemonSet 控制器為 DaemonSet 創(chuàng)建 Pods, 但該控制器不理會調(diào)度方案。 DaemonSet 控制器創(chuàng)建 Pod 時,默認的 Kubernetes 調(diào)度器負責放置 Pod, 并遵從 DaemonSet 控制器中奢侈的 ?nodeAffinity ?規(guī)則。

pod 間親和性與反親和性 

Pod 間親和性與反親和性使你可以基于已經(jīng)在節(jié)點上運行的 Pod 的標簽來約束 Pod 可以調(diào)度到的節(jié)點,而不是基于節(jié)點上的標簽。

Pod 間親和性與反親和性的規(guī)則格式為“如果 X 上已經(jīng)運行了一個或多個滿足規(guī)則 Y 的 Pod, 則這個 Pod 應(yīng)該(或者在反親和性的情況下不應(yīng)該)運行在 X 上”。 這里的 X 可以是節(jié)點、機架、云提供商可用區(qū)或地理區(qū)域或類似的拓撲域, Y 則是 Kubernetes 嘗試滿足的規(guī)則。

你通過標簽選擇算符 的形式來表達規(guī)則(Y),并可根據(jù)需要指定選關(guān)聯(lián)的名字空間列表。 Pod 在 Kubernetes 中是名字空間作用域的對象,因此 Pod 的標簽也隱式地具有名字空間屬性。 針對 Pod 標簽的所有標簽選擇算符都要指定名字空間,Kubernetes 會在指定的名字空間內(nèi)尋找標簽。

你會通過 ?topologyKey ?來表達拓撲域(X)的概念,其取值是系統(tǒng)用來標示域的節(jié)點標簽鍵。

Note:
Pod 間親和性和反親和性都需要相當?shù)挠嬎懔?,因此會在大?guī)模集群中顯著降低調(diào)度速度。 我們不建議在包含數(shù)百個節(jié)點的集群中使用這類設(shè)置。
Note:
Pod 反親和性需要節(jié)點上存在一致性的標簽。換言之, 集群中每個節(jié)點都必須擁有與 ?topologyKey ?匹配的標簽。 如果某些或者所有節(jié)點上不存在所指定的 ?topologyKey ?標簽,調(diào)度行為可能與預(yù)期的不同。

Pod 間親和性與反親和性的類型

與節(jié)點親和性類似,Pod 的親和性與反親和性也有兩種類型:

  • ?requiredDuringSchedulingIgnoredDuringExecution ?
  • ?preferredDuringSchedulingIgnoredDuringExecution ?

例如,你可以使用 ?requiredDuringSchedulingIgnoredDuringExecution ?親和性來告訴調(diào)度器, 將兩個服務(wù)的 Pod 放到同一個云提供商可用區(qū)內(nèi),因為它們彼此之間通信非常頻繁。 類似地,你可以使用 ?preferredDuringSchedulingIgnoredDuringExecution ?反親和性來將同一服務(wù)的多個 Pod 分布到多個云提供商可用區(qū)中。

要使用 Pod 間親和性,可以使用 Pod 規(guī)約中的 ?.affinity.podAffinity? 字段。 對于 Pod 間反親和性,可以使用 Pod 規(guī)約中的 ?.affinity.podAntiAffinity? 字段。

Pod 親和性示例 

考慮下面的 Pod 規(guī)約:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

本示例定義了一條 Pod 親和性規(guī)則和一條 Pod 反親和性規(guī)則。Pod 親和性規(guī)則配置為 ?requiredDuringSchedulingIgnoredDuringExecution?,而 Pod 反親和性配置為 ?preferredDuringSchedulingIgnoredDuringExecution?。

親和性規(guī)則表示,僅當節(jié)點和至少一個已運行且有 ?security=S1? 的標簽的 Pod 處于同一區(qū)域時,才可以將該 Pod 調(diào)度到節(jié)點上。 更確切的說,調(diào)度器必須將 Pod 調(diào)度到具有 ?topology.kubernetes.io/zone=V? 標簽的節(jié)點上,并且集群中至少有一個位于該可用區(qū)的節(jié)點上運行著帶有 ?security=S1? 標簽的 Pod。

反親和性規(guī)則表示,如果節(jié)點處于 Pod 所在的同一可用區(qū)且至少一個 Pod 具有 ?security=S2? 標簽,則該 Pod 不應(yīng)被調(diào)度到該節(jié)點上。 更確切地說, 如果同一可用區(qū)中存在其他運行著帶有 ?security=S2? 標簽的 Pod 節(jié)點, 并且節(jié)點具有標簽 ?topology.kubernetes.io/zone=R?,Pod 不能被調(diào)度到該節(jié)點上。

查閱設(shè)計文檔 以了解 Pod 親和性與反親和性的更多示例。

你可以針對 Pod 間親和性與反親和性為其 ?operator ?字段使用 ?In?、?NotIn?、?Exists?、 ?DoesNotExist ?等值。

原則上,?topologyKey ?可以是任何合法的標簽鍵。出于性能和安全原因,?topologyKey ?有一些限制:

  • 對于 Pod 親和性而言,在 ?requiredDuringSchedulingIgnoredDuringExecution ?和 ?preferredDuringSchedulingIgnoredDuringExecution ?中,?topologyKey ?不允許為空。
  • 對于 ?requiredDuringSchedulingIgnoredDuringExecution ?要求的 Pod 反親和性, 準入控制器 ?LimitPodHardAntiAffinityTopology ?要求 ?topologyKey ?只能是 ?kubernetes.io/hostname?。如果你希望使用其他定制拓撲邏輯, 你可以更改準入控制器或者禁用之。

除了 ?labelSelector ?和 ?topologyKey?,你也可以指定 ?labelSelector ?要匹配的命名空間列表,方法是在 ?labelSelector ?和 ?topologyKey ?所在層同一層次上設(shè)置 ?namespaces?。 如果 ?namespaces ?被忽略或者為空,則默認為 Pod 親和性/反親和性的定義所在的命名空間。

名字空間選擇算符 

FEATURE STATE: Kubernetes v1.24 [stable]

用戶也可以使用 ?namespaceSelector ?選擇匹配的名字空間,?namespaceSelector ?是對名字空間集合進行標簽查詢的機制。 親和性條件會應(yīng)用到 ?namespaceSelector ?所選擇的名字空間和 ?namespaces ?字段中 所列舉的名字空間之上。 注意,空的 ?namespaceSelector?(?{}?)會匹配所有名字空間,而 null 或者空的 ?namespaces ?列表以及 null 值 ?namespaceSelector ?意味著“當前 Pod 的名字空間”。

更實際的用例

Pod 間親和性與反親和性在與更高級別的集合(例如 ReplicaSet、StatefulSet、 Deployment 等)一起使用時,它們可能更加有用。 這些規(guī)則使得你可以配置一組工作負載,使其位于相同定義拓撲(例如,節(jié)點)中。

在下面的 Redis 緩存 Deployment 示例中,副本上設(shè)置了標簽 ?app=store?。 ?podAntiAffinity ?規(guī)則告訴調(diào)度器避免將多個帶有 ?app=store? 標簽的副本部署到同一節(jié)點上。 因此,每個獨立節(jié)點上會創(chuàng)建一個緩存實例。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

下面的 Deployment 用來提供 Web 服務(wù)器服務(wù),會創(chuàng)建帶有標簽 ?app=web-store? 的副本。 Pod 親和性規(guī)則告訴調(diào)度器將副本放到運行有標簽包含 ?app=store? Pod 的節(jié)點上。 Pod 反親和性規(guī)則告訴調(diào)度器不要在同一節(jié)點上放置多個 ?app=web-store? 的服務(wù)器。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine
    

創(chuàng)建前面兩個 Deployment 會產(chǎn)生如下的集群布局,每個 Web 服務(wù)器與一個緩存實例并置, 并分別運行在三個獨立的節(jié)點上。

node-1 node-2 node-3
webserver-1 webserver-2 webserver-3
cache-1 cache-2 cache-3

nodeName

?nodeName ?是比親和性或者 ?nodeSelector ?更為直接的形式。?nodeName ?是 Pod 規(guī)約中的一個字段。如果 ?nodeName ?字段不為空,調(diào)度器會忽略該 Pod, 而指定節(jié)點上的 kubelet 會嘗試將 Pod 放到該節(jié)點上。 使用 ?nodeName ?規(guī)則的優(yōu)先級會高于使用 ?nodeSelector ?或親和性與非親和性的規(guī)則。

使用 ?nodeName ?來選擇節(jié)點的方式有一些局限性:

  • 如果所指代的節(jié)點不存在,則 Pod 無法運行,而且在某些情況下可能會被自動刪除。
  • 如果所指代的節(jié)點無法提供用來運行 Pod 所需的資源,Pod 會失敗, 而其失敗原因中會給出是否因為內(nèi)存或 CPU 不足而造成無法運行。
  • 在云環(huán)境中的節(jié)點名稱并不總是可預(yù)測的,也不總是穩(wěn)定的。

下面是一個使用 ?nodeName ?字段的 Pod 規(guī)約示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

上面的 Pod 只能運行在節(jié)點 ?kube-01? 之上。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號