Déclencher des instantanés Agent Sandbox depuis un cluster

Ce tutoriel explique comment déployer et tester la fonctionnalité d'instantané Agent Sandbox dans un cluster Google Kubernetes Engine (GKE). Vous apprendrez à exécuter une application cliente dans le cluster pour créer, mettre en pause et reprendre des environnements en bac à sable de manière programmatique.

Pour en savoir plus sur la création d'instantanés de pods, consultez Restaurer à partir d'un instantané de pod.

Coûts

Agent Sandbox est proposé sans frais supplémentaires dans GKE. Les tarifs de GKE s'appliquent aux ressources que vous créez.

Avant de commencer

  1. Dans la Google Cloud console, sur la page de sélection du projet, sélectionnez ou créez un Google Cloud projet.

    Rôles requis pour sélectionner ou créer un projet

    • Sélectionner un projet : la sélection d'un projet ne nécessite pas de rôle IAM spécifique Vous pouvez sélectionner n'importe quel projet pour lequel un rôle vous a été attribué.
    • Créer un projet : pour créer un projet, vous avez besoin du rôle Créateur de projet (roles/resourcemanager.projectCreator), qui contient l'autorisation resourcemanager.projects.create. Découvrez comment attribuer des rôles.

    Accéder au sélecteur de projet

  2. Vérifiez que la facturation est activée pour votre Google Cloud projet.

  3. Activez les API Artifact Registry et Kubernetes Engine.

    Rôles requis pour activer les API

    Pour activer les API, vous avez besoin du rôle IAM Administrateur d'utilisation du service (roles/serviceusage.serviceUsageAdmin), qui contient l'autorisation serviceusage.services.enable. Découvrez comment attribuer des rôles.

    Activer les API

  4. Dans la Google Cloud console, activez Cloud Shell.

    Activer Cloud Shell

  5. Vérifiez que vous disposez des autorisations requises pour suivre ce tutoriel.

Rôles requis

Pour obtenir les autorisations nécessaires pour créer et gérer des bacs à sable, demandez à votre administrateur de vous attribuer le rôle IAM Administrateur Kubernetes Engine (roles/container.admin) sur votre projet. Pour en savoir plus sur l'attribution de rôles, consultez Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises via des rôles personnalisés ou d'autres rôles prédéfinis.

Limites

