Acionar snapshots do sandbox do agente de dentro de um cluster

Use snapshots do Agent Sandbox em um cluster do Google Kubernetes Engine (GKE) para preservar o estado dos seus ambientes isolados. Essa abordagem oferece depuração consistente, acelera a iteração de desenvolvimento e facilita a recuperação rápida de configurações complexas. Neste tutorial, você vai implantar e testar essa capacidade executando um aplicativo cliente que cria, pausa e retoma ambientes em sandbox de maneira programática dentro do cluster.

Para mais informações sobre como criar snapshots de pods, consulte Restaurar de um snapshot de pod.

Custos

O Agent Sandbox é oferecido sem custo extra no GKE. Os preços do GKE se aplicam aos recursos que você cria.

Antes de começar

  1. No console do Google Cloud , na página do seletor de projetos, selecione ou crie um projeto do Google Cloud .

    Funções necessárias para selecionar ou criar um projeto

    • Selecionar um projeto: não é necessário um papel específico do IAM para selecionar um projeto. Você pode escolher qualquer projeto em que tenha recebido um papel.
    • Criar um projeto: para criar um projeto, é necessário ter o papel de Criador de projetos (roles/resourcemanager.projectCreator), que contém a permissão resourcemanager.projects.create. Saiba como conceder papéis.

    Acessar o seletor de projetos

  2. Verifique se o faturamento está ativado para o projeto do Google Cloud .

  3. Ative as APIs Artifact Registry, Kubernetes Engine e Cloud Build.

    Funções necessárias para ativar APIs

    Para ativar as APIs, é necessário ter o papel do IAM de administrador de uso do serviço (roles/serviceusage.serviceUsageAdmin), que contém a permissão serviceusage.services.enable. Saiba como conceder papéis.

    Ativar as APIs

  4. No console do Google Cloud , ative o Cloud Shell.

    Ativar o Cloud Shell

  5. Verifique se você tem as permissões necessárias para concluir este tutorial.

Funções exigidas

Para receber as permissões necessárias para criar e gerenciar sandboxes, peça ao administrador para conceder a você o papel do IAM de Administrador do Kubernetes Engine (roles/container.admin) no seu projeto. Para mais informações sobre a concessão de papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.

Também é possível conseguir as permissões necessárias usando papéis personalizados ou outros papéis predefinidos.

Limitações

Em um cluster regional, os nós em zonas diferentes podem ter microarquiteturas de CPU diferentes. Como os snapshots capturam o estado da CPU, a restauração de um snapshot em um nó sem recursos de CPU falha (por exemplo, com o erro OCI runtime restore failed: incompatible FeatureSet).

Para evitar esse problema, use a configuração adequada para seu ambiente:

  • Produção: para preservar a alta disponibilidade em todo o cluster, não fixe cargas de trabalho em uma zona específica. Em vez disso, ajude a garantir a consistência dos recursos de CPU em todas as zonas especificando uma plataforma mínima de CPU. Para mais informações, consulte Escolher uma plataforma mínima de CPU.
  • Teste: para simplificar a configuração e evitar erros iniciais de incompatibilidade de CPU, você pode usar um campo nodeSelector no manifesto SandboxTemplate para fixar o pod em uma zona específica, como us-central1-a. O exemplo neste tutorial usa essa configuração de teste.

Definir variáveis de ambiente

Para simplificar os comandos executados neste tutorial, defina variáveis de ambiente no Cloud Shell. No Cloud Shell, defina as seguintes variáveis de ambiente úteis executando os comandos abaixo:

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"

Confira uma explicação dessas variáveis de ambiente:

  • PROJECT_ID: o ID do seu projeto Google Cloud atual. Definir essa variável ajuda a garantir que todos os recursos sejam criados no projeto correto.
  • PROJECT_NUMBER: o número do projeto Google Cloud atual.
  • CLUSTER_NAME: o nome do cluster do GKE. Por exemplo, test-snapshot.
  • LOCATION: a região Google Cloud em que o cluster do GKE e o repositório do Artifact Registry estão localizados, por exemplo, us-central1.
  • BUCKET_LOCATION: o local dos seus buckets do Cloud Storage, por exemplo, us.
  • BUCKET_NAME: o nome do bucket do Cloud Storage usado para snapshots.
  • CLOUDBUILD_BUCKET_NAME: o nome do bucket do Cloud Storage usado para registros do Cloud Build.
  • MACHINE_TYPE: o tipo de máquina a ser usado para os nós do cluster. Por exemplo, e2-standard-8.
  • REPOSITORY_NAME: o nome do repositório do Artifact Registry. Por exemplo, agent-sandbox.

