GKEで予備の容量プロビジョニングを使用してワークロードのスケールアウトを高速化する

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では Google Kubernetes Engine(以下、GKE)で 予備の容量プロビジョニング(spare capacity provisioning) を使用することで、ワークロードを素早くスケールアウトする方法を解説します。

Google Kubernetes Engine(GKE)

GKE とは

GKE はコンテナオーケストレーションツールである Kubernetes を、Google マネージドのクラスタで使用することができるサービスです。
GKE ではノードの管理をユーザーが行うことで柔軟な要件に対応できる Standard モード のクラスタと、ノードの管理を Google に任せ、ワークロードの管理のみに集中することができる Autopilot モード のクラスタを選択することができます。

GKE の詳細については以下の記事で解説しています。

blog.g-gen.co.jp

ノードの自動プロビジョニングを使用したスケールアウトの問題点

ノードの自動プロビジョニング が有効化されている GKE クラスタでは、Pod がスケールアウトするとき、既存のノードに新しい Pod を起動するための容量がない場合に新しいノードが自動で作成されます。

しかし、新しいノードの起動には約 80 秒 ~ 120 秒かかるため、急激なトラフィック増加などに素早く対応できない場合があります。

ノードの起動待ちでワークロードのスケールアウトに時間がかかる

特に Autopilot モードのクラスタでは、ノードのコンピューティングリソース量をユーザーが指定することができないため、スケールアウト時のリソース使用量を考慮してノードのマシンタイプを設定したり、事前にノード数を増やしておいたりして対策することが難しくなっています。

当記事では、事前にノード数を増やしておき Pod をすぐに起動できるようにする方法として、予備の容量プロビジョニング を使用します。当機能は Autopilot クラスタと Standard クラスタの両方で利用可能です。

予備の容量プロビジョニングについて

予備の容量プロビジョニングの概要

予備の容量プロビジョニングでは、Kubernetes の PriorityClass オブジェクトを使用し、一定数の 優先度の低い Pod をノードで実行します。こうすることで予めノードを増やしておくことができ、ワークロードの Pod がスケールアウトする際に、優先度の低い Pod を終了してワークロードの Pod に置き換えることができます。

ここで使用する優先度の低い Pod は プレースホルダ Pod、または バルーン Pod と呼ばれます。当記事では公式ドキュメントに従いプレースホルダ Pod と記載します。

プレースホルダ Pod を使用してワークロードのスケールアウトを高速化する

予備の容量プロビジョニングでは、プレースホルダ Pod の実行の仕方によって、一貫した容量のプロビジョニング単一のイベント容量のプロビジョニング の 2種類の方法を使うことができます。

一貫した容量のプロビジョニング

一貫した容量のプロビジョニングでは、Deployment を使用することで、クラスタ内で常に実行されるプレースホルダ Pod を配置します。

プレースホルダ Pod の実行に必要な容量だけノードがプロビジョニングされ、ワークロードのスケールアウトが必要になった際はプレースホルダ Pod に置き換える形で Pod を追加することができます。

プレースホルダ Pod はワークロードの Pod に置き換えられますが、Deployment を使用してデプロイされているために、一度ワークロードの Pod に置き換えられたあと、プレースホルダ Pod の数を維持しようとします。そのため、GKE クラスタは新たなノードを追加してプレースホルダ Pod を再作成します。
したがって、クラスタにはワークロードを実行するために必要な容量よりも多くの容量が常に確保される点には注意が必要です。

一貫した容量のプロビジョニングを使用した場合のスケールアウト動作のイメージ

単一イベント容量のプロビジョニング

単一イベント容量のプロビジョニングでは、Job を使用することで特定の期間だけプレースホルダ Pod を起動し、その実行に必要な容量だけノードをプロビジョニングします。

ワークロードのスケールアウトが必要になると、プレースホルダ Pod がワークロードの Pod と置き換わる点は一貫した容量のプロビジョニングと同様ですが、Job で実行されているため、置き換わったあとに Pod が再作成されることはありません。