Dans un cluster régional, les nœuds de différentes zones peuvent avoir des microarchitectures de processeur différentes. Comme les instantanés capturent l'état du processeur, la restauration d'un instantané sur un nœud dont les fonctionnalités de processeur sont manquantes échoue (par exemple, avec l'erreur OCI runtime restore failed: incompatible FeatureSet).

Pour éviter ce problème, utilisez la configuration appropriée pour votre environnement :

  • Production : pour préserver la haute disponibilité dans votre cluster, n'épinglez pas les charges de travail à une zone spécifique. Au lieu de cela, assurez-vous de la cohérence des fonctionnalités du processeur dans toutes les zones en spécifiant une configuration minimale de la plate-forme du processeur. Pour en savoir plus, consultez Choisir une configuration minimum de plate-forme de processeur.
  • Tests : pour simplifier la configuration et éviter les erreurs initiales d'incompatibilité du processeur, vous pouvez utiliser un champ nodeSelector dans votre fichier manifeste SandboxTemplate pour épingler le pod à une zone spécifique, telle que us-central1-a. L'exemple de ce tutoriel utilise cette configuration de test.

Définir des variables d'environnement

Pour simplifier les commandes que vous exécutez dans ce tutoriel, vous pouvez définir des variables d'environnement dans Cloud Shell. Dans Cloud Shell, définissez les variables d'environnement utiles suivantes en exécutant les commandes suivantes :

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export CLUSTER_NAME="test-snapshot"
export LOCATION="us-central1"
export BUCKET_LOCATION="us"
export MACHINE_TYPE="n2-standard-2"
export REPOSITORY_NAME="agent-sandbox"
export BUCKET_NAME="${PROJECT_ID}_snapshots"
export CLOUDBUILD_BUCKET_NAME="${PROJECT_ID}_cloudbuild"

Voici une explication de ces variables d'environnement :

  • PROJECT_ID : ID de votre projet actuel. Google Cloud La définition de cette variable permet de s'assurer que toutes les ressources sont créées dans le bon projet.
  • PROJECT_NUMBER: numéro de votre projet actuel Google Cloud .
  • CLUSTER_NAME: nom de votre cluster GKE, par exemple test-snapshot.
  • LOCATION : région dans laquelle se trouvent votre cluster GKE et votre dépôt Artifact Registry, par exemple us-central1. Google Cloud
  • BUCKET_LOCATION: emplacement de vos buckets Cloud Storage, par exemple us.
  • BUCKET_NAME: nom du bucket Cloud Storage utilisé pour les instantanés.
  • CLOUDBUILD_BUCKET_NAME: nom du bucket Cloud Storage utilisé pour les journaux Cloud Build.
  • MACHINE_TYPE: type de machine à utiliser pour les nœuds de cluster, par exemple e2-standard-8.
  • REPOSITORY_NAME: nom du dépôt Artifact Registry, par exemple agent-sandbox.

Présentation des étapes de configuration

Pour activer et tester les instantanés de pods des environnements Agent Sandbox depuis votre cluster, vous devez effectuer plusieurs étapes de configuration. Pour comprendre ces étapes, il est utile de commencer par comprendre les composants impliqués dans le workflow global.

Composants clés

Ce tutoriel utilise les deux applications Python suivantes pour tester le processus d'instantané :

  • Application cliente : script Python exécuté dans un pod standard de votre cluster. Cette application gère le cycle de vie du bac à sable : elle crée le bac à sable de manière programmatique, le met en pause pour déclencher un instantané, le reprend et vérifie que l'état a été conservé. Dans ce tutoriel, vous allez créer un compte de service Kubernetes nommé agent-sandbox-client-sa et lui accorder des autorisations RBAC afin que le pod de l'application cliente puisse gérer les ressources personnalisées du bac à sable et les objets de déclenchement d'instantanés à l'aide de l'API Kubernetes.
  • Application en bac à sable : script Python qui incrémente et affiche un compteur toutes les secondes. Cette application s'exécute de manière sécurisée dans l'environnement de bac à sable isolé pour générer un état changeant que l'application cliente peut vérifier. Dans ce tutoriel, vous allez créer un compte de service Kubernetes dédié nommé snapshot-sa et configurer Workload Identity pour autoriser le pod en bac à sable à lire et à écrire de manière sécurisée des objets d'instantané dans Cloud Storage.

Processus de configuration et de test

La liste suivante récapitule les étapes à suivre pour configurer votre environnement et exécuter le test :

  1. Créer un cluster : créez un cluster Autopilot ou Standard avec les instantanés de pods et la fonctionnalité Agent Sandbox activés.
  2. Créer un dépôt Artifact Registry: créez un dépôt Docker pour stocker l'image de conteneur de votre application cliente.
  3. Installer Agent Sandbox : installez les composants et extensions principaux d'Agent Sandbox sur votre cluster.
  4. Configurer le stockage et les autorisations: créez un bucket Cloud Storage et configurez les autorisations Workload Identity pour permettre l'enregistrement sécurisé des instantanés.
  5. Configurer les instantanés de pods : créez et appliquez la configuration de stockage des instantanés, la règle d'instantané et le modèle de bac à sable.
  6. Créer l'application cliente : créez l' image de conteneur pour l'application cliente et transmettez-la à votre dépôt Artifact Registry.
  7. Exécuter le test : déployez le pod de l'application cliente, qui crée le bac à sable, le met en pause pour capturer un instantané, le reprend et vérifie que l'état du compteur a bien été restauré.

Créer un cluster

Créez un cluster GKE avec les instantanés de pods activés. Pour une compatibilité totale des fonctionnalités, spécifiez le version disponible rapide.

Autopilot

Créez un cluster Autopilot avec les fonctionnalités requises :

gcloud beta container clusters create-auto ${CLUSTER_NAME} \
   --enable-pod-snapshots \
   --release-channel=rapid \
   --location=${LOCATION}

Standard

Créez un cluster standard avec les fonctionnalités requises :

gcloud beta container clusters create ${CLUSTER_NAME} \
   --enable-pod-snapshots \
   --release-channel=rapid \
   --machine-type=${MACHINE_TYPE} \
   --workload-pool=${PROJECT_ID}.svc.id.goog \
   --workload-metadata=GKE_METADATA \
   --num-nodes=1 \
   --location=${LOCATION}

Créez un pool de nœuds avec gVisor activé :

gcloud container node-pools create gvisor-pool \
   --cluster ${CLUSTER_NAME} \
   --num-nodes=1 \
   --location=${LOCATION} \
   --project=${PROJECT_ID} \
   --sandbox type=gvisor

Créer un dépôt Artifact Registry

Créez un dépôt Docker dans Artifact Registry pour stocker l'image de conteneur de votre application cliente (l'application qui crée et gère le bac à sable) :

