Restaurar desde una captura de Pod

Las copias de seguridad de pods de Google Kubernetes Engine (GKE) ayudan a mejorar la latencia de inicio de las cargas de trabajo restaurando copias de seguridad de los pods en ejecución. Una captura de un pod guarda todo el estado del pod, incluida la memoria y los cambios en el sistema de archivos raíz. Cuando se crean réplicas nuevas, en lugar de inicializar el pod desde un estado nuevo, se restaura la captura. A continuación, el pod reanuda la ejecución desde el punto en el que se tomó la captura.

En este documento se explica cómo habilitar y configurar las instantáneas de pods de GKE para sus cargas de trabajo.

Para obtener más información sobre cómo funcionan las capturas de pods, consulta el artículo Acerca de las capturas de pods.

Antes de empezar

Antes de empezar, asegúrate de que has realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la CLI de gcloud, obtén la versión más reciente ejecutando el comando gcloud components update. Es posible que las versiones anteriores de la interfaz de línea de comandos de gcloud no admitan la ejecución de los comandos de este documento.

Habilitar las copias de seguridad de pods

Para habilitar las capturas de pods, primero crea o actualiza un clúster con la función de captura de pods habilitada. A continuación, crea o actualiza un grupo de nodos para que se ejecute en GKE Sandbox.

  1. Para habilitar la función en un clúster, sigue uno de estos pasos:

    • Para habilitar las instantáneas de pods en un clúster nuevo, ejecuta el siguiente comando:

      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
      

      Haz los cambios siguientes:

      • CLUSTER_NAME: el nombre de tu clúster.
      • CLUSTER_VERSION: la versión de tu nuevo clúster, que debe ser 1.34.1-gke.3084001 o posterior.
      • PROJECT_ID: tu ID de proyecto.
    • Para habilitar las instantáneas de pods en un clúster, sigue estos pasos:

      1. Actualiza el clúster a la versión 1.34.1-gke.3084001 o una posterior:

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

        Haz los cambios siguientes:

        • CLUSTER_NAME: el nombre de tu clúster.
        • NODEPOOL_VERSION: el nombre de tu grupo de nodos.
        • CLUSTER_VERSION: la versión a la que se debe actualizar el nuevo clúster, que debe ser 1.34.1-gke.3084001 o una posterior.
      2. Habilita las capturas de pods en el clúster:

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

        Sustituye PROJECT_ID por el ID del proyecto.

  2. Habilita GKE Sandbox en tu clúster estándar:

    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
    

    Sustituye las siguientes variables:

    • NODE_POOL_NAME: el nombre del nuevo grupo de nodos.
    • NODE_VERSION: la versión que se va a usar en el grupo de nodos.
    • MACHINE_TYPE: el tipo de máquina que se va a usar en los nodos.

    Para obtener más información sobre el uso de gVisor, consulta Aísla tus cargas de trabajo con GKE Sandbox.

Almacenar capturas

Las copias de los pods se almacenan en un segmento de Cloud Storage, que contiene la memoria y (opcionalmente) el estado de la GPU. Las copias de seguridad de pods requieren Workload Identity Federation for GKE para habilitar y usar la cuenta de servicio del pod con el fin de autenticarse en Cloud Storage.

Las instantáneas de pods requieren la siguiente configuración para el bucket:

  • Espacios de nombres jerárquicos: deben habilitarse para permitir un mayor número de consultas de lectura y escritura por segundo. Los espacios de nombres jerárquicos también requieren que se habilite el acceso uniforme a nivel de segmento.
  • Eliminación no definitiva: como las copias de seguridad de pods usan subidas compuestas paralelas, debes inhabilitar las funciones de protección de datos, como la eliminación no definitiva. Si se deja habilitada, la eliminación de los objetos temporales puede aumentar significativamente la factura de almacenamiento.
  • Ubicación: la ubicación del segmento de Cloud Storage debe ser la misma que la del clúster de GKE, ya que el rendimiento puede verse afectado si las copias de seguridad se transfieren entre regiones diferentes.

Crear un segmento de Cloud Storage

