Dynamic Workload Scheduler を使用してバッチ ワークロードと AI ワークロード用の GPU をデプロイする


このページでは、Dynamic Workload Scheduler と ProvisioningRequest を使用して、GPU を使用する大規模なバッチ ワークロードと AI ワークロードの GPU の取得を最適化する方法について説明します。

ベスト プラクティス:

Dynamic Workload Scheduler は、定義された GPU 容量管理条件を使用してオフピーク時に実行できる大規模なバッチ ワークロードと AI ワークロードに使用します。これには、ディープ ラーニング モデルのトレーニングや、多数の GPU が同時に作成されるシミュレーションなどのワークロードが含まれます。

Google Kubernetes Engine(GKE)で Dynamic Workload Scheduler を使用せずに GPU ワークロードを実行するには、GKE Standard ノードプールで GPU を実行するをご覧ください。

Dynamic Workload Scheduler を使用する場合

ワークロードが次のすべての条件を満たしている場合は、Dynamic Workload Scheduler を使用することをおすすめします。

  • ワークロードを実行するために GPU をリクエストする。
  • 予約済みの GPU 容量が限られているかゼロであり、GPU リソースの入手可能性を改善したい。
  • ワークロードに時間的な制約がなく、リクエストしたすべての容量を取得するまで待つことが可能なユースケースである。たとえば、ピーク時以外に GPU リソースを GKE が割り当てる場合などです。
  • ワークロードに複数のノードが必要であり、すべての GPU ノードがプロビジョニングされて同時に準備が整うまでワークロードの実行を開始できない(分散 ML トレーニングなど)。

始める前に

作業を始める前に、次のことを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。

Dynamic Workload Scheduler でノードプールを使用する

このセクションは Standard クラスタにのみ適用されます。

次の 3 つの方法のいずれかを使用して、Dynamic Workload Scheduler がクラスタ内の特定のノードプールで動作するように指定できます。

ノードプールを作成する

gcloud CLI を使用して、Dynamic Workload Scheduler が有効なノードプールを作成します。

gcloud container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
     --enable-queued-provisioning \
    --accelerator type=GPU_TYPE,count=AMOUNT,gpu-driver-version=DRIVER_VERSION \
    --machine-type=MACHINE_TYPE \
    --enable-autoscaling  \
    --num-nodes=0   \
    --total-max-nodes TOTAL_MAX_NODES  \
    --location-policy=ANY  \
    --reservation-affinity=none  \
    --no-enable-autorepair

次のように置き換えます。

  • NODEPOOL_NAME: ノードプールに付ける名前。
  • CLUSTER_NAME: クラスタの名前。
  • LOCATION: クラスタの Compute Engine リージョン(us-central1 など)。
  • GPU_TYPE: GPU タイプ
  • AMOUNT: ノードプール内のノードに接続する GPU の数。
  • DRIVER_VERSION: インストールする NVIDIA ドライバのバージョン。次のいずれか 1 つを指定できます。
    • default: GKE バージョンに対応するデフォルトのドライバ バージョンをインストールします。
    • latest: GKE バージョンに対応する最新のドライバ バージョンをインストールします。Container-Optimized OS を使用するノードでのみ指定できます。
  • TOTAL_MAX_NODES: ノードプール全体における、ノードが自動スケーリングする最大数。
  • MACHINE_TYPE: ノードの Compute Engine マシンタイプ。

    ベスト プラクティス:

    アクセラレータ最適化マシンタイプを使用して、AI/ML ワークロードのパフォーマンスと効率を向上させます。

必要に応じて、次のフラグを使用できます。

  • --no-enable-autoupgrade: 推奨。ノードの自動アップグレードを無効にします。リリース チャンネルに登録されていない GKE クラスタでのみサポートされます。詳細については、既存のノードプールのノードの自動アップグレードを無効にするをご覧ください。
  • --node-locations=COMPUTE_ZONES: GKE が GPU ノードを作成する 1 つ以上のゾーンのカンマ区切りリスト。このゾーンはクラスタと同じリージョンに存在する必要があります。使用可能な GPU があるゾーンを選択します。
  • --enable-gvnic: このフラグにより、GPU ノードプールの gVNIC が有効になり、ネットワーク トラフィック速度が向上します。

