Attiva gli snapshot di Agent Sandbox su GKE dall'interno di un cluster

Utilizza Agent Sandbox sugli snapshot di Google Kubernetes Engine (GKE) all'interno di un cluster per preservare lo stato degli ambienti in sandbox. Questo approccio fornisce un debug coerente, accelera l'iterazione dello sviluppo e facilita il recupero rapido di configurazioni complesse. In questo tutorial, eseguirai il deployment e testerai questa funzionalità eseguendo un'applicazione client che crea, mette in pausa e riprende in modo programmatico gli ambienti in modalità sandbox all'interno del cluster.

Per saperne di più su come acquisire snapshot dei pod, consulta Ripristina da uno snapshot del pod.

Costi

Agent Sandbox su GKE è un software open source che puoi utilizzare senza costi aggiuntivi. I prezzi di GKE si applicano alle risorse che crei, incluse le risorse di computing utilizzate dal controller di Agent Sandbox.

Prima di iniziare

  1. Nella console Google Cloud , nella pagina di selezione del progetto, seleziona o crea un progetto Google Cloud .

    Ruoli richiesti per selezionare o creare un progetto

    • Seleziona un progetto: la selezione di un progetto non richiede un ruolo IAM specifico. Puoi selezionare qualsiasi progetto per cui ti è stato concesso un ruolo.
    • Crea un progetto: per creare un progetto, devi disporre del ruolo Autore progetto (roles/resourcemanager.projectCreator), che contiene l'autorizzazione resourcemanager.projects.create. Scopri come concedere i ruoli.

    Vai al selettore di progetti

  2. Verifica che la fatturazione sia attivata per il tuo progetto Google Cloud .

  3. Abilita le API Artifact Registry, Kubernetes Engine e API Cloud Build, se non sono già abilitate.

    Ruoli richiesti per abilitare le API

    Per abilitare le API, devi disporre dell'autorizzazione serviceusage.services.enable. Se hai creato il progetto, probabilmente hai già questa autorizzazione tramite il ruolo Proprietario (roles/owner). In caso contrario, puoi ottenere questa autorizzazione tramite il ruolo Amministratore Service Usage (roles/serviceusage.serviceUsageAdmin). Scopri come concedere i ruoli.

    Abilita le API

  4. Nella console Google Cloud , attiva Cloud Shell.

    Attiva Cloud Shell

  5. Verifica di disporre delle autorizzazioni necessarie per completare questo tutorial.

Ruoli obbligatori

Per ottenere le autorizzazioni necessarie per creare e gestire le sandbox, chiedi all'amministratore di concederti il ruolo IAM Kubernetes Engine Admin (roles/container.admin) nel progetto. Per saperne di più sulla concessione dei ruoli, consulta Gestisci l'accesso a progetti, cartelle e organizzazioni.

Potresti anche riuscire a ottenere le autorizzazioni richieste tramite i ruoli personalizzati o altri ruoli predefiniti.

Limitazioni

Gli snapshot dei pod richiedono gVisor e non funzionano con le sandbox microVM.