単一イベント容量のプロビジョニングを使用した場合のスケールアウト動作のイメージ

2 つの方法の比較

プレースホルダ Pod の作成に Job を使用する場合(単一イベント容量のプロビジョニング)は Deployment を使用する場合(一貫した容量のプロビジョニング)とは異なり、終了したプレースホルダ Pod を再作成するような動作はしないため、プレースホルダ Pod の再作成するためにノードが追加されることはありません。したがって、プレースホルダ Pod 用に確保される容量は Job を使用したほうが抑えられます。

GKE の Standard モードでは起動しているノードあたりの時間料金、Autopilot モードでは Pod がリクエストしているリソース量あたりの時間料金が発生するため、必要なときだけプレースホルダ Pod を実行するほうにコストメリットがあります。

ただし、Job を使用する場合はワークロードがスケールアウトするタイミングをある程度は把握しておき、それに合わせて Job を作成しておく必要があります。また、一度置き換えられたプレースホルダ Pod は再作成されないため、一日に何度もスケーリングをする必要があるワークロードの場合には向きません。Deployment を使用していればプレースホルダ Pod は常に存在するため、ワークロードがスケールアウトする余裕を持ち続けることができます。

予備の容量プロビジョニングを使用する

ここからは、予備の容量プロビジョニングを使用することで、Pod の起動時間が改善されるかどうかを検証します。

使用するマニフェストファイル

当記事で使用するマニフェストファイルは、公式ドキュメントに記載されているものを参考にしています。

priorityclasses.yaml

Pod に紐付けることができる PriorityClass オブジェクトを 2つ作成するマニフェストファイルです。
優先度は value フィールドに設定し、数値が大きいほど Pod の実行優先度が高くなります。

# priorityclasses.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority
value: -10  # この PriorityClass を使用する Pod の優先度(値が小さいほど優先度が低い)
preemptionPolicy: Never  # この PriorityClass を使用する Pod は、これより優先度の低い Pod を削除しない(=Never)
globalDefault: false
description: "Low priority workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: default-priority
value: 0  # この PriorityClass を使用する Pod の優先度
preemptionPolicy: PreemptLowerPriority  # この PriorityClass を使用する Pod は、これより優先度の低い Pod を削除する(=PreemptLowerPriority)
globalDefault: true  # Pod に PriorityClass が明示的に設定されていない場合、これをデフォルトの PriorityClass とする
description: "The global default priority."

1つ目の PriorityClass は低優先度のプレースホルダ Pod に紐付けるもので、優先度の値が -10 に設定されています。
2つ目の PriorityClass は globalDefault フィールドの値が true に設定されており、GKE クラスタにデプロイされる Pod にデフォルトで紐付けられます。preemptionPolicy フィールドの値が PreemptLowerPriority に設定されているため、この PriorityClass が紐付いた Pod が起動する際にノードの容量が足りていないと、優先度がより低い Pod を削除してから起動するように動作します。

test-deployment.yaml

このマニフェストファイルでは、ワークロードの Pod を想定したサンプルの Pod を 5 つ実行する Deployment を作成します。 PriorityClass を指定していないため、priorityclasses.yaml により作成された PriorityClass がある場合、これらの Pod の優先度は 0となります。

# 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

capacity-res-deployment.yaml(一貫した容量のプロビジョニングで使用)

プレースホルダ Pod を作成するためのマニフェストファイルでは、priorityClassName に低優先度の PriorityClass を指定し、10 個の Pod が優先度 -10 で作成されるようにします。
これらの Pod はノードの容量を予め確保しておき、ワークロードのスケールアウトが必要になったとき、ワークロードの Pod と置き換わります。

# 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  # 低優先度の PriorityClass を指定
      terminationGracePeriodSeconds: 0
      containers:
      - name: ubuntu
        image: ubuntu
        command: ["sleep"]
        args: ["infinity"]
        resources:
          requests:
            cpu: 500m
            memory: 500Mi

capacity-res-job.yaml(単一イベント容量のプロビジョニングで使用)

