迅速な Pod スケーリングのために追加のコンピューティング容量をプロビジョニングする


このページでは、トラフィックの多いイベント中に新しいノードの起動を待たずにワークロードを迅速にスケールアップできるように、Google Kubernetes Engine(GKE)クラスタで追加のコンピューティング容量を予約する方法について説明します。以下の手順を使用すると、コンピューティング オーバーヘッドを常に利用可能な状況で、または特定のイベントに備えて予約できます。

予備容量のプロビジョニングが有用な理由

ノードの自動プロビジョニングを使用する GKE Autopilot クラスタと Standard クラスタでは、新しい Pod を実行する容量を持つ既存のノードがない場合、新しいノードが作成されます。新しいノードが起動するまでに 80~120 秒ほどかかります。GKE は、ノードが起動するまで待機してから、保留中の Pod を新しいノードに配置します。その後、Pod を起動できます。Standard クラスタでは、新しい Pod の実行に必要な追加容量がある新しいノードプールを手動で作成することもできます。このページは、Autopilot やノードの自動プロビジョニングなどのノードの自動スケーリング メカニズムを使用するクラスタに適用されます。

スケールアップ イベント中に Pod の起動時間を短縮した方がよい場合があります。たとえば、人気のライブサービス マルチプレーヤー型ゲームを新たにリリースする場合、ゲームサーバー Pod の起動時間が短縮されると、リリース日にログインしたプレーヤーの待機時間を短縮できる可能性があります。別の例として、e コマース プラットフォームを運営していて期間限定セールを計画している場合、セール期間中はトラフィックが急増することが予想されます。

予備容量のプロビジョニングは Pod のバーストに対応しています。これにより、ノード上の他の Pod からリクエストされたリソースが他の Pod で使用可能であり、未使用の場合、Pod はそのリソースを一時的に使用できます。バースト機能を使用する場合、リソース上限をリソースのリクエスト数よりも高く設定するか、リソース上限を設定しないでください。詳細については、GKE で Pod バーストを構成するをご覧ください。

GKE における予備容量のプロビジョニングの仕組み

予備容量をプロビジョニングするには、Kubernetes PriorityClass とプレースホルダ Pod を使用します。PriorityClass を使用すると、一部のワークロードが他のワークロードよりも優先度が低いことを GKE に指示できます。優先度の低い PriorityClass を使用するプレースホルダ Pod をデプロイし、予約する必要があるコンピューティング容量をリクエストできます。GKE は、プレースホルダ Pod に対応する新しいノードを作成して、クラスタに容量を追加します。

本番環境のワークロードがスケールアップされると、GKE は優先度の低いプレースホルダ Pod を強制排除し、代わりに(優先度の高いの PriorityClass を使用する)本番環境 Pod の新しいレプリカをスケジュールします。優先度レベルが異なる優先度の低い Pod が複数ある場合、GKE は優先度が最も低い Pod を先に強制排除します。

容量のプロビジョニング方法

ユースケースに応じて、次のいずれかの方法で GKE クラスタに追加の容量をプロビジョニングできます。

  • 継続的な容量のプロビジョニング: Deployment を使用して、クラスタ内で常に実行される優先度の低いプレースホルダ Pod を特定の数だけ作成します。GKE がこれらの Pod を強制排除して本番環境ワークロードを実行する場合、Deployment コントローラは、強制排除された優先度の低い Pod を再作成する追加の容量が GKE でプロビジョニングされるようにします。この方法では、Deployment を削除するまで、複数のスケールアップとスケールダウンのイベントにわたって継続的に容量オーバーヘッドが提供されます。
  • 1 回限りの容量のプロビジョニング: Job を使用して、特定の数の優先度の低い並列プレースホルダ Pod を一定期間実行します。その時間が経過するか、GKE がすべての Job レプリカを強制排除すると、予約された容量は使用できなくなります。この方法では、特定の期間に利用可能な特定の容量が提供されます。

料金

GKE Autopilot では、実行中の Pod のリソース リクエストに対して料金が発生します。これには、デプロイした優先度の低いワークロードも含まれます。詳細については、Autopilot の料金をご覧ください。