このコマンドを実行すると、次の構成のノードプールが作成されます。

  • キューに格納されたプロビジョニングとクラスタの自動スケーリングが有効です。
  • ノードプールの初期ノード数はゼロです。
  • --enable-queued-provisioning フラグにより、Dynamic Workload Scheduler が有効になり、ノードプールに cloud.google.com/gke-queued taint が追加されます。
  • --no-enable-autorepair フラグと --no-enable-autoupgrade フラグにより、ノードの自動修復と自動アップグレード(修復 / アップグレード対象のノードで実行中のワークロードが中断される可能性がある)が無効になります。ノードの自動アップグレードは、リリース チャンネルに登録されていないクラスタでのみ無効にできます。

既存のノードプールを更新して Dynamic Workload Scheduler を有効にする

既存のノードプールで Dynamic Workload Scheduler を有効にするノードプールを正しく構成するための前提条件を確認します。

前提条件

  • --reservation-affinity=none フラグを指定してノードプールを作成します。ノードプールの作成後に予約のアフィニティを変更できないため、このフラグは後で Dynamic Workload Scheduler を有効にするために必要です。

  • クラスタが正常に機能するように、Dynamic Workload Scheduler 処理を有効にしていないノードプールを少なくとも 1 つ維持するようにしてください。

  • ノードプールが空であることを確認します。ノードプールのサイズを変更して、ノード数を 0 に設定できます。

  • 自動スケーリングが有効になっており、正しく構成されていることを確認します。

  • 自動修復が無効になっていることを確認します。

既存のノードプールで Dynamic Workload Scheduler を有効にする

既存のノードプールに対して Dynamic Workload Scheduler を有効にするには、gcloud CLI を使用します。

gcloud container node-pools update NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
     --enable-queued-provisioning

次のように置き換えます。

  • NODEPOOL_NAME: 選択したノードプールの名前。
  • CLUSTER_NAME: クラスタの名前。
  • LOCATION: クラスタの Compute Engine リージョン(us-central1 など)。

このノードプールの更新コマンドを実行すると、次の構成が変更されます。

  • --enable-queued-provisioning フラグにより、Dynamic Workload Scheduler が有効になり、ノードプールに cloud.google.com/gke-queued taint が追加されます。

必要に応じて、次のノードプール設定を更新することもできます。

ベスト プラクティス:

Dynamic Workload Scheduler を使用する場合は、次の操作を行います。

ノードの自動プロビジョニングを有効にして、Dynamic Workload Scheduler のノードプールを作成する

ノードの自動プロビジョニングを使用すると、バージョン 1.29.2-gke.1553000 以降で稼働しているクラスタで Dynamic Workload Scheduler に対応するノードプールを管理できます。ノードの自動プロビジョニングを有効にして Dynamic Workload Scheduler を有効にすると、GKE により、関連するワークロードに必要なリソースを含むノードプールが作成されます。

ノードの自動プロビジョニングを有効にするには、次の設定を考慮したうえで、GPU の上限を構成するの手順に沿って操作します。

Dynamic Workload Scheduler でバッチ ワークロードと AI ワークロードを実行する

Dynamic Workload Scheduler でバッチ ワークロードを実行するには、次のいずれかの構成を使用します。

  • Kueue を使用する Job の Dynamic Workload Scheduler: Dynamic Workload Scheduler を Kueue組み合わせて使用すると、Dynamic Workload Scheduler リクエストのライフサイクルを自動化できます。Kueue は Job のキューイングを実装し、Dynamic Workload Scheduler のステータスをモニタリングします。Kueue は、チーム間でリソースを公平に共有するための割り当てと階層に基づいて、Job を待機するタイミングと開始するタイミングを決定します。

  • Kueue を使用しない Job の Dynamic Workload Scheduler: 独自の内部バッチ スケジューリング ツールまたはプラットフォームを使用する場合は、Kueue なしで Dynamic Workload Scheduler を使用できます。Dynamic Workload Scheduler リクエストを手動でモニタリングしてキャンセルします。

ベスト プラクティス:

Kueue を使用して、Dynamic Workload Scheduler でバッチ ワークロードと AI ワークロードを実行します。

Kueue を使用する Job の Dynamic Workload Scheduler

以降のセクションでは、Kueue を使用して Job の Dynamic Workload Scheduler を構成する方法について説明します。次の一般的なノードプールの設定を構成できます。

  • Dynamic Workload Scheduler ノードプールの設定。
  • 予約と Dynamic Workload Scheduler ノードプールの設定。