単一イベント容量のプロビジョニングでは Job を使用してプレースホルダ Pod を実行します。
このマニフェストファイルでは低優先度の Pod を Job として実行し、ワークロード用の Pod による置き換えが起こるか sleep コマンドの処理が終わる(10時間が経過する)と Pod が削除されます。

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

GKE クラスタの作成

以下のコマンドを使用して、検証用のクラスタを作成します。
当記事では Autopilot モードのクラスタを使用していきます。

# Autopilot モードの GKE クラスタを作成する 
$ gcloud container clusters create-auto {クラスタ名} \
    --region={リージョン} \
    --project={プロジェクトID}

予備の容量プロビジョニングを使用しない場合

まず、予備の容量プロビジョニングを使用しない場合のワークロード用 Pod の起動時間を計測してみます。
作成した GKE クラスタのノードの初期状態を確認します。

# ノードの状態を確認
$ kubectl get nodes
  
# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE   VERSION
gk3-cluster-sasashun-gke-default-pool-69f9cf5d-m972   Ready    <none>   74m   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-b9c30e3b-z2jn   Ready    <none>   74m   v1.26.5-gke.1200

次に、Pod の起動時間を確認するために、kubectl get pods コマンドで --watch オプションを使用して Pod のステータスをモニタリングします。

# Pod のステータスをモニタリングする
$ kubectl get pods -w

別のターミナルから test-deployment.yaml をクラスタに適用し、ワークロード用の Pod をデプロイします。

# Pod のデプロイ(別のターミナルで実施)
$ kubectl apply -f test-deployment.yaml

最初のターミナルで Pod のステータスをモニタリングしているため、各 Pod の STATUS 列が Running になるまで待機します。
今回の検証時の出力を以下に記載します。Pod がすべて実行されるまで、2分 30秒ほど要したことがわかります。

# 出力例
$ kubectl get pods -w
NAME                        READY   STATUS    RESTARTS   AGE
helloweb-75f7cfd4f7-mnn8f   0/1     Pending   0          1s
helloweb-75f7cfd4f7-mnn8f   0/1     Pending   0          1s
helloweb-75f7cfd4f7-gz58p   0/1     Pending   0          0s
helloweb-75f7cfd4f7-gz58p   0/1     Pending   0          0s
helloweb-75f7cfd4f7-hhf62   0/1     Pending   0          0s
helloweb-75f7cfd4f7-hhf62   0/1     Pending   0          0s
helloweb-75f7cfd4f7-sng6f   0/1     Pending   0          0s
helloweb-75f7cfd4f7-6j9sx   0/1     Pending   0          0s
helloweb-75f7cfd4f7-sng6f   0/1     Pending   0          0s
helloweb-75f7cfd4f7-6j9sx   0/1     Pending   0          0s
helloweb-75f7cfd4f7-mnn8f   0/1     Pending   0          77s
helloweb-75f7cfd4f7-gz58p   0/1     Pending   0          76s
helloweb-75f7cfd4f7-hhf62   0/1     Pending   0          76s
helloweb-75f7cfd4f7-sng6f   0/1     Pending   0          76s
helloweb-75f7cfd4f7-6j9sx   0/1     Pending   0          76s
helloweb-75f7cfd4f7-mnn8f   0/1     ContainerCreating   0          77s
helloweb-75f7cfd4f7-gz58p   0/1     ContainerCreating   0          76s
helloweb-75f7cfd4f7-hhf62   0/1     ContainerCreating   0          76s
helloweb-75f7cfd4f7-sng6f   0/1     Pending             0          82s
helloweb-75f7cfd4f7-6j9sx   0/1     Pending             0          82s
helloweb-75f7cfd4f7-sng6f   0/1     ContainerCreating   0          82s
helloweb-75f7cfd4f7-6j9sx   0/1     ContainerCreating   0          82s
helloweb-75f7cfd4f7-mnn8f   1/1     Running             0          2m18s
helloweb-75f7cfd4f7-gz58p   1/1     Running             0          2m20s
helloweb-75f7cfd4f7-hhf62   1/1     Running             0          2m23s
helloweb-75f7cfd4f7-sng6f   1/1     Running             0          2m25s
helloweb-75f7cfd4f7-6j9sx   1/1     Running             0          2m30s