gcloud artifacts repositories create ${REPOSITORY_NAME} \
   --repository-format=docker \
   --location=${LOCATION} \
   --description="Docker repository for Agent Sandbox"

Installer Agent Sandbox

Installez les composants et extensions principaux d'Agent Sandbox sur votre cluster (en utilisant la version v0.4.6 comme exemple) :

# Install the core agent-sandbox components
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v0.4.6/manifest.yaml

# Install the extensions (e.g., Warm Pools, Claims)
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v0.4.6/extensions.yaml

Configurer le stockage et les autorisations

Configurez un bucket Cloud Storage pour stocker les instantanés de pods et accordez les autorisations Workload Identity requises au compte de service snapshot-sa et à l'agent de service GKE. Cela permet à vos charges de travail en bac à sable d'enregistrer et de récupérer des objets d'instantané de manière sécurisée :

  1. Créez un bucket Cloud Storage :

    gcloud storage buckets create "gs://${BUCKET_NAME}" \
        --uniform-bucket-level-access \
        --enable-hierarchical-namespace \
        --soft-delete-duration=0d \
        --location="${BUCKET_LOCATION}"
    
  2. Créez un compte de service Kubernetes dans l'espace de noms default. Votre application en bac à sable (le script de compteur Python) utilise cette identité pour s'authentifier auprès des API externes et accéder de manière sécurisée aux objets d'instantané stockés dans Cloud Storage :

    kubectl create serviceaccount "snapshot-sa" \
        --namespace "default"
    
  3. Associez le rôle storage.bucketViewer à votre compte de service à l'aide de Workload Identity. Ce rôle permet à la charge de travail en bac à sable de lister le contenu du bucket et de localiser des instantanés spécifiques :

    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/default/sa/snapshot-sa" \
        --role="roles/storage.bucketViewer"
    
  4. Associez le rôle storage.objectUser à votre compte de service à l'aide de Workload Identity. Ce rôle fournit l'autorisation de lire, d'enregistrer et de supprimer des objets binaires d'instantané dans le bucket :

    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/default/sa/snapshot-sa" \
        --role="roles/storage.objectUser"
    
  5. Accordez à l'agent de service GKE les autorisations nécessaires pour gérer (créer, lister, lire et supprimer) les objets d'instantané dans le bucket :

    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
        --member="serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
        --role="roles/storage.objectUser" \
        --condition="expression=resource.name.startsWith(\"projects/_/buckets/${BUCKET_NAME}\"),title=restrict_to_bucket,description=Restricts access to one bucket only"
    

Configurer les instantanés de pods

