排解 Kubernetes 排程器問題

本頁說明如何解決 Google Distributed Cloud 的 Kubernetes 排程器 (kube-scheduler) 問題。

Kubernetes 一律會將 Pod 排程至同一組節點

這個錯誤可能會以幾種不同的方式呈現:

  • 叢集使用率不平衡。您可以使用 kubectl top nodes 指令,檢查每個節點的叢集用量。以下誇大的範例輸出內容顯示特定節點的使用率明顯偏高:

    NAME                   CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
    XXX.gke.internal       222m         101%       3237Mi          61%
    YYY.gke.internal       91m          0%         2217Mi          0%
    ZZZ.gke.internal       512m         0%         8214Mi          0%
    
  • 要求數量過多。如果您一次在同一個節點上排定大量 Pod,且這些 Pod 會發出 HTTP 要求,節點可能會受到速率限制。在這種情況下,伺服器通常會傳回 429 Too Many Requests 錯誤。

  • 服務無法使用。舉例來說,如果 Node 上的網路伺服器負載過高,可能會對所有要求傳回 503 Service Unavailable 錯誤,直到負載降低為止。

如要檢查是否有 Pod 一律排定在相同節點上,請按照下列步驟操作:

  1. 執行下列 kubectl 指令,查看 Pod 的狀態:

    kubectl get pods -o wide -n default
    

    如要查看 Pod 在節點間的分布情形,請檢查輸出內容中的 NODE 欄。在下列範例輸出中,所有 Pod 都排定在同一個節點上:

    NAME                               READY  STATUS   RESTARTS  AGE  IP             NODE
    nginx-deployment-84c6674589-cxp55  1/1    Running  0         55s  10.20.152.138  10.128.224.44
    nginx-deployment-84c6674589-hzmnn  1/1    Running  0         55s  10.20.155.70   10.128.226.44
    nginx-deployment-84c6674589-vq4l2  1/1    Running  0         55s  10.20.225.7    10.128.226.44
    

Pod 提供多項功能,可供您微調排程行為。這些功能包括拓撲擴散限制和反親和性規則。您可以擇一或任意搭配使用這些功能。您定義的要求會由 kube-scheduler 連結在一起。

在預設記錄細項等級下,系統不會擷取排程器記錄。如需排程器記錄檔進行疑難排解,請按照下列步驟擷取排程器記錄檔:

  1. 提高記錄詳細程度:

    1. 編輯 kube-scheduler Deployment:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. spec.containers.command 區段下方新增 --v=5 標記:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. 完成疑難排解後,請將詳細程度重設回預設層級:

    1. 編輯 kube-scheduler Deployment:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. 將詳細程度設回預設值:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      

拓撲分散限制

拓撲分散限制可用於根據節點的 zonesregionsnode 或其他自訂拓撲,在節點之間平均分配 Pod。

以下資訊清單範例顯示 Deployment,該 Deployment 會使用拓撲散布限制,在所有可排程的節點之間平均散布副本:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      topologySpreadConstraints:
      - maxSkew: 1 # Default. Spreads evenly. Maximum difference in scheduled Pods per Node.
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule # Default. Alternatively can be ScheduleAnyway
        labelSelector:
          matchLabels:
            app: myapp
        matchLabelKeys: # beta in 1.27
        - pod-template-hash
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

使用拓撲分散限制時,請注意下列事項:

  • Pod 的 labels.app: myapp 會與限制的 labelSelector 相符。
  • topologyKey 會指定 kubernetes.io/hostname。這個標籤會自動附加至所有節點,並填入節點的主機名稱。
  • matchLabelKeys 可防止新 Deployment 推出時,在計算 Pod 的排程位置時,將舊修訂版本的 Pod 納入考量。Deployment 會自動填入 pod-template-hash 標籤。

Pod 反相依性

Pod 反相依性可讓您定義限制,說明哪些 Pod 可以共用同一節點。

以下資訊清單範例顯示的 Deployment 使用反相依性,將副本限制為每個節點一個 Pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: with-pod-affinity
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          # requiredDuringSchedulingIgnoredDuringExecution
          # prevents Pod from being scheduled on a Node if it
          # does not meet criteria.
          # Alternatively can use 'preferred' with a weight
          # rather than 'required'.
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            # Your nodes might be configured with other keys
            # to use as `topologyKey`. `kubernetes.io/region`
            # and `kubernetes.io/zone` are common.
            topologyKey: kubernetes.io/hostname
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

這個 Deployment 範例指定了 30 個副本,但只會擴充至叢集中的可用節點數量。

使用 Pod 反相依性時,請注意下列事項:

  • Pod 的 labels.app: myapp 會與限制的 labelSelector 相符。
  • topologyKey 會指定 kubernetes.io/hostname。這個標籤會自動附加至所有節點,並填入節點的主機名稱。如果叢集支援其他標籤,您可以選擇使用這些標籤,例如 regionzone

預先提取容器映像檔

如果沒有其他限制,根據預設,kube-scheduler 會優先將 Pod 排程到已下載容器映像檔的節點上。如果叢集較小,且沒有其他排程設定,可能就會需要這種行為,因為這樣就能在每個節點上都下載映像檔。不過,這項概念應視為最後手段。更好的解決方法是使用 nodeSelector、拓撲擴散限制,或親和性 / 反親和性。詳情請參閱「將 Pod 指派給節點」。

如要確保容器映像檔預先提取至所有節點,可以使用 DaemonSet,例如:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: prepulled-images
spec:
  selector:
    matchLabels:
      name: prepulled-images
  template:
    metadata:
      labels:
        name: prepulled-images
    spec:
      initContainers:
        - name: prepulled-image
          image: IMAGE
          # Use a command the terminates immediately
          command: ["sh", "-c", "'true'"]
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Pod 在所有節點上 Running 後,請再次重新部署 Pod,確認容器現在是否平均分配到各個節點。

後續步驟

如需其他協助,請與 Cloud Customer Care 團隊聯絡。如要進一步瞭解支援資源,包括下列項目,請參閱「取得支援」: