从 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 快照需要 Workload Identity Federation for GKE 来启用并使用 Pod 的服务账号向 Cloud Storage 进行身份验证。

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. 向 KSA 授予受管文件夹的自定义 podSnapshotGcsReadWriter 角色:

      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. 以下示例将 GKE 配置为将 Pod 快照存储在 Cloud Storage 存储分区 BUCKET_NAME 内的 FOLDER_PATH/ 路径中。将以下清单保存为 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 启用快照,请创建一个 PodSnapshotPolicy 资源,其中包含与 Pod 标签匹配的选择器。

  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 会捕获所有容器的完整状态,包括:

  • 应用状态,例如内存和寄存器
  • 对根文件系统和 tmpfs(包括 emptyDir 卷)的更改
  • 内核状态,例如打开的文件描述符、线程和套接字

快照的大小由以下因素决定。较大的快照需要更长时间才能保存和恢复。为了优化性能,在触发快照之前,您应该清理在从快照恢复 Pod 后不需要的任何应用状态或文件。

优化快照大小对于大语言模型 (LLM) 等工作负载尤为重要。LLM 服务器通常会将模型权重下载到本地存储空间(rootfstmpfs),然后再将其加载到 GPU 中。拍摄快照时,系统会保存 GPU 状态和模型权重文件。在这种情况下,如果模型为 100 GB,则生成的快照约为 200 GB(100 GB 的模型文件,加上 100 GB 的 GPU 状态)。将模型权重加载到 GPU 后,应用通常不需要文件系统上的文件即可运行。通过在触发快照之前删除这些模型文件,您可以将快照大小减少一半,并以显著更低的延迟恢复应用。

从工作负载触发快照

如需从应用代码内触发快照,请将应用配置为在准备好拍摄快照时发送信号。如需发出就绪信号,请将 1 写入 /proc/gvisor/checkpoint 文件,例如 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 快照时,系统会创建一个 PodSnapshot CRD 资源来存储该时间点的 Pod 状态。

如需查看命名空间中的所有 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

后续步骤