Créez et appliquez les fichiers de configuration pour installer les ressources personnalisées Kubernetes requises. Ces ressources définissent comment le cluster stocke et gère les instantanés de pods :

  • PodSnapshotStorageConfig : spécifie le bucket Cloud Storage désigné pour stocker les objets binaires d'instantané.
  • PodSnapshotPolicy : définit comment les instantanés sont déclenchés manuellement, leur fréquence de regroupement et leurs règles de conservation.
  • SandboxTemplate : définit le conteneur sous-jacent, les sélecteurs de nœuds et les comptes de service pour exécuter la charge de travail en bac à sable isolée.
  1. Créez un fichier nommé test_client/snapshot_storage_config.yaml. Cette configuration spécifie le bucket Cloud Storage cible dans lequel le cluster enregistre l'état binaire de l'instantané de pod :

    apiVersion: podsnapshot.gke.io/v1
    kind: PodSnapshotStorageConfig
    metadata:
      name: example-pod-snapshot-storage-config
    spec:
      snapshotStorageConfig:
        gcs:
          bucket: "$BUCKET_NAME"
    
  2. Remplacez l'espace réservé de la variable d'environnement dans le fichier de configuration :

    sed -i "s/\$BUCKET_NAME/$BUCKET_NAME/g" test_client/snapshot_storage_config.yaml
    
  3. Appliquez le fichier manifeste de configuration du stockage :

    kubectl apply -f test_client/snapshot_storage_config.yaml
    
  4. Attendez que la configuration du stockage soit prête :

    kubectl wait --for=condition=Ready podsnapshotstorageconfig/example-pod-snapshot-storage-config --timeout=60s
    
  5. Créez un fichier nommé test_client/snapshot_policy.yaml. Cette configuration établit une règle de conservation qui conserve un maximum de deux instantanés pour votre charge de travail en bac à sable. Le type de déclencheur est défini sur manual : cela permet à l'application cliente de contrôler les instantanés à la demande :

    apiVersion: podsnapshot.gke.io/v1
    kind: PodSnapshotPolicy
    metadata:
      name: example-pod-snapshot-policy
      namespace: default
    spec:
      storageConfigName: example-pod-snapshot-storage-config
      selector:
        matchLabels:
          app: agent-sandbox-workload
      triggerConfig:
        type: manual
        postCheckpoint: resume
      snapshotGroupingRules:
        groupByLabelValue:
          labels: ["agents.x-k8s.io/sandbox-name-hash", "tenant-id", "user-id"]
          groupRetentionPolicy:
            maxSnapshotCountPerGroup: 2
    
  6. Appliquez le fichier manifeste de la règle d'instantané :

    kubectl apply -f test_client/snapshot_policy.yaml
    
  7. Créez un fichier nommé test_client/python-counter-template.yaml. Cette configuration définit le pod en bac à sable et lui attribue l'identité du compte de service snapshot-sa. Cette attribution permet de garantir que le bac à sable s'exécute de manière sécurisée. Dans ce pod, l'application en bac à sable (un script Python) affiche en continu un compteur incrémentiel dans les journaux du conteneur :

    apiVersion: extensions.agents.x-k8s.io/v1alpha1
    kind: SandboxTemplate
    metadata:
      name: python-counter-template
      namespace: default
    spec:
      podTemplate:
        metadata:
          labels:
            app: agent-sandbox-workload
        spec:
          serviceAccountName: snapshot-sa
          runtimeClassName: gvisor
          nodeSelector:
            topology.kubernetes.io/zone: us-central1-a # Pin to a zone to avoid CPU mismatch during restore
          containers:
          - name: python-counter
            image: python:3.13-slim
            command: ["python3", "-c"]
            args:
              - |
                import time
                i = 0
                while True:
                  print(f"Count: {i}", flush=True)
                  i += 1
                  time.sleep(1)
    
  8. Appliquez le fichier manifeste du modèle de bac à sable :

    kubectl apply -f test_client/python-counter-template.yaml
    

Créer l'application cliente

