Pod スナップショットから復元する

Google Kubernetes Engine(GKE)Pod スナップショットは、実行中の Pod のスナップショットを復元することで、ワークロードの起動レイテンシを改善します。Pod スナップショットは、メモリやルート ファイル システムの変更など、Pod の状態全体を保存します。新しいレプリカが作成されると、Pod は新しい状態から初期化されるのではなく、スナップショットから復元されます。Pod は、スナップショットが取得された時点から実行を再開します。

このドキュメントでは、ワークロードで GKE Pod スナップショットを有効にして構成する方法について説明します。

Pod スナップショットの仕組みの詳細については、Pod スナップショットについてをご覧ください。

始める前に

作業を始める前に、次のタスクが完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。gcloud CLI をインストール済みの場合は、gcloud components update コマンドを実行して最新のバージョンを取得します。以前のバージョンの gcloud CLI では、このドキュメントのコマンドを実行できない場合があります。

Pod スナップショットを有効にする

Pod スナップショットを有効にするには、まず Pod スナップショット機能を有効にしてクラスタを作成または更新します。次に、GKE Sandbox で実行するノードプールを作成または更新します。

  1. クラスタでこの機能を有効にするには、次のいずれかの操作を行います。

    • 新しいクラスタで Pod スナップショットを有効にするには、次のコマンドを実行します。

      gcloud beta container clusters create CLUSTER_NAME \
          --enable-pod-snapshots \
          --cluster-version=CLUSTER_VERSION \
          --workload-pool=PROJECT_ID.svc.id.goog \
          --workload-metadata=GKE_METADATA
      

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

      • CLUSTER_NAME: クラスタの名前。
      • CLUSTER_VERSION: 新しいクラスタのバージョン。1.34.1-gke.3084001 以降にする必要があります。
      • PROJECT_ID: プロジェクト ID。
    • 既存のクラスタで Pod スナップショットを有効にするには、次の操作を行います。

      1. クラスタをバージョン 1.34.1-gke.3084001 以降に更新します。

        gcloud container clusters upgrade CLUSTER_NAME \
            --node-pool=NODEPOOL_NAME \
            --cluster-version=CLUSTER_VERSION
        

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

        • CLUSTER_NAME: クラスタの名前。
        • NODEPOOL_VERSION: ノードプールの名前。
        • CLUSTER_VERSION: 新しいクラスタを更新するバージョン。1.34.1-gke.3084001 以降にする必要があります。
      2. クラスタで Pod スナップショットを有効にします。

        gcloud container clusters update CLUSTER_NAME \
           --workload-pool=PROJECT_ID .svc.id.goog" \
           --enable-pod-snapshots
        

        PROJECT_ID は、実際のプロジェクト ID に置き換えます。

  2. Standard クラスタで GKE Sandbox を有効にします。

    gcloud container node-pools create NODE_POOL_NAME \
      --cluster=CLUSTER_NAME \
      --node-version=NODE_VERSION \
      --machine-type=MACHINE_TYPE \
      --image-type=cos_containerd \
      --sandbox type=gvisor
    

    次の変数を置き換えます。

    • NODE_POOL_NAME: 新しいノードプールの名前。
    • NODE_VERSION: ノードプールに使用するバージョン。
    • MACHINE_TYPE: ノードに使用するマシンのタイプ

    gVisor の使用方法については、GKE Sandbox を使用してワークロードを分離するをご覧ください。

スナップショットを保存する

Pod スナップショットは Cloud Storage バケットに保存されます。このバケットには、メモリと(必要に応じて)GPU の状態が含まれます。Pod スナップショットには、Pod のサービス アカウントを有効にして使用し、Cloud Storage に対する認証を行うために Workload Identity Federation for GKE が必要です。

Pod スナップショットには、バケットに対する次の構成が必要です。

  • 階層型名前空間: 読み取りと書き込みの秒間クエリ数を増やすには、有効にする必要があります。階層型名前空間では、均一なバケットレベルのアクセスも有効にする必要があります。
  • 削除(復元可能): Pod スナップショットは並列複合アップロードを使用するため、削除(復元可能)などのデータ保護機能を無効にする必要があります。有効のままにしておくと、一時オブジェクトの削除によりストレージの請求額が大幅に増加する可能性があります。
  • ロケーション: スナップショットが異なるリージョン間で転送されるとパフォーマンスに影響する可能性があるため、Cloud Storage バケットのロケーションは GKE クラスタと同じにする必要があります。

