Kubernetes スケジューラのトラブルシューティング

このページでは、Google Distributed Cloud Virtual for Bare Metal の Kubernetes スケジューラ(kube-scheduler)の問題を解決する方法について説明します。

さらにサポートを必要とされる場合は、Cloud カスタマーケアにお問い合わせください。

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 です。

  • サービスを利用できません。 たとえば、高負荷のノードでホストされているウェブサーバーは、負荷が小さくなるまですべてのリクエストに 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 には、スケジューリングの動作を微調整できる機能が多数あります。こうした機能には、トポロジの分散の制約と反アフィニティ ルールが含まれます。こうした機能は 1 つでも、またそれを組み合わせても使用できます。定義した要件は kube-scheduler で AND で結合されます。

デフォルトのロギングの詳細レベルでは、スケジューラのログはキャプチャされません。トラブルシューティングのためにスケジューラ ログが必要な場合は、次の手順でスケジューラ ログをキャプチャします。

  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 を示します。

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 は、Pod をスケジュールする場所を計算するときに、新しい Deployment のロールアウトが古いリビジョンの Pod を考慮しないようにします。pod-template-hash ラベルは、Deployment によって自動的に入力されます。

Pod の反アフィニティ

Pod の反アフィニティでは、Pod を同じノードと同じ場所に配置できる制約を定義できます。

次のマニフェストの例は、反アフィニティを使用してレプリカをノードあたり 1 つの Pod に制限する Deployment を示しています。

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 など、クラスタがサポートしている場合は、他のラベルを使用することもできます。

コンテナ イメージを事前 pull する

他の制約がない場合、デフォルトでは、kube-scheduler はコンテナ イメージがすでにダウンロードされているノードで Pod をスケジューリングします。この動作は、すべてのノードでイメージをダウンロードできる、他のスケジューリング構成を持たない小規模なクラスタでは役立つ場合があります。ただし、この考え方に頼るのは最後の手段と捉えてください。より良い解決策は、nodeSelector、トポロジの分散の制約、またはアフィニティ / 反アフィニティを使用することです。詳細は、ノードに Pod を割り当てるをご覧ください。

コンテナ イメージがすべてのノードに事前 pull されるようにするには、次の例のように 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 カスタマーケアにお問い合わせください。