Créez l'image de conteneur pour l'application cliente et transmettez-la à Artifact Registry.

  1. Créez un fichier nommé test_client/Dockerfile.client. Ce fichier définit l'environnement d'exécution Python et les dépendances de l'application cliente :

    FROM python:3.13-slim
    
    WORKDIR /app
    
    RUN pip install "k8s-agent-sandbox[tracing]==0.4.6"
    
    # Copy test script
    COPY client_test.py /app/client_test.py
    
    CMD ["python", "/app/client_test.py"]
    
  2. Créez un fichier nommé test_client/client_test.py. Ce script gère le cycle de vie du bac à sable et vérifie que l'état reprend correctement après la création d'un instantané :

    import time
    import logging
    import re
    from kubernetes import config, client
    from k8s_agent_sandbox.gke_extensions.snapshots import PodSnapshotSandboxClient
    
    logging.basicConfig(level=logging.INFO)
    
    def get_last_count(pod_name, namespace):
        v1 = client.CoreV1Api()
        try:
            logs = v1.read_namespaced_pod_log(name=pod_name, namespace=namespace)
            counts = re.findall(r"Count: (\d+)", logs)
            if counts:
                return int(counts[-1])
            return None
        except Exception as e:
            logging.error(f"Failed to read logs for pod {pod_name}: {e}")
            return None
    
    def get_current_pod_name(sandbox_id, namespace):
        custom_api = client.CustomObjectsApi()
        try:
            sandbox_cr = custom_api.get_namespaced_custom_object(
                group="agents.x-k8s.io",
                version="v1alpha1",
                namespace=namespace,
                plural="sandboxes",
                name=sandbox_id
            )
            metadata = sandbox_cr.get("metadata", {})
            annotations = metadata.get("annotations", {})
            return annotations.get("agents.x-k8s.io/pod-name")
        except Exception as e:
            logging.error(f"Failed to get sandbox CR: {e}")
            return None
    
    def get_current_count(sandbox_id, namespace="default"):
        pod_name = get_current_pod_name(sandbox_id, namespace)
        if not pod_name:
            logging.error(f"Could not determine pod name for sandbox {sandbox_id}")
            return None
        return get_last_count(pod_name, namespace)
    
    def suspend_sandbox(sandbox):
        logging.info("Pausing sandbox (using snapshots)...")
        try:
            suspend_resp = sandbox.suspend(snapshot_before_suspend=True)
            if suspend_resp.success:
                logging.info("Sandbox paused successfully.")
                if suspend_resp.snapshot_response:
                    logging.info(f"Snapshot created: {suspend_resp.snapshot_response.snapshot_uid}")
                return suspend_resp
            else:
                logging.error(f"Failed to pause: {suspend_resp.error_reason}")
                exit(1)
        except Exception as e:
            logging.error(f"Failed to pause sandbox: {e}")
            exit(1)
    
    def resume_sandbox(sandbox):
        logging.info("Resuming sandbox (using snapshots)...")
        try:
            resume_resp = sandbox.resume()
            if resume_resp.success:
                logging.info("Sandbox resumed successfully.")
                if resume_resp.restored_from_snapshot:
                    logging.info(f"Restored from snapshot: {resume_resp.snapshot_uid}")
                return resume_resp
            else:
                logging.error(f"Failed to resume: {resume_resp.error_reason}")
                exit(1)
        except Exception as e:
            logging.error(f"Failed to resume sandbox: {e}")
            exit(1)
    
    def verify_continuity(count_before, count_after):
        if count_before is not None and count_after is not None:
            logging.info(f"Verification: Count before={count_before}, Count after={count_after}")
            if count_after >= count_before:
                logging.info("SUCCESS: Sandbox resumed from where it left off (or later).")
            else:
                logging.error("FAIL: Sandbox counter reset or went backwards!")
        else:
            logging.warning("Could not verify counter continuity.")
    
    def main():
        try:
            config.load_incluster_config()
        except config.ConfigException:
            config.load_kube_config()
    
        client_reg = PodSnapshotSandboxClient()
    
        logging.info("Creating sandbox...")
        sandbox = client_reg.create_sandbox(template="python-counter-template", namespace="default")
        logging.info(f"Sandbox created with ID: {sandbox.sandbox_id}")
    
        logging.info("Waiting for sandbox to run...")
        time.sleep(10)
    
        count_before = get_current_count(sandbox.sandbox_id)
        logging.info(f"Count before suspend: {count_before}")
    
        suspend_sandbox(sandbox)
    
        logging.info("Waiting 10 seconds...")
        time.sleep(10)
    
        resume_sandbox(sandbox)
    
        logging.info("Waiting for sandbox to be ready again...")
        time.sleep(10)
    
        count_after = get_current_count(sandbox.sandbox_id)
        logging.info(f"Count after resume: {count_after}")
    
        verify_continuity(count_before, count_after)
    
        logging.info("Snapshot test completed successfully.")
    
    if __name__ == "__main__":
        main()
    
  3. Créez l'image de conteneur client et transmettez-la à Artifact Registry. Si Docker est installé dans votre environnement (tel que Cloud Shell), vous pouvez l'utiliser pour créer l'image en local. Si vous travaillez dans un environnement sans Docker, vous pouvez utiliser Cloud Build pour créer et transmettre l'image à distance :

    Docker

    1. Configurez l'authentification Docker pour Artifact Registry :

      gcloud auth configure-docker "${LOCATION}-docker.pkg.dev"
      
    2. Créez et transmettez l'image de conteneur client en local :

      docker build -t "${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/sandbox-client:latest" -f test_client/Dockerfile.client test_client
      docker push "${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/sandbox-client:latest"
      

    Cloud Build

    1. Créez un fichier nommé test_client/cloudbuild.yaml :

      steps:
      - name: 'gcr.io/cloud-builders/docker'
        args: ['build', '-t', '$LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/sandbox-client:latest', '-f', 'test_client/Dockerfile.client', 'test_client']
      images:
      - '$LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/sandbox-client:latest'
      
    2. Remplacez les espaces réservés des variable d'environnement dans le fichier de configuration :

      sed -i "s/\$REPOSITORY_NAME/$REPOSITORY_NAME/g" test_client/cloudbuild.yaml
      sed -i "s/\$LOCATION/$LOCATION/g" test_client/cloudbuild.yaml
      sed -i "s/\$PROJECT_ID/$PROJECT_ID/g" test_client/cloudbuild.yaml
      
    3. Accordez les autorisations nécessaires au compte de service Cloud Build :

      gcloud projects add-iam-policy-binding $PROJECT_ID \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
         --role="roles/artifactregistry.writer"
      
      gcloud projects add-iam-policy-binding $PROJECT_ID \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
         --role="roles/logging.logWriter"
      
      gcloud storage buckets add-iam-policy-binding "gs://$CLOUDBUILD_BUCKET_NAME" \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
         --role="roles/storage.objectAdmin"
      
    4. Exécutez la compilation à l'aide de Cloud Build :

      gcloud builds submit --config test_client/cloudbuild.yaml
      