次に、ノードのスケールアウトが行われたかどうかを確認します。
ワークロードの Pod を実行するために 2つのノードが追加されていることがわかります。ノードの追加を待ってから Pod が起動されるため、ノードの追加待ち時間だけ Pod の起動に時間がかかってしまっています。

# ノードの数を確認する
$ kubectl get nodes
  
# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gk3-cluster-sasashun-gke-default-pool-69f9cf5d-m972   Ready    <none>   79m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-b9c30e3b-z2jn   Ready    <none>   79m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-5d602c8d-zwcp   Ready    <none>   2m29s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-ed49f4e8-cgbn   Ready    <none>   2m25s   v1.26.5-gke.1200

Pod を削除し、ノードの数が元に戻るまで待機します。

# Pod を削除する
$ kubectl delete -f test-deployment.yaml
# ノード数が元に戻ったことを確認
$ kubectl get nodes
  
# 出力例
$ kubectl get nodes
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE   VERSION
gk3-cluster-sasashun-gke-default-pool-69f9cf5d-m972   Ready    <none>   85m   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-b9c30e3b-z2jn   Ready    <none>   85m   v1.26.5-gke.1200

一貫した容量のプロビジョニングを使用する

まず、一貫した容量のプロビジョニングを使用してみます。
この方法では、Deployment によってクラスタ上でプレースホルダ Pod が常に実行されるようにします。

PriorityClass オブジェクトを作成する

Pod に優先度を設定するために PriorityClass オブジェクトを作成します。
クラスタに priorityclasses.yaml を適用します。

# PriorityClass のマニフェストを適用する
$ kubectl apply -f priorityclasses.yaml

プレースホルダ Pod をデプロイする

capacity-res-deployment.yaml をクラスタに適用し、プレースホルダ Pod を作成します。
プレースホルダ Pod が全て作成されるまで待機します。

# プレースホルダ Pod のマニフェストを適用する
$ kubectl apply -f capacity-res-deployment.yaml
# プレースホルダ Pod のステータスを確認する
$ kubectl get pods
  
# 出力例
$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
capacity-res-deploy-74b9b79578-4cvms   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-85tgp   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-89qfz   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-8v2dh   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-d4kth   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-dvxrd   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-gvqcp   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-kdxzl   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-kxr4w   1/1     Running   0          2m43s
capacity-res-deploy-74b9b79578-rzjvk   1/1     Running   0          2m43s

ノードの状態を確認すると、プレースホルダ Pod を作成するためにノードが追加されていることがわかります。

# ノードのスケールアウトを確認する
$ kubectl get nodes 
  
# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gk3-cluster-sasashun-gke-default-pool-69f9cf5d-m972   Ready    <none>   90m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-b9c30e3b-z2jn   Ready    <none>   90m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-144d029c-fjgv   Ready    <none>   3m2s    v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-144d029c-q4xg   Ready    <none>   3m1s    v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-5d602c8d-p78l   Ready    <none>   2m59s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-ed49f4e8-67dq   Ready    <none>   3m1s    v1.26.5-gke.1200

ワークロード用 Pod のデプロイ

ワークロード用の Pod をデプロイし、Pod の起動時間とプレースホルダ Pod の動作を確認します。
まず、Pod のステータスをモニタリングするために以下のコマンドを実行します。

# Pod のステータスをモニタリングする
$ kubectl get pods -w

続いて、別のターミナルで test-deployment.yaml をクラスタに適用し、ワークロード用の Pod をデプロイします。

# ワークロード用 Pod のデプロイ(別のターミナルで実施)
$ kubectl apply -f test-deployment.yaml

ワークロード用 Pod のマニフェストファイルを適用したあと Pod のステータスをモニタリングしているターミナルを確認すると、優先度が低く設定されているプレースホルダ Pod が削除され、代わりにワークロード用 Pod が作成されていることがわかります。
プレースホルダ Pod が事前にノード容量を確保しているため、ワークロード用 Pod は素早く作成されます。今回の検証では 15秒以内に全てのワークロード用 Pod が起動していることがわかります。