GKE Standard では、Pod がその容量を使用しているかどうかにかかわらず、GKE がプロビジョニングする基盤となる Compute Engine VM に対して料金が発生します。詳しくは、Standard の料金をご覧ください。

始める前に

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

  • Google Kubernetes Engine API を有効にします。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化します。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得します。
  • GKE Autopilot クラスタ、またはノードの自動プロビジョニングが有効になっている GKE Standard クラスタがあることを確認します。
  • 容量のプロビジョニングに関する考慮事項を確認して、容量リクエストに適切な値を選択してください。

PriorityClass を作成する

容量のプロビジョニング方法で説明されているいずれかの方法を使用するには、まず次の PriorityClass を作成する必要があります。

  • Default PriorityClass: Pod 仕様で別の PriorityClass が明示的に設定されていない Pod に割り当てられる、グローバルでデフォルトの PriorityClass。このデフォルトの PriorityClass の Pod は、より低い PriorityClass を使用する Pod を強制排除できます。
  • Low PriorityClass: GKE で可能な限り最も低い優先度に設定されたデフォルト以外の PriorityClass。この PriorityClass の Pod は強制排除され、より高い PriorityClass の Pod を実行できます。
  1. 次のマニフェストを priorityclasses.yaml として保存します。

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: low-priority
    value: -10
    preemptionPolicy: Never
    globalDefault: false
    description: "Low priority workloads"
    ---
    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: default-priority
    value: 0
    preemptionPolicy: PreemptLowerPriority
    globalDefault: true
    description: "The global default priority."
    

    このマニフェストには次のフィールドがあります。

    • preemptionPolicy: PriorityClass を使用する Pod が優先度の低い Pod を強制排除できるかどうかを指定します。low-priority PriorityClass は Never を使用し、default PriorityClass は PreemptLowerPriority を使用します。
    • value: PriorityClass を使用する Pod の優先度。default PriorityClass は 0 を使用します。low-priority PriorityClass は -1 を使用します。Autopilot では、この設定は default PriorityClass の優先度よりも小さい任意の値に設定できます。

      Standard では、この値を -10 未満に設定すると、その PriorityClass を使用する Pod は新しいノードの作成をトリガーせず、[保留中] のままになります。

      優先度に適切な値を決定する方法については、優先度を選択するをご覧ください。

    • globalDefault: Pod 仕様で PriorityClass が明示的に設定されていない Pod に、GKE が PriorityClass を割り当てるかどうかを指定します。low-priority PriorityClass は false を使用し、default PriorityClass は true を使用します。

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

    kubectl apply -f priorityclasses.yaml
    

追加のコンピューティング容量をプロビジョニングする

次のセクションでは、単一のイベントの容量をプロビジョニングする場合、または経時的に継続的に容量をプロビジョニングする場合の例を示します。

Deployment を使用して継続的な容量のプロビジョニングを行う

  1. 次のマニフェストを capacity-res-deployment.yaml として保存します。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: capacity-res-deploy
    spec:
      replicas: 10
      selector:
        matchLabels:
          app: reservation
      template:
        metadata:
          labels:
            app: reservation
        spec:
          priorityClassName: low-priority
          terminationGracePeriodSeconds: 0
          containers:
          - name: ubuntu
            image: ubuntu
            command: ["sleep"]
            args: ["infinity"]
            resources:
              requests:
                cpu: 500m
                memory: 500Mi
    

    このマニフェストには次のフィールドがあります。

    • spec.replicas: 要件を満たすようにこの値を変更します。
    • spec.resources.requests: 要件を満たすように CPU リクエストとメモリ リクエストを変更します。容量サイズを選択するのガイダンスを使用して、適切なリクエスト値を決定します。
    • spec.containers.commandspec.containers.args: GKE によって強制排除されるまで、アクティブ状態を維持するよう Pod に指示します。
  2. 次のようにマニフェストを適用します。

    kubectl apply -f capacity-res-deployment.yaml
    
  3. Pod のステータスを取得します。

    kubectl get pods -l app=reservation
    

    すべてのレプリカのステータスが Running になるまで待ちます。