このセクションでは、ai-on-gke リポジトリの dws-examples ディレクトリのサンプルを使用します。サンプルは、Apache2 ライセンスで dws-examples ディレクトリに公開されています。

Kueue をインストールするには、管理者権限が必要です。これらの権限を取得するには、IAM ロール roles/container.admin が付与されていることを確認します。GKE IAM ロールの詳細については、IAM 許可ポリシーを作成するガイドをご覧ください。

環境を準備する

  1. Cloud Shell で、次のコマンドを実行します。

    git clone https://github.com/GoogleCloudPlatform/ai-on-gke
    cd ai-on-gke/tutorials-and-examples/workflow-orchestration/dws-examples
    
  2. クラスタに 最新の Kueue バージョンをインストールします。

    VERSION=v0.7.0
    kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    

0.7.0 より前のバージョンの Kueue を使用する場合は、ProvisioningACC フィーチャー ゲートを true に設定して、Kueue フィーチャー ゲートの構成を変更します。詳細な説明とデフォルトのゲートの値については、Kueue のフィーチャー ゲートをご覧ください。Kueue のインストールの詳細については、インストールをご覧ください。

Dynamic Workload Scheduler ノードプール専用の設定用の Kueue リソースを作成する

次のマニフェストを使用して、dws-cluster-queue という名前のクラスタレベルのキューdws-local-queue という名前の LocalQueue Namespace を作成します。この Namespace の dws-cluster-queue キューを参照する Job は、Dynamic Workload Scheduler を使用して GPU リソースを取得します。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "default-flavor"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
  - nvidia.com/gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "dws-cluster-queue"
spec:
  namespaceSelector: {} 
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu", "ephemeral-storage"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10000  # Infinite quota.
      - name: "memory"
        nominalQuota: 10000Gi # Infinite quota.
      - name: "nvidia.com/gpu"
        nominalQuota: 10000  # Infinite quota.
      - name: "ephemeral-storage"
        nominalQuota: 10000Ti # Infinite quota.
  admissionChecks:
  - dws-prov
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "dws-local-queue"
spec:
  clusterQueue: "dws-cluster-queue"
---

このクラスタのキューの割り当て上限は高く、Dynamic Workload Scheduler インテグレーションのみが有効になります。Kueue API の詳細と上限の設定方法については、Kueue のコンセプトをご覧ください。

LocalQueue をデプロイします。

kubectl create -f ./dws-queues.yaml

出力は次のようになります。

resourceflavor.kueue.x-k8s.io/default-flavor created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created
clusterqueue.kueue.x-k8s.io/dws-cluster-queue created
localqueue.kueue.x-k8s.io/dws-local-queue created

Dynamic Workload Scheduler を使用する Job を他の Namespace で実行する場合は、上記のテンプレートを使用して追加の LocalQueues を作成できます。

Job を実行する

次のマニフェストのサンプルの Job は Dynamic Workload Scheduler を使用します。

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: dws-local-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-nodepool: NODEPOOL_NAME
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
        args: ["120s"]
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
          limits:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
      restartPolicy: Never

このマニフェストには、Dynamic Workload Scheduler の構成に関連する次のフィールドが含まれます。

  • kueue.x-k8s.io/queue-name: dws-local-queue ラベルは、Kueue がその Job のオーケストレーションを担当していることを GKE に示します。このラベルは、Job が追加されるキューも定義します。
  • suspend: true フラグを指定すると、GKE は Job リソースを作成しますが、Pod はまだスケジュールされません。ノードで Job の実行準備が整うと、Kueue はこのフラグを false に変更します。
  • nodeSelector は、指定されたノードプールでのみジョブをスケジュールするように GKE に指示します。この値は、NODEPOOL_NAME(キューに格納されたプロビジョニングを有効にするノードプールの名前)と一致している必要があります。
  1. Job を実行します。

    kubectl create -f ./job.yaml
    

    出力は次のようになります。

    job.batch/sample-job created
    
  2. Job のステータスを確認します。

    kubectl describe job sample-job
    

    出力は次のようになります。

    Events:
      Type    Reason            Age    From                        Message
      ----    ------            ----   ----                        -------
      Normal  Suspended         5m17s  job-controller              Job suspended
      Normal  CreatedWorkload   5m17s  batch/job-kueue-controller  Created Workload: default/job-sample-job-7f173
      Normal  Started           3m27s  batch/job-kueue-controller  Admitted by clusterQueue dws-cluster-queue
      Normal  SuccessfulCreate  3m27s  job-controller              Created pod: sample-job-9qsfd
      Normal  Resumed           3m27s  job-controller              Job resumed
      Normal  Completed         12s    job-controller              Job completed
    