# 出力例
$ kubectl get pods -w
NAME                                   READY   STATUS    RESTARTS   AGE
capacity-res-deploy-74b9b79578-4cvms   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-85tgp   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-89qfz   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-8v2dh   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-d4kth   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-dvxrd   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-gvqcp   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-kdxzl   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-kxr4w   1/1     Running   0          6m19s
capacity-res-deploy-74b9b79578-rzjvk   1/1     Running   0          6m19s
helloweb-75f7cfd4f7-plv6r              0/1     Pending   0          0s
helloweb-75f7cfd4f7-plv6r              0/1     Pending   0          0s
helloweb-75f7cfd4f7-plv6r              0/1     ContainerCreating   0          0s
helloweb-75f7cfd4f7-d8tdv              0/1     Pending             0          0s
helloweb-75f7cfd4f7-lnbbh              0/1     Pending             0          0s
helloweb-75f7cfd4f7-n67w5              0/1     Pending             0          1s
helloweb-75f7cfd4f7-nwknn              0/1     Pending             0          1s
capacity-res-deploy-74b9b79578-kdxzl   1/1     Running             0          6m51s
capacity-res-deploy-74b9b79578-kdxzl   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-kdxzl   1/1     Terminating         0          6m51s
helloweb-75f7cfd4f7-d8tdv              0/1     Pending             0          1s
capacity-res-deploy-74b9b79578-fdmfl   0/1     Pending             0          0s
capacity-res-deploy-74b9b79578-d4kth   1/1     Running             0          6m51s
capacity-res-deploy-74b9b79578-d4kth   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-d4kth   1/1     Terminating         0          6m51s
helloweb-75f7cfd4f7-lnbbh              0/1     Pending             0          1s
capacity-res-deploy-74b9b79578-gvqcp   1/1     Running             0          6m51s
capacity-res-deploy-74b9b79578-gvqcp   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-gvqcp   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-p48dn   0/1     Pending             0          0s
helloweb-75f7cfd4f7-n67w5              0/1     Pending             0          1s
capacity-res-deploy-74b9b79578-dvxrd   1/1     Running             0          6m51s
capacity-res-deploy-74b9b79578-dvxrd   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-dvxrd   1/1     Terminating         0          6m51s
capacity-res-deploy-74b9b79578-bq9bm   0/1     Pending             0          0s
helloweb-75f7cfd4f7-nwknn              0/1     Pending             0          1s
capacity-res-deploy-74b9b79578-fdmfl   0/1     Pending             0          0s
capacity-res-deploy-74b9b79578-p48dn   0/1     Pending             0          0s
capacity-res-deploy-74b9b79578-bq9bm   0/1     Pending             0          0s
capacity-res-deploy-74b9b79578-55mx9   0/1     Pending             0          0s
capacity-res-deploy-74b9b79578-55mx9   0/1     Pending             0          0s
helloweb-75f7cfd4f7-lnbbh              0/1     Pending             0          3s
helloweb-75f7cfd4f7-nwknn              0/1     Pending             0          3s
helloweb-75f7cfd4f7-n67w5              0/1     Pending             0          3s
helloweb-75f7cfd4f7-d8tdv              0/1     Pending             0          3s
helloweb-75f7cfd4f7-lnbbh              0/1     ContainerCreating   0          3s
helloweb-75f7cfd4f7-d8tdv              0/1     ContainerCreating   0          3s
helloweb-75f7cfd4f7-nwknn              0/1     ContainerCreating   0          3s
helloweb-75f7cfd4f7-n67w5              0/1     ContainerCreating   0          3s
helloweb-75f7cfd4f7-plv6r              1/1     Running             0          11s
helloweb-75f7cfd4f7-d8tdv              1/1     Running             0          12s
helloweb-75f7cfd4f7-lnbbh              1/1     Running             0          13s
helloweb-75f7cfd4f7-nwknn              1/1     Running             0          13s
helloweb-75f7cfd4f7-n67w5              1/1     Running             0          14s