Visão geral das etapas de configuração

Para ativar e testar snapshots de pods de ambientes do Sandbox do agente no seu cluster, é preciso realizar várias etapas de configuração. Para entender essas etapas, é útil primeiro conhecer os componentes envolvidos no fluxo de trabalho geral.

Principais componentes

Este tutorial usa os dois aplicativos Python a seguir para testar o processo de snapshot:

  • Aplicativo cliente: um script Python executado em um pod padrão no seu cluster. Esse aplicativo gerencia o ciclo de vida do sandbox: ele cria o sandbox de maneira programática, pausa para acionar um snapshot, retoma o sandbox e verifica se o estado foi preservado. Neste tutorial, você cria uma conta de serviço do Kubernetes chamada agent-sandbox-client-sa e concede a ela permissões de RBAC para que o pod do aplicativo cliente possa gerenciar recursos personalizados do sandbox e acionar objetos de snapshot usando a API Kubernetes.
  • Aplicativo em sandbox: um script Python que incrementa e imprime um contador a cada segundo. Esse aplicativo é executado com segurança dentro do ambiente de sandbox isolado para gerar um estado em mudança que o aplicativo cliente pode verificar. Neste tutorial, você cria uma conta de serviço dedicada do Kubernetes chamada snapshot-sa e configura a Identidade da carga de trabalho para autorizar o pod em sandbox a ler e gravar objetos de snapshot com segurança no Cloud Storage.

Processo de configuração e teste

A lista a seguir resume as etapas necessárias para configurar seu ambiente e executar o teste:

  1. Criar um cluster: crie um cluster do Autopilot ou Standard com snapshots de pod e o recurso de sandbox do agente ativado.
  2. Crie um repositório do Artifact Registry: crie um repositório do Docker para armazenar a imagem do contêiner do aplicativo cliente.
  3. Instalar o Agent Sandbox: instale os componentes e extensões principais do agent-sandbox no cluster.
  4. Configure o armazenamento e as permissões: crie um bucket do Cloud Storage e configure as permissões da Identidade da carga de trabalho para permitir que os snapshots sejam salvos com segurança.
  5. Configurar snapshots de pod: crie e aplique a configuração de armazenamento de snapshot, a política de snapshot e o modelo de sandbox.
  6. Crie o aplicativo cliente: crie a imagem do contêiner para o aplicativo cliente e envie-a para o repositório do Artifact Registry.
  7. Execute o teste: implante o pod do aplicativo cliente, que cria o sandbox, pausa para capturar um snapshot, retoma e verifica se o estado do contador foi restaurado com sucesso.

Criar um cluster

Crie um novo cluster do GKE com os snapshots de pods ativados. Para compatibilidade total de recursos, especifique o canal de lançamento rápido.

Piloto automático

Crie um cluster do Autopilot com os recursos necessários:

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

Padrão

Crie um cluster padrão com os recursos necessários:

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}

Crie um pool de nós com o gVisor ativado:

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

Crie um repositório do Artifact Registry

Crie um repositório Docker no Artifact Registry para armazenar a imagem do contêiner do seu aplicativo cliente (o aplicativo que cria e gerencia o sandbox):

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

Instalar o sandbox de agente

Instale os componentes principais e as extensões do Agent Sandbox no cluster (usando a versão v0.4.6 como exemplo):

# 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

Configurar armazenamento e permissões