In un cluster a livello di regione, i nodi in zone diverse potrebbero avere microarchitetture della CPU diverse. Poiché gli snapshot acquisiscono lo stato della CPU, il ripristino di uno snapshot su un nodo con funzionalità della CPU mancanti non va a buon fine (ad esempio, con l'errore OCI runtime restore failed: incompatible FeatureSet).

Per evitare questo problema, utilizza la configurazione appropriata per il tuo ambiente:

  • Produzione: per preservare l'alta affidabilità nel cluster, non assegnare i workload a una zona specifica. Per garantire la coerenza delle funzionalità della CPU in tutte le zone, specifica una piattaforma CPU minima. Per saperne di più, consulta Scegli una piattaforma CPU minima.
  • Test: per semplificare la configurazione ed evitare errori iniziali di mancata corrispondenza della CPU, puoi utilizzare un campo nodeSelector nel manifest SandboxTemplate per assegnare il pod a una zona specifica, ad esempio us-central1-a. L'esempio in questo tutorial utilizza questa configurazione di test.

Definisci le variabili di ambiente

Per semplificare i comandi che esegui in questo tutorial, puoi impostare le variabili di ambiente in Cloud Shell. In Cloud Shell, definisci le seguenti variabili di ambiente utili eseguendo questi comandi:

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 ZONE="us-central1-a"
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"

Ecco una spiegazione di queste variabili di ambiente:

  • PROJECT_ID: l'ID del progetto Google Cloud corrente. I comandi in questo documento utilizzano questo ID per assegnare un nome alle risorse e concedere le autorizzazioni IAM.
  • PROJECT_NUMBER: il numero del progetto Google Cloud corrente.
  • CLUSTER_NAME: il nome del tuo cluster GKE, ad esempio test-snapshot.
  • LOCATION: la regione Google Cloud per il tuo repository Artifact Registry (ad esempio, us-central1). Se crei un cluster Autopilot, anche il cluster utilizza questa regione.
  • ZONE: la zona del tuo cluster. Se crei un cluster Standard, impostalo su una zona nella regione LOCATION (ad esempio us-central1-a). Il template della sandbox che crei in un secondo momento in questo tutorial esegue le sandbox in us-central1-a. Se utilizzi una zona diversa, modifica anche la zona nel template.
  • BUCKET_LOCATION: la località dei bucket Cloud Storage, ad esempio us.
  • BUCKET_NAME: il nome del bucket Cloud Storage utilizzato per gli snapshot, ad esempio PROJECT_ID_snapshots. I nomi dei bucket devono essere univoci a livello globale.
  • CLOUDBUILD_BUCKET_NAME: il nome del bucket Cloud Storage utilizzato per i log di Cloud Build. Questo valore è il nome predefinito del bucket Cloud Build, quindi non modificarlo.
  • MACHINE_TYPE: il tipo di macchina da utilizzare per i nodi del cluster e il pool di nodi gVisor, ad esempio n2-standard-2. Gli snapshot dei pod non supportano i tipi di macchine E2.
  • REPOSITORY_NAME: il nome del repository Artifact Registry, ad esempio agent-sandbox.

Panoramica dei passaggi di configurazione

Per abilitare e testare gli snapshot dei pod di Agent Sandbox negli ambienti GKE dall'interno del cluster, devi eseguire diversi passaggi di configurazione. Per comprendere questi passaggi, è utile innanzitutto comprendere i componenti coinvolti nel workflow complessivo.

Componenti chiave

Questo tutorial utilizza le seguenti due applicazioni Python per testare il processo di snapshot:

  • Applicazione client: uno script Python in esecuzione in un pod standard nel tuo cluster. Questa applicazione gestisce il ciclo di vita della sandbox: crea la sandbox in modo programmatico, la mette in pausa per attivare uno snapshot, la riprende e verifica che lo stato sia stato conservato. In questo tutorial, creerai un account di servizio Kubernetes denominato agent-sandbox-client-sa e gli concederai le autorizzazioni RBAC in modo che il pod dell'applicazione client possa gestire le risorse personalizzate della sandbox e gli oggetti trigger degli snapshot utilizzando l'API Kubernetes.
  • Applicazione in sandbox: uno script Python che incrementa e stampa un contatore ogni secondo. Questa applicazione viene eseguita in modo sicuro all'interno dell'ambiente sandbox isolato per generare uno stato mutevole che l'applicazione client può verificare. In questo tutorial, creerai un service account Kubernetes dedicato denominato snapshot-sa e configurerai Workload Identity per autorizzare il pod in sandbox a leggere e scrivere in modo sicuro gli oggetti snapshot in Cloud Storage.

Procedura di configurazione e test

L'elenco seguente riassume i passaggi da eseguire per configurare l'ambiente ed eseguire il test:

  1. Crea un cluster: crea un cluster Autopilot o Standard con gli snapshot dei pod abilitati.
  2. Crea un repository Artifact Registry: crea un repository Docker per archiviare l'immagine container per la tua applicazione client.
  3. Installa Agent Sandbox: installa i componenti e le estensioni principali di Agent Sandbox nel tuo cluster.
  4. Configura lo spazio di archiviazione e le autorizzazioni: crea un bucket Cloud Storage e configura le autorizzazioni di Workload Identity per consentire il salvataggio sicuro degli snapshot.
  5. Configura gli snapshot dei pod: crea e applica la configurazione dello spazio di archiviazione degli snapshot, la policy degli snapshot, il template della sandbox e il pool caldo.
  6. Crea l'applicazione client: crea l'immagine container per l'applicazione client ed eseguine il push nel repository Artifact Registry.
  7. Esegui il test: esegui il deployment del pod dell'applicazione client, che crea la sandbox, la mette in pausa per acquisire uno snapshot, la riprende e verifica che lo stato del contatore sia stato ripristinato correttamente.

Crea un cluster

Crea un nuovo cluster GKE con gli snapshot dei pod abilitati. Per la compatibilità completa delle funzionalità, specifica il canale di rilascio rapido.

Autopilot

Crea un cluster Autopilot con le funzionalità richieste:

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

Standard

Crea un cluster Standard con le funzionalità richieste. Per ridurre i costi, il seguente comando crea un cluster di zona, che esegue il control plane e i nodi in una zona:

gcloud 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=${ZONE}

Crea un pool di nodi con gVisor abilitato:

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

Crea un repository Artifact Registry

Crea un repository Docker in Artifact Registry per archiviare l'immagine container per la tua applicazione client (l'applicazione che crea e gestisce la sandbox):

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