さらにしばらく待機すると、Deployment に設定されたプレースホルダ Pod の数を維持するために新たなプレースホルダ Pod が作成されます。

# 出力例
$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
capacity-res-deploy-74b9b79578-4cvms   1/1     Running   0          8m53s
capacity-res-deploy-74b9b79578-55mx9   1/1     Running   0          2m2s
capacity-res-deploy-74b9b79578-85tgp   1/1     Running   0          8m53s
capacity-res-deploy-74b9b79578-89qfz   1/1     Running   0          8m53s
capacity-res-deploy-74b9b79578-8v2dh   1/1     Running   0          8m53s
capacity-res-deploy-74b9b79578-bq9bm   1/1     Running   0          2m2s
capacity-res-deploy-74b9b79578-fdmfl   1/1     Running   0          2m2s
capacity-res-deploy-74b9b79578-kxr4w   1/1     Running   0          8m53s
capacity-res-deploy-74b9b79578-p48dn   1/1     Running   0          2m2s
capacity-res-deploy-74b9b79578-rzjvk   1/1     Running   0          8m53s
helloweb-75f7cfd4f7-d8tdv              1/1     Running   0          2m3s
helloweb-75f7cfd4f7-lnbbh              1/1     Running   0          2m3s
helloweb-75f7cfd4f7-n67w5              1/1     Running   0          2m3s
helloweb-75f7cfd4f7-nwknn              1/1     Running   0          2m3s
helloweb-75f7cfd4f7-plv6r              1/1     Running   0          2m3s

ノードの状態を確認すると、置き換えられたぶんのプレースホルダ Pod を実行するために、ノードが追加されていることがわかります。
このように、一貫した容量のプロビジョニングを使用する場合、プレースホルダ Pod 用の容量が常に確保されることになります。

# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gk3-cluster-sasashun-gke-default-pool-69f9cf5d-m972   Ready    <none>   95m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-b9c30e3b-z2jn   Ready    <none>   95m     v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-144d029c-fjgv   Ready    <none>   8m16s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-144d029c-q4xg   Ready    <none>   8m15s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-5d602c8d-p78l   Ready    <none>   8m13s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-ed49f4e8-67dq   Ready    <none>   8m15s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-2-af991da7-cq9c   Ready    <none>   86s     v1.26.5-gke.1200

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

最後に、単一イベント容量のプロビジョニングを使用して ワークロード用 Pod をデプロイします。
当記事ではクラスタの状態を初期化するために一度クラスタを削除し、再作成しています。

ノードの初期状態を確認する

クラスタを再作成したため、まずはノードの初期状態を確認します。

# ノードの状態を確認
$ kubectl get nodes
  
# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE   VERSION
gk3-cluster-sasashun-gke-default-pool-76ea9d98-xj4v   Ready    <none>   99s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-d830f363-f5bt   Ready    <none>   98s   v1.26.5-gke.1200

PriorityClass オブジェクトを作成する

改めて priorityclasses.yaml を適用し、PriorityClass を作成します。

# PriorityClass のマニフェストを適用する
$ kubectl apply -f priorityclasses.yaml

プレースホルダ Pod をデプロイする

capacity-res-job.yaml をクラスタに適用し、プレースホルダ Pod を実行する Job を作成します。 プレースホルダ Pod が全て作成されるまで待機します。

# プレースホルダ Job のマニフェストを適用する
$ kubectl apply -f capacity-res-job.yaml
# ジョブのステータスを確認する
$ kubectl get jobs
  
# 出力例
$ kubectl get jobs
NAME               COMPLETIONS   DURATION   AGE
capacity-res-job   0/1 of 10     114s       114s
# Job で起動されたプレースホルダ Pod を確認する
$ kubectl get pods
  