Dynamic Workload Scheduler と Kueue のインテグレーションは、オープンソース エコシステムで利用可能な次のような他のワークロード タイプもサポートしています。

  • RayJob
  • JobSet v0.5.2 以降
  • Kubeflow MPIJob、TFJob、PyTorchJob
  • ワークフロー オーケストレーターで頻繁に使用される Kubernetes Pod
  • Flux ミニクラスタ

このサポートの詳細については、Kueue のバッチユーザーをご覧ください。

予約と Dynamic Workload Scheduler ノードプールの設定用の Kueue リソースを作成する

次のマニフェストでは、2 つの異なるノードプール(reservation-nodepooldws-nodepool)に関連付けられた 2 つの ResourceFlavors を作成します。これらのノードプールの名前は例にすぎません。これらの名前は、ノードプールの構成に応じて変更します。また、ClusterQueue 構成では、受信 Job は reservation-nodepool の使用を試みます。容量がない場合は、これらの Job は Dynamic Workload Scheduler を使用して GPU リソースを取得します。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "reservation"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "reservation-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "dws"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "dws-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {} # match all.
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
    flavors:
    - name: "reservation" # first we try reservation
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 36Gi
      - name: "nvidia.com/gpu"
        nominalQuota: 9
    - name: "dws"         # if reservation is saturated we try dws
      resources:
      - name: "cpu"
        nominalQuota: 10000   # Infinite quota.
      - name: "memory"
        nominalQuota: 10000Gi # Infinite quota.
      - name: "nvidia.com/gpu"
        nominalQuota: 10000   # Infinite quota.
  admissionChecksStrategy:
    admissionChecks:
      - name: "dws-prov"
        onFlavors: [dws]
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "user-queue"
spec:
  clusterQueue: "cluster-queue"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
  - nvidia.com/gpu

このクラスタのキューの割り当て上限は高く、Dynamic Workload Scheduler インテグレーションのみが有効になります。Kueue API の詳細と上限の設定方法については、Kueue のコンセプトをご覧ください。

次のコマンドを使用してマニフェストをデプロイします。

kubectl create -f ./dws_and_reservation.yaml

出力は次のようになります。

resourceflavor.kueue.x-k8s.io/reservation created
resourceflavor.kueue.x-k8s.io/dws created
clusterqueue.kueue.x-k8s.io/cluster-queue created
localqueue.kueue.x-k8s.io/user-queue created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created

Job を実行する

前の設定とは異なり、このマニフェストには nodeSelector フィールドが含まれていません。これは、ClusterQueue の空き容量に応じて Kueue によって入力されるためです。

apiVersion: batch/v1
kind: Job
metadata:
  generateName: sample-job-
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: user-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
        args: ["120s"]
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
          limits:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
      restartPolicy: Never
  1. Job を実行します。

    kubectl create -f ./job-without-node-selector.yaml
    

    出力は次のようになります。

    job.batch/sample-job-v8xwm created
    

Job が使用するノードプールを確認するには、Job が使用する ResourceFlavor を確認する必要があります。

トラブルシューティング

Kueue のトラブルシューティングの詳細については、Kueue でのプロビジョニング リクエストのトラブルシューティングをご覧ください。

Kueue を使用しない Job の Dynamic Workload Scheduler

ProvisioningRequest オブジェクトを定義する