Installa Agent Sandbox

Installa i componenti principali e le estensioni di Agent Sandbox sul tuo cluster. Questo tutorial utilizza la versione 1.0.6:

# Install the core agent-sandbox components and the extensions (e.g., Warm Pools, Claims)
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.6/sandbox-with-extensions.yaml

Per saperne di più sull'installazione di Agent Sandbox, consulta Installa Agent Sandbox su GKE.

Configura lo spazio di archiviazione e le autorizzazioni

Configura un bucket Cloud Storage per l'archiviazione degli snapshot dei pod e concedi le autorizzazioni di Workload Identity richieste al account di servizio snapshot-sa e all'agente di servizio GKE. In questo modo, i workload in sandbox possono salvare e recuperare in modo sicuro gli oggetti snapshot:

  1. Crea un nuovo 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. Crea un account di servizio Kubernetes nello spazio dei nomi default. La tua applicazione in sandbox (lo script del contatore Python) utilizza questa identità per autenticarsi con le API esterne e accedere in modo sicuro agli oggetti snapshot archiviati in Cloud Storage:

    kubectl create serviceaccount "snapshot-sa" \
        --namespace "default"
    
  3. Associa il ruolo storage.bucketViewer al tuo account di servizio utilizzando Workload Identity. Questo ruolo consente al workload in sandbox di elencare i contenuti del bucket e individuare snapshot specifici:

    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. Associa il ruolo storage.objectUser al tuo account di servizio utilizzando Workload Identity. Questo ruolo fornisce l'autorizzazione per leggere, salvare ed eliminare gli oggetti binari degli snapshot all'interno del 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. Concedi all'agente di servizio GKE le autorizzazioni per gestire (creare, elencare, leggere ed eliminare) gli oggetti snapshot all'interno del 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"
    

Configura gli snapshot dei pod