# 出力例
$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
capacity-res-job-fc8v2   1/1     Running   0          3m4s
capacity-res-job-g56jm   1/1     Running   0          3m4s
capacity-res-job-ks6j7   1/1     Running   0          3m3s
capacity-res-job-lzpqk   1/1     Running   0          3m4s
capacity-res-job-n9w6p   1/1     Running   0          3m4s
capacity-res-job-nsmks   1/1     Running   0          3m3s
capacity-res-job-ph5cp   1/1     Running   0          3m3s
capacity-res-job-rh8cq   1/1     Running   0          3m3s
capacity-res-job-t7h4t   1/1     Running   0          3m3s
capacity-res-job-zxgg4   1/1     Running   0          3m3s

ノードの状態を確認すると、プレースホルダ Pod を作成するためにノードが追加されていることがわかります。

# ノードのスケールアウトを確認する
$ kubectl get nodes 
  
# 出力例
$ kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gk3-cluster-sasashun-gke-default-pool-76ea9d98-xj4v   Ready    <none>   6m7s    v1.26.5-gke.1200
gk3-cluster-sasashun-gke-default-pool-d830f363-f5bt   Ready    <none>   6m6s    v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-1dc5c9f6-6g97   Ready    <none>   2m17s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-1dc5c9f6-fl7d   Ready    <none>   2m16s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-4d328f57-bm4l   Ready    <none>   2m19s   v1.26.5-gke.1200
gk3-cluster-sasashun-gke-publi-pool-1-b29f6e44-xkhk   Ready    <none>   2m19s   v1.26.5-gke.1200

ワークロード用 Pod のデプロイ

ワークロード用の Pod をデプロイし、Pod の起動時間とプレースホルダ Pod の動作を確認します。
まず、Pod のステータスをモニタリングするために以下のコマンドを実行します。

# Pod のステータスをモニタリングする
$ kubectl get pods -w

続いて、別のターミナルで test-deployment.yaml をクラスタに適用し、ワークロード用の Pod をデプロイします。

# ワークロード用 Pod のデプロイ(別のターミナルで実施)
$ kubectl apply -f test-deployment.yaml

Job によってプレースホルダ Pod がノード容量を確保しているため、ワークロード用 Pod は素早く作成されます。今回も 15秒以内に全てのワークロード用 Pod が起動していることがわかります。