Configure um bucket do Cloud Storage para armazenar snapshots de pods e conceda as permissões necessárias da Identidade da carga de trabalho à conta de serviço snapshot-sa e ao agente de serviço do GKE. Isso permite que suas cargas de trabalho em sandbox salvem e recuperem objetos de snapshot com segurança:

  1. Crie um novo bucket do Cloud Storage

    gcloud storage buckets create "gs://${BUCKET_NAME}" \
        --uniform-bucket-level-access \
        --enable-hierarchical-namespace \
        --soft-delete-duration=0d \
        --location="${BUCKET_LOCATION}"
    
  2. Crie uma conta de serviço do Kubernetes no namespace default. O aplicativo em sandbox (o script de contador do Python) usa essa identidade para autenticar APIs externas e acessar com segurança objetos de snapshot armazenados no Cloud Storage:

    kubectl create serviceaccount "snapshot-sa" \
        --namespace "default"
    
  3. Vincule o papel storage.bucketViewer à sua conta de serviço usando a Identidade da carga de trabalho. Essa função permite que a carga de trabalho em sandbox liste o conteúdo do bucket e localize snapshots específicos:

    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. Vincule o papel storage.objectUser à sua conta de serviço usando o Workload Identity. Esse papel fornece a permissão para ler, salvar e excluir objetos binários de snapshot no 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. Conceda ao agente de serviço do GKE permissões para gerenciar (criar, listar, ler e excluir) objetos de snapshot no 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"
    

Configurar snapshots de pod

Crie e aplique os arquivos de configuração para instalar os recursos personalizados necessários do Kubernetes. Esses recursos definem como o cluster armazena e gerencia snapshots de pod:

  • PodSnapshotStorageConfig: especifica o bucket do Cloud Storage designado para armazenar objetos binários de snapshot.
  • PodSnapshotPolicy: define como os snapshots são acionados manualmente, com que frequência são agrupados e as políticas de retenção deles.
  • SandboxTemplate: define o contêiner, os seletores de nós e as contas de serviço subjacentes para executar a carga de trabalho isolada em sandbox.
  1. Crie um novo diretório para os arquivos do cliente de teste:

    mkdir test_client
    
  2. Navegue até o novo diretório:

    cd test_client
    
  3. Crie um arquivo chamado snapshot_storage_config.yaml. Essa configuração especifica o bucket do Cloud Storage de destino em que o cluster salva o estado binário do snapshot do pod:

    apiVersion: podsnapshot.gke.io/v1
    kind: PodSnapshotStorageConfig
    metadata:
      name: example-pod-snapshot-storage-config
    spec:
      snapshotStorageConfig:
        gcs:
          bucket: "$BUCKET_NAME"
    
  4. Substitua o marcador de posição da variável de ambiente no arquivo de configuração:

    sed -i "s/\$BUCKET_NAME/$BUCKET_NAME/g" snapshot_storage_config.yaml
    
  5. Aplique o manifesto de configuração de armazenamento:

    kubectl apply -f snapshot_storage_config.yaml
    
  6. Aguarde até que a configuração de armazenamento esteja pronta:

    kubectl wait --for=condition=Ready podsnapshotstorageconfig/example-pod-snapshot-storage-config --timeout=60s
    
  7. Crie um arquivo chamado snapshot_policy.yaml. Essa configuração estabelece uma regra de retenção que mantém um máximo de dois snapshots para sua carga de trabalho em sandbox. O tipo de acionador é definido como manual: isso permite que o aplicativo cliente controle snapshots sob demanda:

    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. Aplique o manifesto da política de snapshot:

    kubectl apply -f snapshot_policy.yaml
    
  9. Crie um arquivo chamado python-counter-template.yaml. Essa configuração define o pod de sandbox e atribui a ele a identidade da conta de serviço snapshot-sa. Essa atribuição ajuda a garantir que a sandbox seja executada com segurança. Dentro desse pod, o aplicativo em sandbox (um script Python) imprime continuamente um contador crescente nos registros do contêiner:

    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)
    
  10. Aplique o manifesto do modelo de sandbox:

    kubectl apply -f python-counter-template.yaml
    

Criar o aplicativo cliente