Crea e applica i file di configurazione per installare le risorse personalizzate Kubernetes richieste. Queste risorse definiscono il modo in cui il cluster archivia e gestisce gli snapshot dei pod:

  • PodSnapshotStorageConfig: specifica il bucket Cloud Storage designato per l'archiviazione degli oggetti binari degli snapshot.
  • PodSnapshotPolicy: definisce il modo in cui gli snapshot vengono attivati manualmente, la frequenza con cui vengono raggruppati e le relative policy di conservazione.
  • SandboxTemplate: definisce il container, i selettori dei nodi e i service account sottostanti per l'esecuzione del workload in sandbox isolato.
  • SandboxWarmPool: mantiene una sandbox pre-riscaldata, creata dalla risorsa SandboxTemplate, pronta per essere richiesta dall'applicazione client.
  1. Crea una nuova directory per i file del client di test:

    mkdir test_client
    
  2. Vai alla nuova directory:

    cd test_client
    
  3. Crea un file denominato snapshot_storage_config.yaml. Questa configurazione specifica il bucket Cloud Storage di destinazione in cui il cluster salva lo stato dello snapshot binario dei pod:

    apiVersion: podsnapshot.gke.io/v1
    kind: PodSnapshotStorageConfig
    metadata:
      name: example-pod-snapshot-storage-config
    spec:
      snapshotStorageConfig:
        gcs:
          bucket: "$BUCKET_NAME"
    
  4. Sostituisci il segnaposto della variabile di ambiente nel file di configurazione:

    sed -i "s/\$BUCKET_NAME/${BUCKET_NAME}/g" snapshot_storage_config.yaml
    
  5. Applica il manifest di configurazione dello spazio di archiviazione:

    kubectl apply -f snapshot_storage_config.yaml
    
  6. Attendi che la configurazione dello spazio di archiviazione sia pronta:

    kubectl wait --for=condition=Ready podsnapshotstorageconfig/example-pod-snapshot-storage-config --timeout=60s
    
  7. Crea un file denominato snapshot_policy.yaml. Questa configurazione stabilisce una regola di conservazione che conserva un massimo di due snapshot per il workload in sandbox. Il tipo di trigger è impostato su manual: ciò consente all'applicazione client di controllare gli snapshot on demand:

    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
    
  8. Applica il manifest della policy snapshot:

    kubectl apply -f snapshot_policy.yaml
    
  9. Crea un file denominato python-counter-template.yaml. Questa configurazione definisce il pod della sandbox e gli assegna l'identità del account di servizio snapshot-sa. Questa assegnazione contribuisce a garantire che la sandbox venga eseguita in modo sicuro. All'interno del pod, l'applicazione in sandbox (uno script Python) stampa continuamente un contatore incrementale nei log del container:

    apiVersion: extensions.agents.x-k8s.io/v1beta1
    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)
    
  10. Applica il manifest del template della sandbox:

    kubectl apply -f python-counter-template.yaml
    
  11. Crea un file denominato python-counter-pool.yaml. Questa configurazione crea un pool caldo che mantiene una sandbox pre-riscaldata, creata dal template python-counter-template, pronta per essere richiesta dall'applicazione client:

    apiVersion: extensions.agents.x-k8s.io/v1beta1
    kind: SandboxWarmPool
    metadata:
      name: python-counter-pool
      namespace: default
    spec:
      replicas: 1
      sandboxTemplateRef:
        name: python-counter-template
    
  12. Applica il manifest del pool caldo:

    kubectl apply -f python-counter-pool.yaml
    

Crea l'applicazione client