Para crear el segmento y los permisos necesarios, siga estos pasos:

  1. Crea un segmento de Cloud Storage. El siguiente comando crea un bucket con la configuración necesaria:

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

    Haz los cambios siguientes:

    • BUCKET_NAME: el nombre de tu segmento.
    • LOCATION: la ubicación de tu segmento.

    Para ver una lista completa de las opciones de creación de segmentos, consulta las opciones de buckets create.

Concede permiso a las cargas de trabajo para acceder al segmento de Cloud Storage

De forma predeterminada, GKE no tiene permisos para acceder a Cloud Storage. Para leer y escribir archivos de instantáneas, debes conceder permisos de gestión de identidades y accesos a la cuenta de servicio de Kubernetes (KSA) que usan los pods de tu carga de trabajo.

  1. Obtén las credenciales para comunicarte con tu clúster mediante comandos de kubectl:

    gcloud container clusters get-credentials "CLUSTER_NAME"
    
  2. Sigue estos pasos con cada pod:

    1. Crea un KSA para cada Pod:

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

      Haz los cambios siguientes:

      • KSA_NAME: el nombre de tu KSA.
      • NAMESPACE: el espacio de nombres de tus pods.
    2. Concede al KSA permiso para acceder al segmento:

      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"
      

      Haz los cambios siguientes:

      • PROJECT_NUMBER: tu número de proyecto.
      • PROJECT_ID: tu ID de proyecto.

(Opcional) Crear carpetas gestionadas para el segmento de Cloud Storage

Crear carpetas te permite aislar los permisos de las instantáneas de los pods que no son de confianza entre sí, lo que resulta útil en casos prácticos de varios arrendatarios. Para configurar carpetas gestionadas, sigue estos pasos:

  1. Crea un rol de gestión de identidades y accesos personalizado que solo contenga los permisos necesarios para las capturas de Pod:

    gcloud iam roles create podSnapshotGcsReadWriter \
        --project="PROJECT_ID" \
        --permissions="storage.objects.get,storage.objects.create,storage.objects.delete,storage.folders.create"
    
  2. Concede el rol roles/storage.bucketViewer a todos los KSA del espacio de nombres de destino. Este rol permite a las KSAs leer metadatos de segmentos, pero no les concede permisos de lectura o escritura para los objetos del segmento.

    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"
    

    Haz los cambios siguientes:

    • PROJECT_NUMBER: tu número de proyecto.
    • PROJECT_ID: tu ID de proyecto.
  3. Para cada KSA que necesite almacenar snapshots de pods, sigue estos pasos:

    1. Crea una carpeta gestionada para la KSA:

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

      Sustituye FOLDER_PATH por la ruta de la carpeta gestionada (por ejemplo, my-app-snapshots).

    2. Asigna a la cuenta de servicio con autorización el rol personalizado podSnapshotGcsReadWriter en la carpeta gestionada:

      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"
      

      Sustituye KSA_NAME por el nombre de la KSA.

Configurar el almacenamiento de las instantáneas

Para especificar dónde se almacenarán los archivos de la captura, crea un PodSnapshotStorageConfig recurso.

  1. En el siguiente ejemplo se configura GKE para almacenar las copias de seguridad de los pods en la ruta FOLDER_PATH/ del segmento de Cloud Storage BUCKET_NAME. Guarda el siguiente archivo de manifiesto como 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"
    

    Haz los cambios siguientes:

    • NAMESPACE: el espacio de nombres de tus pods. El valor predeterminado es default.
    • BUCKET_NAME: el nombre de tu segmento de Cloud Storage.
    • FOLDER_PATH: la ruta de la carpeta gestionada de Cloud Storage.
  2. Aplica el archivo de manifiesto:

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

Crear una política de capturas

Para habilitar las copias de seguridad de un pod, crea un recurso PodSnapshotPolicy con un selector que coincida con las etiquetas del pod.

  1. En el siguiente ejemplo se crea una política que se aplica a los pods con la etiqueta app: my-app y usa la configuración de almacenamiento example-pod-snapshot-storage-config. Guarda el siguiente manifiesto como 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. Aplica el archivo de manifiesto:

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

Optimizar el tamaño de las capturas