Cloud Storage バケットを作成する

バケットと必要な権限を作成する手順は次のとおりです。

  1. Cloud Storage バケットを作成します。次のコマンドは、必要な構成でバケットを作成します。

    gcloud storage buckets create "gs://BUCKET_NAME" \
       --uniform-bucket-level-access \
       --enable-hierarchical-namespace \
       --soft-delete-duration=0d \
       --location="LOCATION"
    

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

    • BUCKET_NAME: バケットの名前。
    • LOCATION: バケットのロケーション。

    バケットの作成オプションの完全なリストについては、buckets create オプションをご覧ください。

Cloud Storage バケットにアクセスする権限をワークロードに付与する

デフォルトでは、GKE には Cloud Storage にアクセスする権限がありません。スナップショット ファイルの読み取りと書き込みを行うには、ワークロード Pod で使用される Kubernetes サービス アカウント(KSA)に IAM 権限を付与する必要があります。

  1. kubectl コマンドを使用してクラスタと通信できるように、認証情報を取得します。

    gcloud container clusters get-credentials "CLUSTER_NAME"
    
  2. Pod ごとに、次の操作を行います。

    1. 各 Pod の KSA を作成します。

      kubectl create serviceaccount "KSA_NAME" \
          --namespace "NAMESPACE"
      

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

      • KSA_NAME: KSA の名前。
      • NAMESPACE: Pod の名前空間。
    2. バケットにアクセスする権限を KSA に付与します。

      gcloud storage buckets add-iam-policy-binding "gs://BUCKET_NAME" \
          --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME" \
          --role="roles/storage.bucketViewer"
      
      gcloud storage buckets add-iam-policy-binding "gs://BUCKET_NAME" \
          --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME" \
          --role="roles/storage.objectUser"
      

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

      • PROJECT_NUMBER: プロジェクトの番号。
      • PROJECT_ID: プロジェクト ID。

(省略可)Cloud Storage バケットのマネージド フォルダを作成する

フォルダを作成すると、スナップショットの権限を、相互に信頼できない Pod から分離できます。これは、マルチテナントのユースケースで役立ちます。マネージド フォルダを設定する手順は次のとおりです。

  1. Pod スナップショットに必要な権限のみを含むカスタム IAM ロールを作成します。

    gcloud iam roles create podSnapshotGcsReadWriter \
        --project="PROJECT_ID" \
        --permissions="storage.objects.get,storage.objects.create,storage.objects.delete,storage.folders.create"
    
  2. ターゲット名前空間内のすべての KSA に roles/storage.bucketViewer ロールを付与します。このロールを使用すると、KSA はバケット メタデータを読み取りますが、バケット内のオブジェクトに対する読み取り権限や書き込み権限は付与されません。

    gcloud storage buckets add-iam-policy-binding "gs://BUCKET_NAME" \
        --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/namespace/NAMESPACE" \
        --role="roles/storage.bucketViewer"
    

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

    • PROJECT_NUMBER: プロジェクトの番号。
    • PROJECT_ID: プロジェクト ID。
  3. Pod スナップショットを保存する必要がある KSA ごとに、次の操作を行います。

    1. KSA のマネージド フォルダを作成します。

      gcloud storage managed-folders create "gs://BUCKET_NAME/FOLDER_PATH/"
      

      FOLDER_PATH は、マネージド フォルダのパス(my-app-snapshots など)に置き換えます。

    2. マネージド フォルダに対するカスタム podSnapshotGcsReadWriter ロールを KSA に付与します。

      gcloud storage managed-folders add-iam-policy-binding "gs://BUCKET_NAME/FOLDER_PATH/" \
          --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME" \
          --role="projects/PROJECT_ID/roles/podSnapshotGcsReadWriter"
      

      KSA_NAME は、KSA の名前に置き換えます。

スナップショットのストレージを構成する