Job を使用して単一イベントの容量をプロビジョニングする

  1. 次のマニフェストを capacity-res-job.yaml として保存します。

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: capacity-res-job
    spec:
      parallelism: 4
      backoffLimit: 0
      template:
        spec:
          priorityClassName: low-priority
          terminationGracePeriodSeconds: 0
          containers:
          - name: ubuntu-container
            image: ubuntu
            command: ["sleep"]
            args: ["36000"]
            resources:
              requests:
                cpu: "16"
          restartPolicy: Never
    

    このマニフェストには次のフィールドがあります。

    • spec.parallelism: 容量を予約するために並列で実行する Job の数に変更します。
    • spec.backoffLimit: 0: Job コントローラが強制排除された Job を再作成しないようにします。
    • template.spec.resources.requests: 要件を満たすように CPU リクエストとメモリ リクエストを変更します。考慮事項のガイダンスを使用して、適切な値を決定します。
    • template.spec.containers.commandtemplate.spec.containers.args: 追加の容量が必要な期間(秒単位)、アクティブ状態を維持するよう Job に指示します。
  2. 次のようにマニフェストを適用します。

    kubectl apply -f capacity-res-job.yaml
    
  3. Job のステータスを取得します。

    kubectl get jobs
    

    すべての Job のステータスが Running になるまで待ちます。

容量のプロビジョニングと強制排除をテストする

容量のプロビジョニングが想定どおりに機能することを確認するには、次の操作を行います。

  1. ターミナルで、容量のプロビジョニング ワークロードのステータスを確認します。

    1. Deployment の場合は、次のコマンドを実行します。

      kubectl get pods --label=app=reservation -w
      
    2. Job の場合は、次のコマンドを実行します。

      kubectl get Jobs -w
      
  2. 新しいターミナル ウィンドウを開き、次の操作を行います。

    1. 次のマニフェストを test-deployment.yaml として保存します。

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: helloweb
        labels:
          app: hello
      spec:
        replicas: 5
        selector:
          matchLabels:
            app: hello
            tier: web
        template:
          metadata:
            labels:
              app: hello
              tier: web
          spec:
            containers:
            - name: hello-app
              image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
              ports:
              - containerPort: 8080
              resources:
                requests:
                  cpu: 400m
                  memory: 400Mi
      
    2. 次のようにマニフェストを適用します。

      kubectl apply -f test-deployment.yaml
      
  3. 元のターミナル ウィンドウで、GKE は次の例のように、容量のプロビジョニング ワークロードの一部を終了して、新しいレプリカをスケジュールします。

    NAME                                         READY   STATUS    RESTARTS   AGE
    capacity-res-deploy-6bd9b54ffc-5p6wc         1/1     Running   0          7m25s
    capacity-res-deploy-6bd9b54ffc-9tjbt         1/1     Running   0          7m26s
    capacity-res-deploy-6bd9b54ffc-kvqr8         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-n7zn4         1/1     Running   0          2m33s
    capacity-res-deploy-6bd9b54ffc-pgw2n         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-t5t57         1/1     Running   0          2m32s
    capacity-res-deploy-6bd9b54ffc-v4f5f         1/1     Running   0          7m24s
    helloweb-85df88c986-zmk4f                    0/1     Pending   0          0s
    helloweb-85df88c986-lllbd                    0/1     Pending   0          0s
    helloweb-85df88c986-bw7x4                    0/1     Pending   0          0s
    helloweb-85df88c986-gh8q8                    0/1     Pending   0          0s
    helloweb-85df88c986-74jrl                    0/1     Pending   0          0s
    capacity-res-deploy-6bd9b54ffc-v6dtk   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-kvqr8   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-pgw2n   1/1     Terminating   0          2m47s
    capacity-res-deploy-6bd9b54ffc-n7zn4   1/1     Terminating   0          2m48s
    capacity-res-deploy-6bd9b54ffc-2f8kx   1/1     Terminating   0          2m48s
    ...
    helloweb-85df88c986-lllbd              0/1     Pending       0          1s
    helloweb-85df88c986-gh8q8              0/1     Pending       0          1s
    helloweb-85df88c986-74jrl              0/1     Pending       0          1s
    helloweb-85df88c986-zmk4f              0/1     Pending       0          1s
    helloweb-85df88c986-bw7x4              0/1     Pending       0          1s
    helloweb-85df88c986-gh8q8              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-zmk4f              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-bw7x4              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-lllbd              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-74jrl              0/1     ContainerCreating   0          1s
    helloweb-85df88c986-zmk4f              1/1     Running             0          4s
    helloweb-85df88c986-lllbd              1/1     Running             0          4s
    helloweb-85df88c986-74jrl              1/1     Running             0          5s
    helloweb-85df88c986-gh8q8              1/1     Running             0          5s
    helloweb-85df88c986-bw7x4              1/1     Running             0          5s
    

    この出力は、新しい Deployment が [保留中] から [実行中] に変わるまでに 5 秒かかったことを示しています。