Cuando se activa una captura de un pod, gVisor registra todo el estado de todos los contenedores, incluidos los siguientes:

  • Estado de la aplicación, como la memoria y los registros
  • Cambios en el sistema de archivos raíz y en tmpfs (incluidos los volúmenes de emptyDir)
  • Estado del kernel, como descriptores de archivos abiertos, hilos y sockets

El tamaño de la instantánea se determina en función de estos factores. Las instantáneas más grandes tardan más en guardarse y restaurarse. Para optimizar el rendimiento, antes de activar una instantánea, debes limpiar cualquier estado o archivo de la aplicación que no sea necesario después de que se restaure el pod a partir de la instantánea.

Optimizar el tamaño de las copias de seguridad es especialmente importante para cargas de trabajo como los modelos de lenguaje extensos (LLMs). Los servidores de LLM suelen descargar los pesos del modelo en el almacenamiento local (rootfs o tmpfs) antes de cargarlos en la GPU. Cuando se hace una captura, se guardan tanto el estado de la GPU como los archivos de pesos del modelo. En este caso, si el modelo es de 100 GB, la instantánea resultante será de unos 200 GB (100 GB de archivos del modelo más 100 GB que representan el estado de la GPU). Una vez que los pesos del modelo se han cargado en la GPU, los archivos del sistema de archivos no suelen ser necesarios para que se ejecute la aplicación. Si eliminas estos archivos de modelo antes de activar la instantánea, puedes reducir su tamaño a la mitad y restaurar la aplicación con una latencia significativamente menor.

Activar una captura

Puedes activar una instantánea desde una carga de trabajo cuando la aplicación esté lista o puedes activar manualmente una instantánea bajo demanda de un Pod específico.

Activar una captura de una carga de trabajo

Para activar una captura desde el código de tu aplicación, configúrala para que envíe una señal cuando esté lista para hacer una captura. Para indicar que está listo, escribe 1 en el archivo /proc/gvisor/checkpoint. Por ejemplo, echo 1 > /proc/gvisor/checkpoint.. La operación de escritura inicia el proceso de creación de la captura de forma asíncrona y devuelve el resultado de inmediato. La lectura del mismo descriptor de archivo bloqueará el proceso de lectura hasta que se completen tanto la instantánea como la restauración y la carga de trabajo esté lista para reanudarse.

El uso exacto variará en función de tu aplicación, pero en el siguiente ejemplo se muestra un activador de instantáneas para una aplicación de Python. Para activar una instantánea de esta carga de trabajo de ejemplo, sigue estos pasos:

  1. Guarda el siguiente archivo de manifiesto como 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. Despliega la aplicación:

    kubectl apply -f my-app.yaml
    

Activar una instantánea manualmente

Para activar manualmente una captura de disco a petición de un pod específico, crea un recurso PodSnapshotManualTrigger. La activación manual de una instantánea está disponible en las versiones 1.34.1-gke.3556000 y posteriores de GKE.

  1. En el siguiente ejemplo se activa una instantánea de un Pod llamado my-pod. Guarda el siguiente manifiesto como example-manual-trigger.yaml:

    apiVersion: podsnapshot.gke.io/v1alpha1
    kind: PodSnapshotManualTrigger
    metadata:
      name: example-manual-trigger
      namespace: NAMESPACE
    spec:
      targetPod: my-pod
    
  2. Aplica el archivo de manifiesto:

    kubectl apply -f example-manual-trigger.yaml --namespace NAMESPACE
    

Para confirmar si la creación de la captura se ha activado correctamente, comprueba el campo status del recurso PodSnapshotManualTrigger:

kubectl get podsnapshotmanualtriggers.podsnapshot.gke.io example-manual-trigger -n NAMESPACE -o yaml

El campo status indica si se ha podido activar la instantánea o no.

Verificar las instantáneas

Para confirmar que se ha hecho una captura, consulta el historial de eventos de eventos GKEPodSnapshotting:

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

Sustituye POD_NAME por el nombre de tu pod (por ejemplo, my-app o my-pod).

La salida es similar a la siguiente:

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

Gestionar capturas