Crea l'immagine container per l'applicazione client e caricala in Artifact Registry.

  1. Crea un file denominato Dockerfile.client. Questo file definisce l'ambiente di runtime Python e le dipendenze per l'applicazione client:

    FROM python:3.13-slim
    
    WORKDIR /app
    
    RUN pip install "k8s-agent-sandbox[tracing]==1.0.6"
    
    # Copy test script
    COPY client_test.py /app/client_test.py
    
    CMD ["python", "/app/client_test.py"]
    
  2. Crea un file denominato client_test.py. Questo script gestisce il ciclo di vita della sandbox e verifica che lo stato venga ripristinato correttamente dopo l'acquisizione di uno snapshot:

    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_count(sandbox):
        # The sandbox's Pod has the same name as the Sandbox resource.
        pod_name = sandbox.get_pod_name()
        if not pod_name:
            logging.error(f"Could not determine pod name for sandbox {sandbox.sandbox_id}")
            return None
        return get_last_count(pod_name, sandbox.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(warmpool="python-counter-pool", 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)
        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)
        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. Crea l'immagine container del client e caricala in Artifact Registry. Se nel tuo ambiente (ad esempio Cloud Shell) è installato Docker, puoi utilizzarlo per creare l'immagine localmente. Se lavori in un ambiente senza Docker, puoi utilizzare Cloud Build per creare l'immagine ed eseguirne il push da remoto:

    Docker

    1. Configura l'autenticazione Docker per Artifact Registry:

      gcloud auth configure-docker "${LOCATION}-docker.pkg.dev"
      
    2. Crea ed esegui il push dell'immagine container del client in locale:

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

    Cloud Build

    1. Crea un file denominato cloudbuild.yaml:

      steps:
      - name: 'gcr.io/cloud-builders/docker'
        args: ['build', '-t', '$LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/sandbox-client:latest', '-f', 'Dockerfile.client', '.']
      images:
      - '$LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/sandbox-client:latest'
      
    2. Sostituisci i segnaposto variabile di ambiente nel file di configurazione:

      sed -i "s/\$REPOSITORY_NAME/${REPOSITORY_NAME}/g" cloudbuild.yaml
      sed -i "s/\$LOCATION/${LOCATION}/g" cloudbuild.yaml
      sed -i "s/\$PROJECT_ID/${PROJECT_ID}/g" cloudbuild.yaml
      
    3. Crea il bucket Cloud Storage per i log di Cloud Build. Se il tuo progetto ha già utilizzato Cloud Build, questo bucket potrebbe già esistere e il comando non va a buon fine con un errore che indica che il bucket è già di tua proprietà. Puoi ignorare questo errore:

      gcloud storage buckets create "gs://${CLOUDBUILD_BUCKET_NAME}" \
          --location="${BUCKET_LOCATION}"
      
    4. Concedi le autorizzazioni necessarie al account di servizio Cloud Build.

      I seguenti comandi utilizzano il account di servizio predefinito di Compute Engine ($PROJECT_NUMBER-compute@developer.gserviceaccount.com), che è l'impostazione predefinita per la maggior parte dei progetti. Se il tuo progetto utilizza il account di servizio Cloud Build legacy, sostituisci l'indirizzo email del membro nei comandi seguenti con $PROJECT_NUMBER@cloudbuild.gserviceaccount.com.

      Poiché in precedenza in questo tutorial hai aggiunto un'associazione IAM condizionale al tuo progetto, devi includere il flag --condition=None quando aggiungi queste nuove associazioni. In questo modo, gcloud non si interrompe per chiedere una conferma interattiva:

      gcloud projects add-iam-policy-binding ${PROJECT_ID} \
         --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
         --role="roles/artifactregistry.writer" \
         --condition=None
      
      gcloud projects add-iam-policy-binding ${PROJECT_ID} \
         --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
         --role="roles/logging.logWriter" \
         --condition=None
      
      gcloud storage buckets add-iam-policy-binding "gs://${CLOUDBUILD_BUCKET_NAME}" \
         --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
         --role="roles/storage.objectAdmin"
      
    5. Esegui la build utilizzando Cloud Build:

      gcloud builds submit --config cloudbuild.yaml
      

Esegui il test