容量のプロビジョニングに関する考慮事項

継続的な容量のプロビジョニング

  • 必要なプレースホルダ Pod レプリカの数と、各レプリカのリクエストのサイズを評価します。優先度の低いレプリカでは、最大の本番環境ワークロードと少なくとも同じ容量をリクエストして、そのワークロードが優先度の低いワークロードによって予約された容量に収まるようにする必要があります。
  • 多数の本番環境ワークロードを大規模に運用する場合は、プレースホルダ Pod のリソース リクエストを、1 つではなく複数の本番環境ワークロードを実行するのに十分な容量をプロビジョニングする値に設定することを検討してください。

1 回限りの容量のプロビジョニング

  • プレースホルダ Job が継続する時間を、追加の容量が必要な時間に設定します。たとえば、ゲームのリリース日 24 時間分の追加容量が必要な場合は、時間を 86,400 秒に設定します。これにより、プロビジョニングされた容量が必要以上に長くならないようにできます。
  • 容量を予約する期間と同じ期間のメンテナンスの時間枠を設定します。これにより、ノードのアップグレード中に優先度の低い Job が強制排除されるのを防ぐことができます。メンテナンスの時間枠の設定は、ワークロードの需要が高いことが予想される場合にもおすすめします。
  • 多数の本番環境ワークロードを大規模に運用する場合は、プレースホルダ Job のリソース リクエストを、1 つではなく複数の本番環境ワークロードを実行するのに十分な容量をプロビジョニングする値に設定することを検討してください。

容量は、単一のスケーリング イベントに対してのみプロビジョニングされます。スケールアップして容量を使用し、次にスケールダウンすると、その容量は別のスケールアップ イベントで使用できなくなります。複数のスケールアップ イベントとスケールダウン イベントが予想される場合は、継続的な容量予約方法を使用し、必要に応じて予約のサイズを調整します。たとえば、イベントの前に Pod リクエストを多くし、イベントの後に Pod リクエストを少なくするか、0 にします。

優先度を選択する

PriorityClass の優先度を 0 未満に設定する。

要件の異なるワークロードで使用するために、クラスタに複数の PriorityClass を定義できます。たとえば、1 回限りの容量のプロビジョニングの場合は、優先度が -10 の PriorityClass を作成し、継続的な容量のプロビジョニングの場合は優先度 -9 の PriorityClass を作成できます。その後、優先度 -9 の PriorityClass を使用して継続的な容量をプロビジョニングし、特別なイベントのために容量を増やす場合は、優先度 -10 の PriorityClass を使用する新しい Job をデプロイできます。GKE は、優先度が最も低いワークロードを先に強制排除します。

他の PriorityClass を使用して、フォールト トレラント バッチ ワークロードなど、実際のタスクを実行する優先度の低い非本番環境ワークロードを、本番環境ワークロードよりも低いがプレースホルダ Pod よりも高い優先度で実行することもできます。たとえば、-5 です。

容量サイズを選択する

プレースホルダ ワークロードのレプリカ数とリソース リクエストを、スケールアップ時に本番環境ワークロードが必要とする容量以上に設定します。

プロビジョニングされる合計容量は、デプロイするプレースホルダ Pod の数と、各レプリカのリソース リクエストに基づきます。スケールアップで、プレースホルダ Pod にプロビジョニングされた GKE よりも多くの容量が必要な場合、GKE がより多くの容量をプロビジョニングできるようになるまで、一部の本番環境ワークロードは Pending のままになります。

次のステップ