Cuando creas una instantánea de un pod, se crea un recurso CRD PodSnapshot para almacenar el estado del pod en ese momento. El campo status de este recurso indica si la operación de creación de la captura se ha realizado correctamente y si la captura está disponible para restauraciones.

Para ver todos los recursos de PodSnapshot de un espacio de nombres, ejecuta el siguiente comando:

kubectl get podsnapshots.gke.io --namespace NAMESPACE

La salida es similar a la siguiente:

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

Restaurar una carga de trabajo a partir de una captura

Para restaurar tu carga de trabajo a partir de la última captura, puedes eliminar el pod después de hacer una captura y, a continuación, volver a implementarlo. También puedes desplegar un nuevo pod con una especificación idéntica. GKE restaura automáticamente el pod a partir de la captura correspondiente.

En los siguientes pasos se muestra cómo se restaura un pod a partir de una instantánea coincidente eliminando y volviendo a implementar el pod:

  1. Elimina el pod:

    kubectl delete -f POD_NAME.yaml
    

    Sustituye POD_NAME por el nombre de tu pod (por ejemplo, my-app).

  2. Vuelve a aplicar el pod:

    kubectl apply -f POD_NAME.yaml
    
  3. Consulta los registros para confirmar la restauración de la instantánea:

    kubectl logs my-app --namespace NAMESPACE
    

    El resultado depende de cómo hayas configurado tu aplicación. En la aplicación de ejemplo, los registros muestran GKE Pod Snapshot: restore cuando se produce una operación de restauración.

Restaurar desde una captura específica

De forma predeterminada, GKE restaura las cargas de trabajo a partir del PodSnapshot recurso más reciente que coincida con el pod. Cuando se crea una instantánea, GKE genera automáticamente un nombre único (UUID) para el recurso PodSnapshot, que puedes ver ejecutando kubectl get podsnapshots.gke.io --namespace NAMESPACE.

Para restaurar una carga de trabajo a partir de un recurso PodSnapshot anterior o específico, añade la anotación podsnapshot.gke.io/ps-name a la especificación del pod de la carga de trabajo. Especifica el nombre del recurso PodSnapshot que se va a usar para restaurar la carga de trabajo:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  namespace: NAMESPACE
  labels:
    app: my-app
  annotations:
    podsnapshot.gke.io/ps-name: "POD_SNAPSHOT_NAME"
spec:
  serviceAccountName: KSA_NAME
  runtimeClassName: gvisor
  containers:
  ...

Sustituye POD_SNAPSHOT_NAME por el nombre de la instantánea desde la que quieras restaurar. Para obtener los nombres de las instantáneas, ejecuta el comando kubectl get podsnapshots.gke.io --namespace NAMESPACE.

Para que GKE use la instantánea especificada en la restauración, el estado del recurso PodSnapshot debe ser Ready y debe estar en el mismo espacio de nombres que el pod. Si PodSnapshot no es Ready o no existe en el mismo espacio de nombres que el pod, la carga de trabajo realizará un arranque en frío en lugar de restaurar desde una instantánea.

Inhabilitar las instantáneas

Si se elimina el PodSnapshotPolicy CRD, no se podrán crear instantáneas de los pods ni restaurarlos. Los pods en ejecución no se ven afectados por la eliminación de recursos. Sin embargo, si eliminas la política mientras se guarda o se restaura un Pod, es posible que este pase a un estado de error.

Para inhabilitar la creación de instantáneas y la restauración de los nuevos pods regidos por una política, elimina el PodSnapshotPolicy ejecutando el siguiente comando:

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

Sustituye SNAPSHOT_POLICY por el nombre del PodSnapshotPolicy que quieras eliminar (por ejemplo, example-pod-snapshot-policy).

También puedes eliminar un recurso PodSnapshot específico para que los pods ya no se restauren a partir de esa instantánea. Si eliminas el recurso PodSnapshot, también se eliminarán los archivos almacenados en Cloud Storage.

Para evitar que se use una instantánea específica en restauraciones futuras, elimina el objeto PodSnapshot ejecutando el siguiente comando:

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

Sustituye POD_SNAPSHOT_NAME por el nombre de la instantánea que quieras eliminar, por ejemplo, example-podsnapshot.

Siguientes pasos