Kubernetes 提供多種排程策略,讓開發者可以精確控制 Pod 的佈署位置。本文將從最基本的 nodeNamenodeSelector 開始,逐步深入到更進階的 Node AffinityTaintsTolerations 等技術,並搭配例項說明如何根據不同的應用場景選擇合適的排程策略。瞭解這些策略,可以有效提升資源利用率,確保應用程式的高用性和效能。

Kubernetes Pod 排程高階技巧

Kubernetes 的 kube-scheduler 會根據特定的規則和組態值來選擇適合的節點(Node)執行 Pod。透過這些組態,你可以精確控制 kube-scheduler 如何選擇節點來執行 Pod。要控制 Pod 的執行位置,可以設定約束條件來限制其執行在特定的節點上,或指示優先選擇的節點。

為何需要影響 Pod 排程

通常情況下,Kubernetes 會有效地處理 Pod 的放置,確保 Pod 分散在不同的節點上以避免資源短缺。然而,在某些情況下,你可能需要影響 Pod 的排程,例如確保 Pod 執行在具備 SSD 的節點上,或將頻繁通訊的 Pod 放置在相同的可用區域內。

影響 Pod 排程的方法

你可以使用以下方法來影響 Kubernetes 中的 Pod 排程:

  • 使用 nodeSelector 欄位匹配節點標籤。
  • 設定親和性(affinity)與反親和性(anti-affinity)規則。
  • 指定 nodeName 欄位。
  • 定義 Pod 拓撲分佈約束(Pod topology spread constraints)。
  • 使用汙點(Taints)和容忍(Tolerations)。

設定多節點 Kubernetes 叢集

在進行實作之前,請確保你擁有一個多節點的 Kubernetes 叢集。以下示範如何使用 minikube 建立一個多節點叢集:

$ minikube start \
--driver=virtualbox \
--nodes 3 \
--cni calico \
--cpus=2 \
--memory=2g \
--kubernetes-version=v1.30.0 \
--container-runtime=containerd

執行上述指令後,你將獲得一個包含三個節點的 Kubernetes 叢集。使用 kubectl get nodes 指令檢視節點狀態:

$ kubectl get nodes
NAME           STATUS   ROLES           AGE   VERSION
minikube       Ready    control-plane   3m34s v1.30.0
minikube-m02   Ready    <none>          2m34s v1.30.0
minikube-m03   Ready    <none>          87s   v1.30.0

管理節點親和性

要了解 Kubernetes 中的節點親和性是如何工作的,首先需要檢視最基本的排程選項,即使用 nodeNamenodeSelector

使用 nodeName 排程 Pod

每個 Pod 物件都有一個 nodeName 欄位,通常由 kube-scheduler 控制。然而,你可以在建立 Pod 或使用 Pod 範本建立控制器時,直接在 YAML 清單中設定此屬性。這是靜態排程 Pod 到特定節點的最簡單形式,但不具備彈性且無法擴充套件。

以下是一個 Deployment 物件的範例,展示瞭如何使用 nodeName 將五個 nginx Pod 排程到 minikube-m02 節點上:

# 01_nodename/nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 5
  selector:
    matchLabels:
      app: nginx
      environment: test
  template:
    metadata:
      labels:
        app: nginx
        environment: test
    spec:
      nodeName: minikube-m02  # 指定節點名稱
      containers:
      - name: nginx
        image: nginx:1.17
        ports:
        - containerPort: 80

套用上述 Deployment YAML 後,使用 kubectl get pods 指令檢視 Pod 的狀態和節點名稱:

$ kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName"
NAME                     STATUS    NODE
nginx-app-7b547cfd87-4g9qx Running minikube
nginx-app-7b547cfd87-m76l2 Running minikube-m02
nginx-app-7b547cfd87-mjf78 Running minikube-m03
nginx-app-7b547cfd87-vvrgk Running minikube-m02
nginx-app-7b547cfd87-w7jcw Running minikube-m03

詳細解說

  1. nodeName 的作用:直接指定 Pod 要執行的節點名稱,繞過 kube-scheduler 的排程邏輯。
  2. 優缺點:簡單直接,但缺乏彈性和自動化排程能力,不適合動態環境。
  3. 適用場景:除錯或特定測試場景,需要將 Pod 固定在特定節點上。

Kubernetes 中的 Pod 排程技術