Esegui il deployment dell'applicazione client per creare la sandbox, attivare uno snapshot e verificare che il contatore interno riprenda correttamente dallo stato salvato.

  1. Crea un file denominato client_sa.yaml. Questo manifest definisce il account di servizio agent-sandbox-client-sa e le autorizzazioni RBAC richieste per la gestione delle risorse personalizzate della sandbox:

    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. Applica il account di servizio client e il manifest RBAC:

    kubectl apply -f client_sa.yaml
    
  3. Crea un file denominato client_pod.yaml. Questo manifest crea il pod dell'applicazione client utilizzando l'immagine container predefinita:

    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. Sostituisci i segnaposto variabile di ambiente nel manifest:

    sed -i "s/\$REPOSITORY_NAME/${REPOSITORY_NAME}/g" client_pod.yaml
    sed -i "s/\$LOCATION/${LOCATION}/g" client_pod.yaml
    sed -i "s/\$PROJECT_ID/${PROJECT_ID}/g" client_pod.yaml
    
  5. Attendi che la sandbox del pool caldo sia pronta:

    kubectl wait sandboxwarmpool python-counter-pool \
        --for=jsonpath='{.status.readyReplicas}'=1 \
        --timeout=600s
    

    Nel passaggio successivo, l'applicazione client crea una risorsa SandboxClaim. Se l'applicazione crea la richiesta prima che la sandbox del pool sia pronta, il controller potrebbe creare una nuova sandbox per la richiesta anziché assegnare quella del pool. In un cluster Autopilot, la sandbox può richiedere diversi minuti per essere pronta perché GKE potrebbe dover creare un nodo.

  6. Applica il manifest del pod dell'applicazione client:

    kubectl apply -f client_pod.yaml
    
  7. Trasmetti in streaming i log del pod per verificare il flusso di esecuzione:

    kubectl logs -f agent-sandbox-client-pod
    

Quando il test viene eseguito correttamente, l'output è simile al seguente (abbreviato qui per leggibilità):

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).

L'output mostra che la sandbox conserva correttamente il suo stato quando viene sospesa e ripresa. Il contatore smette di avanzare mentre la sandbox è sospesa, perché il controller di Agent Sandbox elimina il pod della sandbox. Quando la sandbox riprende, il controller ricrea il pod, che viene ripristinato dallo snapshot, e il contatore continua. Senza la sospensione, il contatore avrebbe continuato ad avanzare durante il periodo di sospensione e il conteggio sarebbe stato significativamente più alto.

Esegui la pulizia delle risorse

Per evitare che al tuo account Google Cloud vengano addebitati costi, elimina le risorse che hai creato:

  1. Elimina il cluster GKE. In questo modo vengono eliminati anche il pool di nodi e tutti i service account Kubernetes al suo interno:

    Autopilot

    gcloud container clusters delete ${CLUSTER_NAME} \
        --location="${LOCATION}" --quiet
    

    Standard

    gcloud container clusters delete ${CLUSTER_NAME} \
        --location="${ZONE}" --quiet
    
  2. Elimina il repository Artifact Registry per rimuovere il repository Docker che hai creato per l'immagine di test:

    gcloud artifacts repositories delete ${REPOSITORY_NAME} --location="${LOCATION}" --quiet
    
  3. Elimina il bucket Cloud Storage e tutti gli snapshot al suo interno. In questo modo, le associazioni IAM di Workload Identity a livello di bucket applicate vengono rimosse automaticamente:

    gcloud storage rm --recursive "gs://${BUCKET_NAME}"
    
  4. Rimuovi l'associazione IAM a livello di progetto per l'agente di servizio 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. Se hai utilizzato Cloud Build anziché Docker per creare ed eseguire il push dell'immagine container, esegui la pulizia delle risorse di Cloud Build. Elimina solo le risorse che hai creato per questo tutorial, perché altri workload nel tuo progetto potrebbero dipendere da risorse esistenti in precedenza:

    • Se il bucket Cloud Build non esisteva prima di iniziare questo tutorial, eliminalo:

      gcloud storage rm --recursive "gs://${CLOUDBUILD_BUCKET_NAME}"
      
    • Se il account di servizio predefinito di Compute Engine non disponeva dei ruoli roles/artifactregistry.writer e roles/logging.logWriter prima di iniziare questo tutorial, rimuovili:

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

Passaggi successivi