Crie a imagem do contêiner para o aplicativo cliente e faça upload dela no Artifact Registry.

  1. Crie um arquivo chamado Dockerfile.client. Esse arquivo define o ambiente de execução do Python e as dependências do aplicativo 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. Crie um arquivo chamado client_test.py. Esse script gerencia o ciclo de vida da caixa de simulação e verifica se o estado é retomado após a criação de um 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_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. Crie a imagem do contêiner do cliente e faça upload dela no Artifact Registry. Se o ambiente (como o Cloud Shell) tiver o Docker instalado, use-o para criar a imagem localmente. Se você estiver trabalhando em um ambiente sem Docker, use o Cloud Build para criar e enviar a imagem remotamente:

    Docker

    1. Configure a autenticação do Docker para o Artifact Registry:

      gcloud auth configure-docker "${LOCATION}-docker.pkg.dev"
      
    2. Crie e envie a imagem do contêiner do cliente localmente:

      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. Crie um arquivo chamado 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. Substitua os marcadores de posição das variável de ambiente no arquivo de configuração:

      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. Crie o bucket do Cloud Storage para registros do Cloud Build:

      gcloud storage buckets create "gs://${CLOUDBUILD_BUCKET_NAME}" \
          --location="${BUCKET_LOCATION}"
      
    4. Conceda as permissões necessárias à conta de serviço do Cloud Build.

      Os comandos a seguir usam a conta de serviço padrão do Compute Engine ($PROJECT_NUMBER-compute@developer.gserviceaccount.com), que é a padrão para a maioria dos projetos. Se o projeto usar a conta de serviço legada do Cloud Build, substitua o endereço de e-mail do membro nos comandos a seguir por $PROJECT_NUMBER@cloudbuild.gserviceaccount.com.

      Como você adicionou uma vinculação condicional do IAM ao projeto no início deste tutorial, inclua a flag --condition=None ao adicionar essas novas vinculações. Isso evita que gcloud pause para pedir confirmação interativa:

      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"
      
    5. Execute o build usando o Cloud Build:

      gcloud builds submit --config cloudbuild.yaml
      

Executar o teste

Implante o aplicativo cliente para criar o sandbox, acionar um snapshot e verificar se o contador interno retoma com êxito do estado salvo.

  1. Crie um arquivo chamado client_sa.yaml. Esse manifesto define a conta de serviço agent-sandbox-client-sa e as permissões RBAC necessárias para gerenciar recursos personalizados da 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. Aplique a conta de serviço do cliente e o manifesto do RBAC:

    kubectl apply -f client_sa.yaml
    
  3. Crie um arquivo chamado client_pod.yaml. Este manifesto cria o pod do aplicativo cliente usando a imagem de contêiner pré-criada:

    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. Substitua os marcadores de posição das variável de ambiente no manifesto:

    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. Aplique o manifesto do pod do aplicativo cliente:

    kubectl apply -f client_pod.yaml
    
  6. Transmita os registros do pod para verificar o fluxo de execução:

    kubectl logs -f agent-sandbox-client-pod
    

Quando o teste está sendo executado corretamente, a saída é semelhante a esta (abreviada aqui para facilitar a leitura):

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

A saída mostra que o sandbox preserva o estado quando é suspenso e retomado. O contador para de avançar enquanto o sandbox está suspenso (pausado e reduzido a zero) e retoma a contagem quando o sandbox é restaurado. Sem a suspensão, o contador teria continuado avançando durante o período de suspensão, e a contagem seria significativamente maior.

Limpar recursos

Para evitar cobranças na sua conta do Google Cloud , exclua os recursos criados:

  1. Exclua o cluster do GKE. Isso também exclui o pool de nós e todas as contas de serviço do Kubernetes nele:

    gcloud beta container clusters delete ${CLUSTER_NAME} --location="${LOCATION}" --quiet
    
  2. Exclua o repositório do Artifact Registry para remover o repositório do Docker criado para a imagem de teste:

    gcloud artifacts repositories delete ${REPOSITORY_NAME} --location="${LOCATION}" --quiet
    
  3. Exclua o bucket do Cloud Storage e todos os snapshots nele. Isso remove automaticamente as vinculações do IAM da Identidade da carga de trabalho no nível do bucket aplicadas a ele:

    gcloud storage rm --recursive "gs://${BUCKET_NAME}"
    
  4. Remova a vinculação do IAM no nível do projeto para o agente de serviço do 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 você usou o Cloud Build em vez do Docker para criar e enviar a imagem do contêiner, exclua o bucket de registros e remova as permissões da conta de serviço:

    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"
    

A seguir