在 Kubernetes 中,Pod 的排程是一個重要的議題。預設情況下,Kubernetes 的 kube-scheduler 會根據預設的排程策略來分配 Pod 到不同的 Node 上。在本章中,我們將探討如何使用 nodeNamenodeSelector 來控制 Pod 的排程。

使用 nodeName 強制排程 Pod

首先,我們來看看如何使用 nodeName 來強制排程 Pod 到特定的 Node 上。在 Deployment 的 Pod 範本中,我們可以指定 nodeName 欄位來強制將 Pod 排程到指定的 Node 上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  ...
  template:
    ...
    spec:
      nodeName: minikube-m02

在這個例子中,我們將 nodeName 設定為 minikube-m02,這意味著所有的 Pod 都將被排程到 minikube-m02 這個 Node 上。

內容解密:

  • nodeName 欄位用於指定 Pod 應該被排程到哪個 Node 上。
  • 當我們設定了 nodeName 後,kube-scheduler 將會忽略其他的排程策略,直接將 Pod 排程到指定的 Node 上。
  • 這種方式可以讓我們強制控制 Pod 的排程,但也需要謹慎使用,以避免資源分配不均的問題。

使用 nodeSelector 排程 Pod

除了 nodeName 之外,我們還可以使用 nodeSelector 來根據 Node 的標籤(label)來排程 Pod。nodeSelector 允許我們指定一個或多個標籤,只有具備這些標籤的 Node 才會被考慮用於排程 Pod。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  ...
  template:
    ...
    spec:
      nodeSelector:
        node-type: superfast

在這個例子中,我們設定了 nodeSelector{node-type: superfast},這意味著只有具備 node-type=superfast 標籤的 Node 才會被用於排程 Pod。

內容解密:

  • nodeSelector 用於根據 Node 的標籤來篩選適合排程的 Node。
  • 只有當 Node 的標籤與 nodeSelector 中指定的標籤相符時,該 Node 才會被考慮用於排程 Pod。
  • 這種方式提供了一種更靈活的方式來控制 Pod 的排程,可以根據不同的需求來設定不同的標籤。

為 Node 新增自定義標籤

在 Kubernetes 中,每個 Node 都有一組預設的標籤,例如 kubernetes.io/archkubernetes.io/os。除了這些預設的標籤之外,我們還可以為 Node 新增自定義的標籤。

$ kubectl label nodes minikube-m02 node-type=superfast
$ kubectl label nodes minikube-m03 node-type=superfast

在這個例子中,我們為 minikube-m02minikube-m03 這兩個 Node 增加了 node-type=superfast 的標籤。

內容解密:

  • 使用 kubectl label 命令可以為 Node 新增自定義的標籤。
  • 自定義標籤可以用於 nodeSelector 中,以控制 Pod 的排程。
  • 為 Node 新增合適的標籤可以幫助我們更好地管理和排程資源。

進階 Pod 排程技術:Node Affinity 的應用

在 Kubernetes 中,Pod 的排程是一個重要的議題。除了基本的 nodeSelector 之外,Kubernetes 還提供了更進階的排程技術,例如 Node Affinity。本篇文章將探討 Node Affinity 的概念、組態方法及其在實際應用中的場景。

為什麼需要 Node Affinity?

在之前的章節中,我們已經瞭解瞭如何使用 nodeSelector 將 Pod 指派到特定的 Node 上。然而,nodeSelector 的功能相對有限,只能進行簡單的標籤匹配。Node Affinity 則提供了更豐富的語言來定義 Pod 與 Node 之間的關聯,使得排程規則更加靈活和強大。

Node Affinity 的優勢

nodeSelector 相比,Node Affinity 具有以下優勢:

  1. 更豐富的匹配規則:Node Affinity 支援多種運算元,如 InNotInExistsDoesNotExistGtLt,使得標籤匹配更加靈活。
  2. 軟性與硬性規則:可以定義軟性(soft)規則和硬性(hard)規則。軟性規則代表一種偏好,而非強制性要求。
  3. 權重組態:軟性規則可以設定權重,讓排程器在決策時綜合考量多個因素。

如何組態 Node Affinity?