スナップショット ファイルの保存場所を指定するには、PodSnapshotStorageConfig リソースを作成します。

  1. 次の例では、Cloud Storage バケット BUCKET_NAME 内の FOLDER_PATH/ パスに Pod スナップショットを保存するように GKE を構成します。次のマニフェストを example-pod-snapshot-storage-config として保存します。

    apiVersion: podsnapshot.gke.io/v1alpha1
    kind: PodSnapshotStorageConfig
    metadata:
      name: example-pod-snapshot-storage-config
      namespace: NAMESPACE
    spec:
      snapshotStorageConfig:
        gcs:
          bucket: "BUCKET_NAME"
          path: "FOLDER_PATH"
    

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

    • NAMESPACE: Pod の名前空間。 デフォルトは default です。
    • BUCKET_NAME: Cloud Storage バケットの名前。
    • FOLDER_PATH: Cloud Storage マネージド フォルダのパス。
  2. 次のようにマニフェストを適用します。

    kubectl apply -f example-pod-snapshot-storage-config.yaml
    

スナップショット ポリシーを作成する

Pod のスナップショットを有効にするには、Pod のラベルに一致するセレクタを含む PodSnapshotPolicy リソースを作成します。

  1. 次の例では、app: my-app ラベルが付いた Pod に適用され、example-pod-snapshot-storage-config ストレージ構成を使用するポリシーを作成します。次のマニフェストを example-pod-snapshot-policy.yaml として保存します。

    apiVersion: podsnapshot.gke.io/v1alpha1
    kind: PodSnapshotPolicy
    metadata:
      name: example-pod-snapshot-policy
      namespace: NAMESPACE
    spec:
      storageConfigName: example-pod-snapshot-storage-config
      selector:
        matchLabels:
          app: my-app
      triggerConfig:
        type: workload
        postCheckpoint: resume
    
  2. 次のようにマニフェストを適用します。

    kubectl apply -f example-pod-snapshot-policy.yaml --namespace NAMESPACE
    

スナップショットのサイズを最適化する

Pod スナップショットがトリガーされると、gVisor は次のものを含むすべてのコンテナの状態全体をキャプチャします。

  • アプリケーションの状態(メモリやレジスタなど)
  • ルート ファイル システムと tmpfsemptyDir ボリュームを含む)への変更
  • カーネルの状態(開いているファイル記述子、スレッド、ソケットなど)

スナップショットのサイズは、こうした要因によって決まります。スナップショットが大きいほど、保存と復元に時間がかかります。パフォーマンスを最適化するには、スナップショットをトリガーする前に、Pod がスナップショットから復元された後に不要になるアプリケーションの状態やファイルをクリーンアップする必要があります。

スナップショット サイズの最適化は、大規模言語モデル(LLM)などのワークロードで特に重要です。LLM サーバーは、モデルの重みを GPU に読み込む前に、ローカル ストレージ(rootfs または tmpfs)にダウンロードすることがよくあります。スナップショットが生成されると、GPU の状態とモデルの重みファイルの両方が保存されます。このシナリオでは、モデルが 100 GB の場合、結果のスナップショットは約 200 GB(100 GB のモデルファイルと、GPU の状態を表す 100 GB)になります。モデルの重みが GPU に読み込まれた後、ファイル システム上のファイルが、アプリケーションの実行に必要ではなくなることがよくあります。スナップショットをトリガーする前にこれらのモデルファイルを削除すると、スナップショットのサイズを半分に減らし、レイテンシを大幅に削減してアプリケーションを復元できます。

ワークロードからスナップショットをトリガーする

アプリケーション コード内からスナップショットをトリガーするには、スナップショットの準備ができたときにシグナルを送信するようにアプリケーションを構成します。準備完了を通知するには、/proc/gvisor/checkpoint ファイルに 1 を書き込みます(例: echo 1 > /proc/gvisor/checkpoint.)。書き込みオペレーションは、スナップショット プロセスを非同期で開始し、すぐに戻ります。同じファイル記述子から読み取ると、スナップショットと復元が完了し、ワークロードの再開の準備が整うまで、読み取りプロセスがブロックされます。