# 出力例
$ kubectl get pods -w
NAME                     READY   STATUS    RESTARTS   AGE
capacity-res-job-fc8v2   1/1     Running   0          3m47s
capacity-res-job-g56jm   1/1     Running   0          3m47s
capacity-res-job-ks6j7   1/1     Running   0          3m46s
capacity-res-job-lzpqk   1/1     Running   0          3m47s
capacity-res-job-n9w6p   1/1     Running   0          3m47s
capacity-res-job-nsmks   1/1     Running   0          3m46s
capacity-res-job-ph5cp   1/1     Running   0          3m46s
capacity-res-job-rh8cq   1/1     Running   0          3m46s
capacity-res-job-t7h4t   1/1     Running   0          3m46s
capacity-res-job-zxgg4   1/1     Running   0          3m46s
helloweb-75f7cfd4f7-rvh8w   0/1     Pending   0          0s
helloweb-75f7cfd4f7-rvh8w   0/1     Pending   0          0s
helloweb-75f7cfd4f7-rvh8w   0/1     ContainerCreating   0          0s
helloweb-75f7cfd4f7-6wq75   0/1     Pending             0          0s
helloweb-75f7cfd4f7-2wblr   0/1     Pending             0          0s
helloweb-75f7cfd4f7-5wjvd   0/1     Pending             0          1s
helloweb-75f7cfd4f7-llkfh   0/1     Pending             0          1s
capacity-res-job-zxgg4      1/1     Running             0          4m7s
capacity-res-job-zxgg4      1/1     Terminating         0          4m7s
helloweb-75f7cfd4f7-6wq75   0/1     Pending             0          1s
capacity-res-job-ph5cp      1/1     Running             0          4m7s
capacity-res-job-zxgg4      1/1     Terminating         0          4m7s
capacity-res-job-ph5cp      1/1     Terminating         0          4m7s
helloweb-75f7cfd4f7-2wblr   0/1     Pending             0          1s
capacity-res-job-ph5cp      1/1     Terminating         0          4m7s
capacity-res-job-rh8cq      1/1     Running             0          4m7s
capacity-res-job-rh8cq      1/1     Terminating         0          4m7s
helloweb-75f7cfd4f7-5wjvd   0/1     Pending             0          1s
capacity-res-job-fc8v2      1/1     Running             0          4m8s
capacity-res-job-fc8v2      1/1     Terminating         0          4m8s
capacity-res-job-rh8cq      1/1     Terminating         0          4m7s
helloweb-75f7cfd4f7-llkfh   0/1     Pending             0          1s
capacity-res-job-fc8v2      1/1     Terminating         0          4m8s
capacity-res-job-n9w6p      1/1     Terminating         0          4m9s
capacity-res-job-g56jm      1/1     Terminating         0          4m9s
capacity-res-job-nsmks      1/1     Terminating         0          4m8s
capacity-res-job-ks6j7      1/1     Terminating         0          4m8s
capacity-res-job-t7h4t      1/1     Terminating         0          4m8s
capacity-res-job-lzpqk      1/1     Terminating         0          4m9s
capacity-res-job-t7h4t      1/1     Terminating         0          4m8s
capacity-res-job-nsmks      1/1     Terminating         0          4m8s
capacity-res-job-zxgg4      1/1     Terminating         0          4m8s
capacity-res-job-ks6j7      1/1     Terminating         0          4m8s
capacity-res-job-ph5cp      1/1     Terminating         0          4m8s
capacity-res-job-n9w6p      1/1     Terminating         0          4m9s
capacity-res-job-g56jm      1/1     Terminating         0          4m9s
capacity-res-job-fc8v2      1/1     Terminating         0          4m9s
helloweb-75f7cfd4f7-llkfh   0/1     Pending             0          2s
helloweb-75f7cfd4f7-6wq75   0/1     Pending             0          2s
helloweb-75f7cfd4f7-2wblr   0/1     Pending             0          2s
helloweb-75f7cfd4f7-5wjvd   0/1     Pending             0          2s
capacity-res-job-rh8cq      1/1     Terminating         0          4m8s
capacity-res-job-lzpqk      1/1     Terminating         0          4m9s
helloweb-75f7cfd4f7-5wjvd   0/1     ContainerCreating   0          2s
helloweb-75f7cfd4f7-llkfh   0/1     ContainerCreating   0          2s
helloweb-75f7cfd4f7-6wq75   0/1     ContainerCreating   0          2s
helloweb-75f7cfd4f7-2wblr   0/1     ContainerCreating   0          2s
helloweb-75f7cfd4f7-rvh8w   1/1     Running             0          9s
helloweb-75f7cfd4f7-2wblr   1/1     Running             0          9s
helloweb-75f7cfd4f7-6wq75   1/1     Running             0          10s
helloweb-75f7cfd4f7-5wjvd   1/1     Running             0          12s
helloweb-75f7cfd4f7-llkfh   1/1     Running             0          13s

また、Job から作成された優先度の低いプレースホルダ Pod はすべて削除されています。
このように、Job を使用する場合は、置き換えが起こった後にプレースホルダ Pod を再作成するための容量が確保されないため、使用リソースを節約できる反面、再度スケールアウトが必要になったときに対応することができなくなります。

# 出力例
$ kubectl get po
NAME                        READY   STATUS    RESTARTS   AGE
helloweb-75f7cfd4f7-2wblr   1/1     Running   0          3m10s
helloweb-75f7cfd4f7-6wq75   1/1     Running   0          3m10s
helloweb-75f7cfd4f7-cclwt   1/1     Running   0          2m
helloweb-75f7cfd4f7-kw7t5   1/1     Running   0          2m
helloweb-75f7cfd4f7-rvh8w   1/1     Running   0          3m10s

佐々木 駿太 (記事一覧)

G-gen最北端、北海道在住のクラウドソリューション部エンジニア

2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。

趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。