Exécuter le test

Déployez l'application cliente pour créer le bac à sable, déclencher un instantané et vérifier que le compteur interne reprend correctement à partir de son état enregistré.

  1. Créez un fichier nommé test_client/client_sa.yaml. Ce fichier manifeste définit le compte de service agent-sandbox-client-sa et les autorisations RBAC requises pour gérer les ressources personnalisées du bac à sable :

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: agent-sandbox-client-sa
      namespace: default
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: agent-sandbox-client-role
      namespace: default
    rules:
    - apiGroups: ["agents.x-k8s.io"]
      resources: ["sandboxes"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    - apiGroups: ["extensions.agents.x-k8s.io"]
      resources: ["sandboxclaims"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    - apiGroups: ["podsnapshot.gke.io"]
      resources: ["podsnapshotmanualtriggers", "podsnapshots"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    - apiGroups: [""]
      resources: ["pods", "pods/log"]
      verbs: ["get", "list", "watch"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: agent-sandbox-client-rolebinding
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: agent-sandbox-client-sa
      namespace: default
    roleRef:
      kind: Role
      name: agent-sandbox-client-role
      apiGroup: rbac.authorization.k8s.io
    
  2. Appliquez le fichier manifeste du compte de service client et de RBAC :

    kubectl apply -f test_client/client_sa.yaml
    
  3. Créez un fichier nommé test_client/client_pod.yaml. Ce fichier manifeste crée le pod de l'application cliente à l'aide de l'image de conteneur prédéfinie :

    apiVersion: v1
    kind: Pod
    metadata:
      name: agent-sandbox-client-pod
      namespace: default
    spec:
      serviceAccountName: agent-sandbox-client-sa
      containers:
      - name: client
        image: $LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/sandbox-client:latest
        imagePullPolicy: Always
      restartPolicy: Never
    
  4. Remplacez les espaces réservés des variable d'environnement dans le fichier manifeste :

    sed -i "s/\$REPOSITORY_NAME/$REPOSITORY_NAME/g" test_client/client_pod.yaml
    sed -i "s/\$LOCATION/$LOCATION/g" test_client/client_pod.yaml
    sed -i "s/\$PROJECT_ID/$PROJECT_ID/g" test_client/client_pod.yaml
    
  5. Appliquez le fichier manifeste du pod de l'application cliente :

    kubectl apply -f test_client/client_pod.yaml
    
  6. Diffusez les journaux du pod pour vérifier le flux d'exécution :

    kubectl logs -f agent-sandbox-client-pod
    

Lorsque le test s'exécute correctement, le résultat ressemble à ceci (raccourci ici pour plus de lisibilité) :

2026-04-21 23:02:39,030 - INFO - Creating sandbox...
...
2026-04-21 23:02:51,755 - INFO - Count before suspend: 23
2026-04-21 23:02:51,755 - INFO - Pausing sandbox (using snapshots)...
...
2026-04-21 23:03:07,115 - INFO - Resuming sandbox (using snapshots)...
...
2026-04-21 23:03:21,329 - INFO - Count after resume: 38
2026-04-21 23:03:21,329 - INFO - Verification: Count before=23, Count after=38
2026-04-21 23:03:21,329 - INFO - SUCCESS: Sandbox resumed from where it left off (or later).

Le résultat indique que le bac à sable conserve correctement son état lorsqu'il est suspendu et repris. Le compteur cesse d'avancer lorsque le bac à sable est suspendu (mis en pause et mis à l'échelle à zéro), et reprend le compteur lorsque le bac à sable est restauré. Sans suspension, le compteur aurait continué à avancer pendant la période de suspension et le nombre serait beaucoup plus élevé.

Effectuer un nettoyage des ressources

Pour éviter que des frais ne soient facturés sur votre Google Cloud compte, supprimez les ressources que vous avez créées :

  1. Supprimez le cluster GKE. Cette opération supprime également le pool de nœuds et tous les comptes de service Kubernetes qu'il contient :

    gcloud beta container clusters delete test-snapshot --location="${LOCATION}" --quiet
    
  2. Supprimez le dépôt Artifact Registry pour supprimer le dépôt Docker que vous avez créé pour l'image de test :

    gcloud artifacts repositories delete ${REPOSITORY_NAME} --location="${LOCATION}" --quiet
    
  3. Supprimez le bucket Cloud Storage et tous les instantanés qu'il contient. Cette opération supprime automatiquement les liaisons IAM Workload Identity au niveau du bucket qui lui sont appliquées :

    gcloud storage rm --recursive "gs://${BUCKET_NAME}"
    
  4. Supprimez la liaison IAM au niveau du projet pour l'agent de service GKE :

    gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
        --member="serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
        --role="roles/storage.objectUser" \
        --condition="expression=resource.name.startsWith(\"projects/_/buckets/${BUCKET_NAME}\"),title=restrict_to_bucket,description=Restricts access to one bucket only"
    
  5. Si vous avez utilisé Cloud Build au lieu de Docker pour créer et transmettre l'image de conteneur, supprimez le bucket de journaux et supprimez les autorisations du compte de service :

    gcloud storage rm --recursive "gs://${CLOUDBUILD_BUCKET_NAME}"
    
    gcloud projects remove-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
        --role="roles/artifactregistry.writer"
    
    gcloud projects remove-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
        --role="roles/logging.logWriter"
    

Étape suivante