# 將 Pod 指派給 Node 今天來看 Scheduler 分配 Pods 到 Node 的一些依據。在這之前需要先認識 [Label 和 Annotation 的用法](https://hackmd.io/@6j0OMC7UQbGqQLfUg9pauA/rkgWcv7Yda)。 您可以限制 Pod,讓它只能在特定 Node 上運行,或**更傾向**在特定 Node 上運行。有多種方法可以做到這一點,建議的方法都使用 [Label Selector](https://hackmd.io/@6j0OMC7UQbGqQLfUg9pauA/rkgWcv7Yda#Label-selectors) 來方便選擇。 通常,您不需要設定任何此類限制;Scheduler 會自動進行合理的放置(例如,將 Pod 分散到各個 Node 上,以免將 Pod 放置在可用資源不足的 Node 上)。但是,在某些情況下,您可能想要控制 Pod 部署到哪個 Node,例如,確保 Pod 最終位於連接了 SSD 的 Node 上,或是有 GPU 裝置的 Node。 您可以使用以下任何方法來選擇 Kubernetes 調度特定 Pod 的位置: + **`nodeSelector`** 字段與 Node Labels 匹配 + Affinity and anti-affinity + **`nodeName`** 字段 + Pod topology spread constraints(本教程不會示範,請參閱[官方文件](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)) ## nodeSelector **`nodeSelector`** 是最簡單的 Node 選擇限制推薦形式。您可以將 **`nodeSelector`** 字段新增至 Pod **`spec`** 中,並指定您希望目標 Node 具有的 Node Labels。Kubernetes 僅將 Pod 調度到具有您指定的每個 Label 的 Node 上。 ```yaml apiVersion: v1 kind: Pod metadata: name: node-selector-demo spec: containers: - name: nginx image: nginx ports: - containerPort: 80 nodeSelector: role: agent # ===>> here <<=== ``` 這裡的 key-value 會是 Node 身上的 Label。因此必須要有一個 Node 擁有 **`role`** 這個 key 並且 value 等於 **`agent`**,這個 Pod 才會被分配到該 Node 上。 ### Node labels 幫 Node 增加 Label 的指令 **`kubectl label nodes <node-name> <label-key>=<label-value>`**: ```bash kubectl label nodes k3d-mycluster-agent-0 role=agent # node/k3d-mycluster-agent-0 labeled ``` 顯示 Node 及其 Label: ```bash kubectl get nodes --show-labels # NAME STATUS ROLES AGE VERSION LABELS # k3d-mycluster-server-0 Ready control-plane,master 8d v1.27.4+k3s1 ...,kubernetes.io/hostname=k3d-mycluster-server-0 # k3d-mycluster-agent-1 Ready <none> 8d v1.27.4+k3s1 ...,kubernetes.io/hostname=k3d-mycluster-agent-1 # k3d-mycluster-agent-0 Ready <none> 8d v1.27.4+k3s1 ...,role=agent,kubernetes.io/hostname=k3d-mycluster-agent-0 ``` 不過使用 **`nodeSelector`**,我們只能指定單一個條件,如果我們希望更彈性的去安排這些 Pods 呢? 接下來就是介紹 - Affinity and anti-affinity。 ## Affinity and anti-affinity **`nodeSelector`** 是將 Pod 限制到具有特定 Label 的 Node 的最簡單方法。Affinity 和 anti-affinity 擴展了您可以定義的限制類型。Affinity 和 anti-affinity 的一些好處包括: + Affinity/anti-affinity 功能的表達方式更加豐富。**`nodeSelector`** 只選擇具有所有特定 Label 的 Node。Affinity/anti-affinit 為您提供了更多控制在選擇邏輯的能力。 + 您可以指定規則是 *soft* 或 *preferred*,以便 Scheduler 即使找不到匹配的 Node,仍然會調度 Pod。 + 您可以使用 Node 上運行的其他 Pod 的 Label(或其他拓撲域的 Label)來限制 Pod,而不僅僅是使用 Node Label,這允許您定義哪些 Pod 可以共同位於一個 Node 上的規則。 Affinity 特徵由兩種類型的 affinity 組成: + *Node affinity* 的功能類似於 **`nodeSelector`** 字段,但表達能力更強,允許您指定 soft 規則。 + *Inter-pod affinity/anti-affinity* 允許您根據其他 Pod 上的 Label 來限制 Pod。(本教程不會示範,請參閱[官方文件](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity)) ### Node affinity Node affinity 在概念上類似於 **`nodeSelector`**,讓您可以根據 Node Label 來限制您的 Pod 可以調度到哪些 Node 上。Node affinity 有兩種類型: + **`requiredDuringSchedulingIgnoredDuringExecution`**:除非滿足規則,否則 Scheduler 無法調度 Pod。它的功能類似於 **`nodeSelector`**,但使用更具表達力的語法。 + **`preferredDuringSchedulingIgnoredDuringExecution`**:Scheduler 嘗試找到滿足規則的 Node。如果沒有相符的 Node,Scheduler 仍會調度 Pod。 :::success :notebook: **Note:** 在上述類型中,IgnoredDuringExecution 表示如果 Kubernetes 調度 Pod 後 Node Label 發生變化,則該 Pod 繼續運作。 ::: 您可以使用 Pod **`spec`** 中的 **`.spec.affinity.nodeAffinity`** 字段指定 Node affinities。 例如,考慮以下 Pod **`spec`**: ```yaml apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - antarctica-east1 - antarctica-west1 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: registry.k8s.io/pause:2.0 ``` 在此範例中,適用以下規則: + 該 Node **必須**有一個帶有 key **`topology.kubernetes.io/zone`** 的 Label,並且該 Label 的 value 必須是 **`antarctica-east1`** 或 **`antarctica-west1`**。 + 此 Node **傾向**具有 key **`another-node-label-key`** 和 value **`another-node-label-value`** 的 Label。 您可以使用 **`operator`** 字段指定 Kubernetes 在解釋規則時使用的邏輯運算子。您可以使用 **`In`**、**`NotIn`**、**`Exists`**、**`DoesNotExist`**、**`Gt`** 和 **`Lt`**。 **`NotIn`** 和 **`DoesNotExist`** 可讓您定義 Node anti-affinity 行為。或者,您可以使用 Node taints 來排斥特定 Node 的 Pod。 :::success :notebook: **Note:** 如果您同時指定了 **`nodeSelector`** 和 **`nodeAffinity`**,則必須**同時**滿足這兩個條件才能將 Pod 調度到 Node 上。 如果您在與 **`nodeAffinity`** 類型相關的 **`nodeSelectorTerms`** 中指定了多個規則,那麼如果能滿足其中一個指定的規則,Pod 就可以被調度到一個 Node 上(規則之間是以 OR 的方式組合的)。 ```yaml nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: ... - matchExpressions: ... ``` 如果您在與 **`nodeSelectorTerms`** 中某個規則相關的單個 **`matchExpressions`** 字段中指定了多個表達式,那麼只有當所有這些表達式都被滿足時(表達式之間是以 AND 的方式組合的),Pod 才能被調度到一個 Node 上。 ```yaml nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: ... ... - key: ... ... ``` ::: ### Node affinity weight **`preferredDuringSchedulingIgnoredDuringExecution`** 的部分還能夠設定 **`weight`** 要多少,可設定 1-100 之間。這個 **`weight`** 會被 Scheduler 拿來計算分數,當 Pod 要被分配時會優先放到分數最高的 Node。 ```yaml 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 ``` 如果有兩個可能的 Node 與 **`PreferredDuringSchedulingIgnoredDuringExecution`** 規則匹配,一個具有 **`label-1:key-1`** Label,另一個具有 **`label-2:key-2`** Label,則 Scheduler 會考慮每個 Node 的 **`weight`**,並將權重添加到該 Node 的其他分數,並將 Pod 調度到最終分數最高的 Node。 ## nodeName **`nodeName`** 是比 affinity 或 **`nodeSelector`** 更直接的 Node 選擇形式。**`nodeName`** 是 Pod **`spec`** 中的一個字段。如果 **`nodeName`** 字段不為空,則 Scheduler 會忽略該 Pod,並且指定 Node 上的 kubelet 會嘗試將 Pod 放置在該 Node 上。使用 **`nodeName`** 會否決使用 **`nodeSelector`** 或 affinity 和 anti-affinity 規則。 使用 **`nodeName`** 選擇 Node 的一些限制是: + 如果指定的 Node 不存在,Pod 將不會運行,並且在某些情況下可能會自動刪除。 + 如果指定的 Node 沒有足夠的資源來容納該 Pod,則 Pod 會因無法部署而失敗,其失敗原因會被指明,例如記憶體不足(OutOfmemory)或 CPU 資源不足(OutOfcpu)。 + 雲端環境中的 Node 名稱並不總是可預測或穩定的。 :::success :notebook: **Note:** **`nodeName`** 用於自定義 Scheduler 或當您需要避開已設定的 Scheduler 時的進階應用情境。但如果直接指定 Node 而不經過 Scheduler,可能會因為 Node 資源不足而導致 Pod 啟動失敗。您可以使用 Node affinity 或 **`nodeselector`** 字段來安排 Pod 到特定 Node,而無需繞過 Scheduler。 ::: 以下是使用 **`nodeName`** 字段的 Pod **`spec`** 範例: ```yaml apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx nodeName: kube-01 ``` 上面的 Pod 只會運行在 Node **`kube-01`** 上。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up