Job ごとに ProvisioningRequest API を使用してリクエストを作成します。 Dynamic Workload Scheduler は Pod を起動するのではなく、ノードのプロビジョニングのみを行います。

  1. 次の provisioning-request.yaml マニフェストを作成します。

    Standard

    apiVersion: v1
    kind: PodTemplate
    metadata:
      name: POD_TEMPLATE_NAME
      namespace: NAMESPACE_NAME
      labels:
        cloud.google.com/apply-warden-policies: "true"
    template:
      spec:
        nodeSelector:
          cloud.google.com/gke-nodepool: NODEPOOL_NAME
        tolerations:
          - key: "nvidia.com/gpu"
            operator: "Exists"
            effect: "NoSchedule"
        containers:
          - name: pi
            image: perl
            command: ["/bin/sh"]
            resources:
              limits:
                cpu: "700m"
                nvidia.com/gpu: 1
              requests:
                cpu: "700m"
                nvidia.com/gpu: 1
        restartPolicy: Never
    ---
    apiVersion: autoscaling.x-k8s.io/API_VERSION
    kind: ProvisioningRequest
    metadata:
      name: PROVISIONING_REQUEST_NAME
      namespace: NAMESPACE_NAME
    spec:
      provisioningClassName: queued-provisioning.gke.io
      parameters:
        maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS"
      podSets:
      - count: COUNT
        podTemplateRef:
          name: POD_TEMPLATE_NAME
    

    次のように置き換えます。

    • API_VERSION: API のバージョン(v1 または v1beta1)。GKE バージョン 1.31.1-gke.1678000 以降では、安定性と最新機能へのアクセスのために v1 を使用することをおすすめします。
    • NAMESPACE_NAME: Kubernetes Namespace の名前。この Namespace は Pod の Namespace と同じにする必要があります。
    • PROVISIONING_REQUEST_NAME: ProvisioningRequest の名前。この名前は Pod のアノテーションで参照します。
    • MAX_RUN_DURATION_SECONDS: ノードの最大実行時間(秒)。最大実行時間はデフォルトの 7 日間です(省略可)。詳細については、Dynamic Workload Scheduler の仕組みをご覧ください。この値は、リクエストの作成後は変更できません。このフィールドは、GKE バージョン 1.28.5-gke.1355000 以降で使用できます。
    • COUNT: リクエストする Pod 数。ノードは 1 つのゾーンでアトミックにスケジュールされます。
    • POD_TEMPLATE_NAME: PodTemplate の名前。
    • NODEPOOL_NAME: ノードプールに付ける名前。自動プロビジョニングされたノードプールを使用する場合は削除します。

    GKE は、Pod の作成時に検証とミューテーションを Pod に適用することがあります。cloud.google.com/apply-warden-policies ラベルを使用すると、GKE は PodTemplate オブジェクトに同じ検証とミューテーションを適用できます。このラベルは、GKE が Pod のノードリソース要件を計算するために必要です。

    ノードの自動プロビジョニング

    apiVersion: v1
    kind: PodTemplate
    metadata:
      name: POD_TEMPLATE_NAME
      namespace: NAMESPACE_NAME
      labels:
        cloud.google.com/apply-warden-policies: "true"
    template:
      spec:
        nodeSelector:
          cloud.google.com/gke-accelerator: GPU_TYPE
        tolerations:
          - key: "nvidia.com/gpu"
            operator: "Exists"
            effect: "NoSchedule"
        containers:
          - name: pi
            image: perl
            command: ["/bin/sh"]
            resources:
              limits:
                cpu: "700m"
                nvidia.com/gpu: 1
              requests:
                cpu: "700m"
                nvidia.com/gpu: 1
        restartPolicy: Never
    ---
    apiVersion: autoscaling.x-k8s.io/API_VERSION
    kind: ProvisioningRequest
    metadata:
      name: PROVISIONING_REQUEST_NAME
      namespace: NAMESPACE_NAME
    spec:
      provisioningClassName: queued-provisioning.gke.io
      parameters:
        maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS"
      podSets:
      - count: COUNT
        podTemplateRef:
          name: POD_TEMPLATE_NAME
    

    次のように置き換えます。

    • API_VERSION: API のバージョン(v1 または v1beta1)。GKE バージョン 1.31.1-gke.1678000 以降では、安定性と最新機能へのアクセスのために v1 を使用することをおすすめします。
    • NAMESPACE_NAME: Kubernetes Namespace の名前。この Namespace は Pod の Namespace と同じにする必要があります。
    • PROVISIONING_REQUEST_NAME: ProvisioningRequest の名前。この名前は Pod のアノテーションで参照します。
    • MAX_RUN_DURATION_SECONDS: ノードの最大実行時間(秒)。最大実行時間はデフォルトの 7 日間です(省略可)。詳細については、Dynamic Workload Scheduler の仕組みをご覧ください。この値は、リクエストの作成後は変更できません。このフィールドは、GKE バージョン 1.28.5-gke.1355000 以降で使用できます。
    • COUNT: リクエストする Pod 数。ノードは 1 つのゾーンでアトミックにスケジュールされます。
    • POD_TEMPLATE_NAME: PodTemplate の名前。
    • GPU_TYPE: GPU ハードウェアのタイプ。

    GKE は、Pod の作成時に検証とミューテーションを Pod に適用することがあります。cloud.google.com/apply-warden-policies ラベルを使用すると、GKE は PodTemplate オブジェクトに同じ検証とミューテーションを適用できます。このラベルは、GKE が Pod のノードリソース要件を計算するために必要です。

  2. 次のようにマニフェストを適用します。

    kubectl apply -f provisioning-request.yaml
    

