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

このページでは、Google Distributed Cloud の 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 には、スケジューリング動作を微調整できる機能がいくつかあります。こうした機能には、トポロジの分散の制約とアンチ アフィニティ ルールが含まれます。こうした機能は単独で使用することも、組み合わせて使用することもできます。定義した要件は、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 と一致します。
  • topologyKeykubernetes.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 と一致します。
  • topologyKeykubernetes.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 カスタマーケアにお問い合わせください。