以下是一個範例,展示如何使用 Node Affinity 來定義 Pod 的排程規則。假設我們有一個 Deployment,需要將 Pod 排程到具有 node-type 標籤且值為 fastsuperfast 的 Node 上,但嚴格避免排程到具有 node-type=extremelyslow 的 Node 上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 5
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80
            preference:
              matchExpressions:
              - key: node-type
                operator: In
                values:
                - fast
                - superfast
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-type
                operator: NotIn
                values:
                - extremelyslow
      containers:
      - name: nginx
        image: nginx:latest

內容解密:

  1. preferredDuringSchedulingIgnoredDuringExecution:定義軟性規則,優先將 Pod 排程到具有 node-type=fastnode-type=superfast 的 Node 上。這裡設定了權重 80,表示排程器會傾向於滿足這個條件。
  2. requiredDuringSchedulingIgnoredDuringExecution:定義硬性規則,確保 Pod 不會被排程到具有 node-type=extremelyslow 的 Node 上。這是透過 NotIn 運算元實作的。

實際操作與結果分析

  1. 將上述組態套用到 Kubernetes 叢集:

    kubectl apply -f nginx-deployment.yaml
    
  2. 檢視 Pod 的狀態和 Node 分配情況:

    kubectl get pods -o custom-columns="NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName"
    

    結果顯示,Pod 主要被排程到符合 node-type=fastnode-type=superfast 的 Node 上,並且避免了具有 node-type=extremelyslow 的 Node。

Plantuml 圖表說明

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes Pod 排程策略詳解

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

此圖示展示了 Pod 如何透過 Node Affinity 被排程到符合特定條件的 Node 上。優先選擇具有 node-type=fastsuperfast 的 Node,同時避免具有 node-type=extremelyslow 的 Node。

高階Pod排程技術:Node親和性(Affinity)與反親和性(Anti-Affinity)實踐

在Kubernetes叢集中,合理地排程Pod至適當的Node上是確保應用效能和資源利用率的關鍵。本章節將探討如何使用Node親和性(Node Affinity)與反親和性(Node Anti-Affinity)來控制Pod的排程。

Node親和性與反親和性組態

為了展示Node親和性與反親和性的工作原理,我們首先為叢集中的Node設定特定的標籤。假設我們有三個Node:Node1、Node2和Node3,分別具有slowfastsuperfastnode-type標籤。我們的目標是讓Deployment的Pod優先排程到fastsuperfast的Node上,同時嚴格避免排程到具有extremelyslow標籤的Node。

首先,我們使用kubectl label nodes命令為Node新增或更新node-type標籤:

$ kubectl label nodes --overwrite minikube node-type=slow
$ kubectl label nodes --overwrite minikube-m02 node-type=fast
$ kubectl label nodes --overwrite minikube-m03 node-type=superfast

接下來,我們需要在Deployment的YAML檔案中定義Node親和性規則。以下是一個範例組態:

# 03_affinity/nginx-deployment.yaml
spec:
  template:
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-type
                operator: NotIn
                values:
                - extremelyslow
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: node-type
                operator: In
                values:
                - fast
                - superfast

內容解密:

  1. nodeAffinity定義了Node親和性規則,用於控制Pod排程到哪些Node上。
  2. requiredDuringSchedulingIgnoredDuringExecution定義了一個硬性條件:Pod不能被排程到具有extremelyslow標籤的Node上。
  3. preferredDuringSchedulingIgnoredDuringExecution定義了一個軟性條件:Pod優先被排程到具有fastsuperfast標籤的Node上。如果這些Node資源不足,Pod可以被排程到其他Node上。

實驗驗證

  1. 套用Deployment組態並觀察Pod的狀態和Node分配:

    $ kubectl apply -f 03_affinity/nginx-deployment.yaml
    $ kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName"
    

    結果顯示Pod被成功排程到fastsuperfast的Node上。

  2. 修改Node標籤,將Node2和Node3標記為extremelyslow,並重新佈署Deployment:

    $ kubectl label nodes --overwrite minikube-m02 node-type=extremelyslow
    $ kubectl label nodes --overwrite minikube-m03 node-type=extremelyslow
    $ kubectl rollout restart deployment nginx-app
    
  3. 再次檢查Pod的狀態和Node分配:

    $ kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName"
    

    由於硬性條件的限制,Pod被排程到唯一的可用Node(Node1,具有slow標籤)上。如果Node1資源不足,Pod將保持在Pending狀態。