Pod を構成する

このセクションでは、Kubernetes Job を使用して Pod を構成します。ただし、Kubernetes JobSet や Kubeflow、Ray、カスタム コントローラなどの他のフレームワークを使用することもできます。Job 仕様で、次のアノテーションを使用して Pod を ProvisioningRequest にリンクします。

apiVersion: batch/v1
kind: Job
spec:
  template:
    metadata:
      annotations:
        autoscaling.x-k8s.io/consume-provisioning-request: PROVISIONING_REQUEST_NAME
        autoscaling.x-k8s.io/provisioning-class-name: "queued-provisioning.gke.io"
    spec:
      ...

GKE バージョン 1.30.3-gke.1854000 より前は、次の以前のアノテーションを使用する必要があります。

      annotations:
        cluster-autoscaler.kubernetes.io/consume-provisioning-request: PROVISIONING_REQUEST_NAME
        cluster-autoscaler.kubernetes.io/provisioning-class-name: "queued-provisioning.gke.io"

GKE バージョン 1.31.1-gke.1678000 以降、cluster-autoscaler.kubernetes.io/consume-provisioning-request アノテーションと cluster-autoscaler.kubernetes.io/provisioning-class-name アノテーションは非推奨になりました。

Pod のアノテーション キー consume-provisioning-request は、消費する ProvisioningRequest を定義します。GKE は consume-provisioning-requestprovisioning-class-name のアノテーションを使用して次の処理を行います。

  • Dynamic Workload Scheduler によってプロビジョニングされたノードでのみ Pod をスケジュールする。
  • クラスタ オートスケーラーで、Pod 間と Dynamic Workload Scheduler 間でリソース リクエストが重複してカウントされるのを回避する。
  • クラスタ オートスケーラーがノード間で Pod を移動してバッチ計算を中断することを防ぐため、safe-to-evict: false アノテーションを挿入する。この動作は、Pod アノテーションで safe-to-evict: true を指定することによって変更できます。

Dynamic Workload Scheduler のステータスを確認する

Dynamic Workload Scheduler のステータスは、Pod がスケジュール可能かどうかを定義します。Kubernetes の watch を使用すると、変化を効率的に監視できます。または、Kubernetes オブジェクトのステータスのトラッキングに使っているその他のツールを利用できます。次の表に、Dynamic Workload Scheduler の各ステータスと、考えられる結果を示します。

Dynamic Workload Scheduler のステータス 説明 考えられる結果
Pending リクエストは認識されず、まだ処理されていません。 処理後、リクエストは Accepted 状態または Failed 状態に移行します。
Accepted=true リクエストは承認済みで、リソースが使用可能になるのを待機しています。 リソースが見つかってノードがプロビジョニングされた場合、リクエストは Provisioned 状態に移行し、そうでない場合は Failed 状態に移行します。
Provisioned=true ノードの準備ができています。 10 分以内に Pod を起動して、プロビジョニングされたリソースを使用できます。この期間が経過すると、ノードはクラスタ オートスケーラーによって不要ノードと見なされ、削除されます。
Failed=true エラーのため、ノードをプロビジョニングできません。Failed=true は終了状態です。 条件の Reason フィールドと Message フィールドの情報に基づいて、条件のトラブルシューティングを行ってください。新しい Dynamic Workload Scheduler リクエストを作成して再試行します。
Provisioned=false ノードがまだプロビジョニングされていません。

Reason=NotProvisioned の場合は、すべてのリソースが使用可能になる前の一時的な状態です。

Reason=QuotaExceeded の場合は、この理由と条件の Message フィールドの情報に基づいて、この状態のトラブルシューティングを行います。追加の割り当てのリクエストが必要になる場合があります。詳細については、Dynamic Workload Scheduler が割り当てによって制限されているかどうかを確認するをご覧ください。この Reason は、GKE バージョン 1.29.2-gke.1181000 以降でのみ使用できます。