実際の使用方法はアプリケーションによって異なりますが、次の例は Python アプリケーションのスナップショット トリガーを示しています。このワークロードの例からスナップショットをトリガーするには、次の操作を行います。

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-app
      namespace: NAMESPACE
      labels:
        app: my-app
    spec:
      serviceAccountName: KSA_NAME
      runtimeClassName: gvisor
      containers:
      - name: my-container
        image: python:3.10-slim
        command: ["python3", "-c"]
        args:
          - |
            import time
            def trigger_snapshot():
              try:
                with open("/proc/gvisor/checkpoint", "r+") as f:
                  f.write("1")
                  res = f.read().rstrip()
                  print(f"GKE Pod Snapshot: {res}")
              except FileNotFoundError:
                print("GKE Pod Snapshot file does not exist -- Pod Snapshots is disabled")
                return
              except OSError as e:
                return e
            i = 0
            while True:
              print(f"Count: {i}", flush=True)
              if (i == 20): #simulate the application being ready to snapshot at 20th count
                trigger_snapshot()
              i += 1
              time.sleep(1)
        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
          requests:
            cpu: "250m"
            memory: "256Mi"
    
  2. アプリケーションをデプロイします。

    kubectl apply -f my-app.yaml
    
  3. スナップショットが作成されたことを確認するには、GKEPodSnapshotting イベントのアクティビティの履歴を確認します。

    kubectl get events -o \
    custom-columns=NAME:involvedObject.name,CREATIONTIME:.metadata.creationTimestamp,REASON:.reason,MESSAGE:.message \
    --namespace NAMESPACE \
    --field-selector involvedObject.name=my-app,reason=GKEPodSnapshotting
    

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

    NAME                                    CREATIONTIME           REASON               MESSAGE
    default/5b449f9c7c-bd7pc                 2025-11-05T16:25:11Z   GKEPodSnapshotting   Successfully checkpointed the pod to PodSnapshot
    

スナップショットを管理する

Pod スナップショットを作成すると、その時点の Pod の状態を保存するために PodSnapshot CRD リソースが作成されます。

名前空間内のすべての PodSnapshot リソースを表示するには、次のコマンドを実行します。

kubectl get podsnapshots.gke.io --namespace NAMESPACE

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

NAME                                   STATUS                  POLICY           AGE
de334898-1e7a-4cdb-9f2e-7cc2181c29e4   AllSnapshotsAvailable   example-policy   47h

スナップショットからワークロードを復元する

最新のスナップショットからワークロードを復元するには、スナップショットの取得後に既存の Pod を削除し、Pod を再デプロイします。また、同じ仕様で新しい Pod をデプロイすることもできます。GKE は、一致するスナップショットから Pod を自動的に復元します。

次の手順は、Pod を削除して再デプロイすることで、一致するスナップショットから Pod を復元する方法を示しています。

  1. Pod を削除します。

    kubectl delete -f POD_NAME.yaml
    

    POD_NAME は、Pod の名前に置き換えます(例: my-app)。

  2. Pod を再適用します。

    kubectl apply -f POD_NAME.yaml
    
  3. ログを表示して、スナップショットの復元を確認します。

    kubectl logs my-app --namespace NAMESPACE
    

    出力は、アプリケーションの構成方法によって異なります。アプリケーション例では、復元オペレーションが発生すると、ログに GKE Pod Snapshot: restore が表示されます。

スナップショットを無効にする

PodSnapshotPolicy CRD を削除すると、Pod のスナップショット作成と復元ができなくなります。実行中の Pod は、リソースの削除の影響を受けません。ただし、Pod の保存中または復元中にポリシーを削除すると、Pod が失敗状態になることがあります。

ポリシーで管理される新しい Pod のスナップショット作成と復元を無効にするには、次のコマンドを実行して PodSnapshotPolicy を削除します。

kubectl delete podsnapshotpolicies.podsnapshot.gke.io SNAPSHOT_POLICY --namespace=NAMESPACE

SNAPSHOT_POLICY は、削除する PodSnapshotPolicy の名前に置き換えます(例: example-pod-snapshot-policy)。

特定の PodSnapshot リソースを削除して、Pod が特定のスナップショットから復元されないようにすることもできます。PodSnapshot リソースを削除すると、Cloud Storage に保存されているファイルも削除されます。

特定のスナップショットが将来の復元に使用されないようにするには、次のコマンドを実行して PodSnapshot オブジェクトを削除します。

kubectl delete podsnapshots.podsnapshot.gke.io POD_SNAPSHOT_NAME --namespace=NAMESPACE

POD_SNAPSHOT_NAME は、削除するスナップショットの名前(example-podsnapshot など)に置き換えます。

次のステップ