Reason=ResourcePoolExhausted で、MessageExpected time is indefinite が含まれている場合は、別のゾーンまたはリージョンを選択するか、リクエストされたリソースを調整します。

Pod を起動する

Dynamic Workload Scheduler リクエストが Provisioned=true ステータスに達したら、Job を実行して Pod を起動できます。これにより、保留中のリクエストや失敗したリクエストによってスケジュール不可能な Pod が急増する(これは、kube-scheduler とクラスタ オートスケーラーのパフォーマンスに影響を与えます)のを回避できます。

また、スケジュール不可の Pod があることが特に重要ではない場合は、Dynamic Workload Scheduler と並行して Pod を作成できます。

Dynamic Workload Scheduler リクエストをキャンセルする

プロビジョニングの実行前にリクエストをキャンセルするには、ProvisioningRequest を削除します。

kubectl delete provreq PROVISIONING_REQUEST_NAME -n NAMESPACE

ほとんどの場合、ProvisioningRequest を削除すればノードは作成されなくなります。 ただし、タイミングによっては(ノードがすでにプロビジョニングされている場合など)、最終的にノードが作成されることもあります。この場合、Pod が作成されなければ、ノードは 10 分後にクラスタ オートスケーラーによって削除されます。

Dynamic Workload Scheduler の仕組み

ProvisioningRequest API を使用すると、Dynamic Workload Scheduler は次の処理を行います。

  1. 必要なすべてのノードが同時に使用可能になるまでワークロードが無期限に待機することを、ユーザーが GKE に指示します。
  2. クラスタ オートスケーラーがユーザーのリクエストを受け入れ、必要なノードの数を計算します(それらのノードは 1 つのユニットとして扱われます)。
  3. リクエストは、必要なすべてのリソースが単一のゾーンで使用可能になるまで待機します。1.29.1-gke.1708000 以降を実行しているクラスタの場合、このゾーンは利用可能な容量に関する情報を使用して選択され、待機時間が短縮されます。以前のバージョンを実行しているクラスタでは、この情報を利用することなくゾーンが選択されているため、待機時間が大幅に長いゾーンでキューイングが発生する可能性があります。
  4. 必要なノードが利用可能になった時点で、クラスタ オートスケーラーはすべてのノードを一度にプロビジョニングします。
  5. 新しくプロビジョニングされたノードで、ワークロードのすべての Pod が同時に実行可能になります。
  6. プロビジョニングされたノードの実行時間は 7 日間に制限されます。maxRunDurationSeconds パラメータを設定して、ワークロードの実行に必要な時間が短いことを指定する場合は、実行時間をこれよりも短くできます。詳細については、VM のランタイムを制限するをご覧ください。この機能は、GKE バージョン 1.28.5-gke.1355000 以降で使用できます。この時間が経過すると、ノードとその上で実行中の Pod はプリエンプトされます。Pod がそれより前に終了し、ノードが使用されなければ、クラスタ オートスケーラーは自動スケーリング プロファイルに従ってノードと Pod を削除します。
  7. Dynamic Workload Scheduler 間でノードが再利用されることはありません。各 ProvisioningRequest により、新たな 7 日間を実行時間とする新しいノードの作成がリクエストされます。

GKE では実行時間がノードレベルで測定されます。起動時の遅延により、Pod を実行可能な時間は若干短くなることもあります。Pod の再試行ではこの実行時間が共有されるため、再試行後には Pod を実行可能な時間が短くなります。GKE では、Dynamic Workload Scheduler リクエストごとに実行時間がカウントされます。

割り当て

Dynamic Workload Scheduler リクエストによってプロビジョニングされるすべての VM は、プリエンプティブル割り当てを使用します。

Accepted 状態の ProvisioningRequests の数は、専用の割り当てによって制限されます。プロジェクトごとに割り当てを構成します。リージョンごとに 1 つの割り当て構成です。

Google Cloud コンソールで割り当てを確認する

Google Cloud コンソールで割り当て上限の名前と現在の使用量を確認する手順は次のとおりです。

  1. Google Cloud コンソールで [割り当て] ページに移動します。

    [割り当て] に移動

  2. [ フィルタ] ボックスで [指標] プロパティを選択し、「active_resize_requests」と入力して Enter キーを押します。

デフォルト値は 100 です。割り当てを増やすには、割り当て上限の引き上げをリクエストするの説明に従ってください。

Dynamic Workload Scheduler が割り当てによって制限されているかどうかを確認する

Dynamic Workload Scheduler リクエストの処理に予想よりも時間がかかっている場合は、リクエストが割り当てによって制限されていないことを確認してください。追加の割り当てのリクエストが必要になる場合があります。

バージョン 1.29.2-gke.1181000 以降を実行しているクラスタでは、特定の割り当て制限が原因でリクエストの処理が妨げられているかどうかを確認します。

kubectl describe provreq PROVISIONING_REQUEST_NAME \
    --namespace NAMESPACE

出力は次のようになります。

…
Last Transition Time:  2024-01-03T13:56:08Z
    Message:               Quota 'NVIDIA_P4_GPUS' exceeded. Limit: 1.0 in region europe-west4.
    Observed Generation:   1
    Reason:                QuotaExceeded
    Status:                False
    Type:                  Provisioned
…

この例では、europe-west4 リージョンに十分な割り当てがないため、GKE はノードをデプロイできません。

Dynamic Workload Scheduler を使用してワークロードの中断を管理する

ノードプール内のすべてのノードまたはほとんどのノードが利用可能であることを必要とするワークロードは、強制排除の影響を受けやすくなります。ProvisioningRequest API を使用してプロビジョニングされたノードの自動修復または自動アップグレードはサポートされていません。これは、これらのオペレーションでは、そのノードで実行中のすべてのワークロードが強制排除され、ワークロードをスケジュールできなくなるためです。

ワークロードの中断を最小限に抑えるためのベスト プラクティス

Dynamic Workload Scheduler を使用するワークロード実行の中断を最小限に抑えるには、次のタスクを実行します。

  • クラスタのリリース チャンネルの登録に応じて、次のベスト プラクティスを使用して、ノードの自動アップグレードによってワークロードが中断されないようにします。
  • ノードの自動修復を無効にします
  • メンテナンスの時間枠と除外を使用して、実行中のワークロードへの影響を最小限に抑えながら、GKE が自動メンテナンスを行うための時間を確保します。実行中のワークロードがない時間に指定してください。
  • ノードプールを最新の状態に保つには、アクティブな Dynamic Workload Scheduler リクエストがなく、ノードプールが空であるときに、ノードプールを手動でアップグレードすることをおすすめします。

制限事項

  • Pod 間のアンチアフィニティはサポートされていません。クラスタ オートスケーラーではノードのプロビジョニング中に Pod 間のアンチアフィニティ ルールが考慮されないため、スケジュール不可能なワークロードが発生する可能性があります。これは、複数の Dynamic Workload Scheduler オブジェクト用のノードが同じノードプールでプロビジョニングされたときに発生する場合があります。
  • GPU ノードのみがサポートされています。
  • Dynamic Workload Scheduler では予約はサポートされていません。ノードプールの作成時に --reservation-affinity=none を指定する必要があります。 Dynamic Workload Scheduler では、クラスタの自動スケーリングに必要な ANY ロケーション ポリシーのみがサポートされます。
  • 1 つの Dynamic Workload Scheduler リクエストで最大 1,000 個の VM を作成できます。これは、1 つのノードプールのゾーンあたりの最大ノード数です。
  • GKE では、Compute Engine の ACTIVE_RESIZE_REQUESTS 割り当てにより、キューで保留される Dynamic Workload Scheduler リクエストの数が制御されます。デフォルトでは、この割り当ては Google Cloud プロジェクト レベルで 100 に制限されています。この割り当てを超える Dynamic Workload Scheduler リクエストを作成しようとすると、新しいリクエストは失敗します。
  • Dynamic Workload Scheduler を使用するノードプールは、ノードがまとめてプロビジョニングされるため、停止の影響を受けやすくなります。詳細については、Dynamic Workload Scheduler を使用するワークロードを含むノードプールの停止設定を構成するをご覧ください。
  • Google Cloud コンソールに、有効期間の短い追加の VM が表示される場合があります。これは、必要なすべてのマシンをプロビジョニングする容量が使用可能になるまで、Compute Engine によって VM が作成され、すぐに削除される可能性があるためです。
  • Dynamic Workload Scheduler インテグレーションでサポートされる PodSet は 1 つのみです。異なる Pod テンプレートを混在させる場合は、リクエストされるリソースが最も多いテンプレートを使用してください。異なるマシンタイプ(GPU タイプが異なる VM など)を混在させることはできません。

次のステップ