Memicu snapshot Sandbox Agen dari dalam cluster

Gunakan snapshot Agent Sandbox dalam cluster Google Kubernetes Engine (GKE) untuk mempertahankan status lingkungan sandbox Anda. Pendekatan ini memberikan proses debug yang konsisten, mempercepat iterasi pengembangan, dan memfasilitasi pemulihan cepat konfigurasi yang kompleks. Dalam tutorial ini, Anda akan men-deploy dan menguji kemampuan ini dengan menjalankan aplikasi klien yang secara terprogram membuat, menjeda, dan melanjutkan lingkungan sandbox di dalam cluster.

Untuk mengetahui informasi selengkapnya tentang pengambilan snapshot Pod, lihat Memulihkan dari snapshot Pod.

Biaya

Agent Sandbox ditawarkan tanpa biaya tambahan di GKE. Harga GKE berlaku untuk resource yang Anda buat.

Sebelum memulai

  1. Di konsol Cloud de Confiance , pada halaman pemilih project, pilih atau buat project Cloud de Confiance .

    Peran yang diperlukan untuk memilih atau membuat project

    • Pilih project: Memilih project tidak memerlukan peran IAM tertentu—Anda dapat memilih project mana pun yang telah diberi peran.
    • Membuat project: Untuk membuat project, Anda memerlukan peran Project Creator (roles/resourcemanager.projectCreator), yang berisi izin resourcemanager.projects.create. Pelajari cara memberikan peran.

    Buka pemilih project

  2. Verifikasi bahwa penagihan diaktifkan untuk project Cloud de Confiance Anda.

  3. Aktifkan Artifact Registry, Kubernetes Engine, Cloud Build API.

    Peran yang diperlukan untuk mengaktifkan API

    Untuk mengaktifkan API, Anda memerlukan peran IAM Service Usage Admin (roles/serviceusage.serviceUsageAdmin), yang berisi izin serviceusage.services.enable. Pelajari cara memberikan peran.

    Aktifkan API

  4. Di konsol Cloud de Confiance , aktifkan Cloud Shell.

    Aktifkan Cloud Shell

  5. Pastikan Anda memiliki izin yang diperlukan untuk menyelesaikan tutorial ini.

Peran yang diperlukan

Untuk mendapatkan izin yang diperlukan guna membuat dan mengelola sandbox, minta administrator untuk memberi Anda peran IAM Kubernetes Engine Admin (roles/container.admin) di project Anda. Untuk mengetahui informasi selengkapnya tentang cara memberikan peran, lihat Mengelola akses ke project, folder, dan organisasi.

Anda mungkin juga bisa mendapatkan izin yang diperlukan melalui peran khusus atau peran bawaan lainnya.

Batasan

Di cluster regional, node di zona yang berbeda mungkin memiliki mikroarsitektur CPU yang berbeda. Karena snapshot merekam status CPU, memulihkan snapshot pada node dengan fitur CPU yang tidak ada akan gagal (misalnya, dengan error OCI runtime restore failed: incompatible FeatureSet).

Untuk menghindari masalah ini, gunakan konfigurasi yang sesuai untuk lingkungan Anda:

  • Produksi: Untuk mempertahankan ketersediaan tinggi di seluruh cluster, jangan menetapkan workload ke zona tertentu. Sebagai gantinya, bantu pastikan konsistensi fitur CPU di semua zona dengan menentukan platform CPU minimum. Untuk mengetahui informasi selengkapnya, lihat Memilih platform CPU minimum.
  • Pengujian: Untuk menyederhanakan penyiapan dan menghindari error ketidakcocokan CPU awal, Anda dapat menggunakan kolom nodeSelector dalam manifes SandboxTemplate untuk menyematkan Pod ke zona tertentu, seperti us-central1-a. Contoh dalam tutorial ini menggunakan konfigurasi pengujian ini.

Menentukan variabel lingkungan

Untuk menyederhanakan perintah yang Anda jalankan dalam tutorial ini, Anda dapat menetapkan variabel lingkungan di Cloud Shell. Di Cloud Shell, tentukan variabel lingkungan berguna berikut dengan menjalankan perintah berikut:

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"

Berikut penjelasan variabel lingkungan ini:

  • PROJECT_ID: ID project Cloud de Confiance by S3NS saat ini. Menentukan variabel ini membantu memastikan bahwa semua resource dibuat di project yang benar.
  • PROJECT_NUMBER: nomor project dari Cloud de Confiance by S3NS project saat ini.
  • CLUSTER_NAME: nama cluster GKE Anda—misalnya, test-snapshot.
  • LOCATION: Cloud de Confiance by S3NS region tempat cluster GKE dan repositori Artifact Registry Anda berada—misalnya, us-central1.
  • BUCKET_LOCATION: lokasi bucket Cloud Storage Anda—misalnya, us.
  • BUCKET_NAME: nama bucket Cloud Storage yang digunakan untuk snapshot.
  • CLOUDBUILD_BUCKET_NAME: nama bucket Cloud Storage yang digunakan untuk log Cloud Build.
  • MACHINE_TYPE: jenis mesin yang akan digunakan untuk node cluster—misalnya, e2-standard-8.
  • REPOSITORY_NAME: nama repositori Artifact Registry—misalnya, agent-sandbox.

Ringkasan langkah-langkah konfigurasi

Untuk mengaktifkan dan menguji snapshot Pod lingkungan Agent Sandbox dari dalam cluster, Anda perlu melakukan beberapa langkah konfigurasi. Untuk memahami langkah-langkah ini, sebaiknya pahami terlebih dahulu komponen yang terlibat dalam keseluruhan alur kerja.

Komponen penting

Tutorial ini menggunakan dua aplikasi Python berikut untuk menguji proses snapshot:

  • Aplikasi klien: skrip Python yang berjalan di Pod standar di cluster Anda. Aplikasi ini mengelola siklus proses sandbox: aplikasi ini membuat sandbox secara terprogram, menjedanya untuk memicu snapshot, melanjutkan sandbox, dan memverifikasi bahwa status dipertahankan. Dalam tutorial ini, Anda membuat akun layanan Kubernetes bernama agent-sandbox-client-sa dan memberikan izin RBAC sehingga Pod aplikasi klien dapat mengelola resource kustom sandbox dan objek pemicu snapshot menggunakan Kubernetes API.
  • Aplikasi sandbox: skrip Python yang menambah dan mencetak penghitung setiap detik. Aplikasi ini berjalan dengan aman di dalam lingkungan sandbox terisolasi untuk menghasilkan status yang berubah yang dapat diverifikasi oleh aplikasi klien. Dalam tutorial ini, Anda akan membuat akun layanan Kubernetes khusus bernama snapshot-sa dan mengonfigurasi Workload Identity untuk mengizinkan Pod sandbox membaca dan menulis objek snapshot dengan aman di Cloud Storage.

Proses konfigurasi dan pengujian

Daftar berikut merangkum langkah-langkah yang perlu Anda lakukan untuk menyiapkan lingkungan dan menjalankan pengujian:

  1. Buat cluster: buat cluster Autopilot atau Standard dengan snapshot Pod dan fitur Agent Sandbox yang diaktifkan.
  2. Buat repositori Artifact Registry: buat repositori Docker untuk menyimpan image container aplikasi klien Anda.
  3. Instal Agent Sandbox: instal komponen dan ekstensi agent-sandbox inti di cluster Anda.
  4. Mengonfigurasi penyimpanan dan izin: buat bucket Cloud Storage dan konfigurasi izin Workload Identity untuk memungkinkan snapshot disimpan dengan aman.
  5. Mengonfigurasi snapshot Pod: buat dan terapkan konfigurasi penyimpanan snapshot, kebijakan snapshot, dan template sandbox.
  6. Bangun aplikasi klien: bangun image container untuk aplikasi klien dan kirimkan ke repositori Artifact Registry Anda.
  7. Jalankan pengujian: deploy Pod aplikasi klien, yang membuat sandbox, menjedanya untuk merekam cuplikan, melanjutkannya, dan memverifikasi bahwa status penghitung berhasil dipulihkan.

Membuat cluster

Buat cluster GKE baru dengan snapshot Pod yang diaktifkan. Untuk kompatibilitas fitur penuh, tentukan saluran rilis cepat.

Autopilot

Buat cluster Autopilot dengan fitur yang diperlukan:

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

Standar

Buat cluster Standard dengan fitur yang diperlukan:

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

Buat node pool dengan gVisor yang diaktifkan:

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

Membuat repositori Artifact Registry

Buat repositori Docker di Artifact Registry untuk menyimpan image container aplikasi klien Anda (aplikasi yang membuat dan mengelola sandbox):

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

Menginstal Sandbox Agen

Instal komponen dan ekstensi inti Agent Sandbox di cluster Anda (menggunakan versi v0.4.6 sebagai contoh):

# 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

Mengonfigurasi penyimpanan dan izin

Konfigurasi bucket Cloud Storage untuk menyimpan snapshot Pod dan berikan izin Workload Identity yang diperlukan ke akun layanan snapshot-sa dan agen layanan GKE. Hal ini memungkinkan workload dengan sandbox Anda menyimpan dan mengambil objek snapshot dengan aman:

  1. Buat bucket Cloud Storage baru:

    gcloud storage buckets create "gs://${BUCKET_NAME}" \
        --uniform-bucket-level-access \
        --enable-hierarchical-namespace \
        --soft-delete-duration=0d \
        --location="${BUCKET_LOCATION}"
    
  2. Buat akun layanan Kubernetes di namespace default. Aplikasi sandbox Anda (skrip penghitung Python) menggunakan identitas ini untuk mengautentikasi ke API eksternal dan mengakses objek snapshot yang disimpan di Cloud Storage secara aman:

    kubectl create serviceaccount "snapshot-sa" \
        --namespace "default"
    
  3. Ikat peran storage.bucketViewer ke akun layanan Anda menggunakan Workload Identity. Peran ini memungkinkan workload yang di-sandbox mencantumkan konten bucket dan menemukan snapshot tertentu:

    gcloud storage buckets add-iam-policy-binding "gs://${BUCKET_NAME}" \
        --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.s3ns.svc.id.goog/subject/ns/default/sa/snapshot-sa" \
        --role="roles/storage.bucketViewer"
    
  4. Ikat peran storage.objectUser ke akun layanan Anda menggunakan Workload Identity. Peran ini memberikan izin untuk membaca, menyimpan, dan menghapus objek biner snapshot dalam bucket:

    gcloud storage buckets add-iam-policy-binding "gs://${BUCKET_NAME}" \
        --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.s3ns.svc.id.goog/subject/ns/default/sa/snapshot-sa" \
        --role="roles/storage.objectUser"
    
  5. Beri agen layanan GKE izin untuk mengelola (membuat, mencantumkan, membaca, dan menghapus) objek snapshot dalam bucket:

    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
        --member="serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.s3ns-system.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"
    

Mengonfigurasi snapshot Pod

Buat dan terapkan file konfigurasi untuk menginstal resource kustom Kubernetes yang diperlukan. Resource ini menentukan cara cluster menyimpan dan mengelola snapshot Pod:

  • PodSnapshotStorageConfig: menentukan bucket Cloud Storage yang ditetapkan untuk menyimpan objek biner snapshot.
  • PodSnapshotPolicy: menentukan cara snapshot dipicu secara manual, frekuensi pengelompokannya, dan kebijakan retensinya.
  • SandboxTemplate: menentukan penampung yang mendasarinya, pemilih node, dan akun layanan untuk menjalankan workload sandbox yang terisolasi.
  1. Buat direktori baru untuk file klien pengujian:

    mkdir test_client
    
  2. Buka direktori baru:

    cd test_client
    
  3. Buat file bernama snapshot_storage_config.yaml. Konfigurasi ini menentukan bucket Cloud Storage target tempat cluster menyimpan status snapshot Pod biner:

    apiVersion: podsnapshot.gke.io/v1
    kind: PodSnapshotStorageConfig
    metadata:
      name: example-pod-snapshot-storage-config
    spec:
      snapshotStorageConfig:
        gcs:
          bucket: "$BUCKET_NAME"
    
  4. Ganti placeholder variabel lingkungan dalam file konfigurasi:

    sed -i "s/\$BUCKET_NAME/$BUCKET_NAME/g" snapshot_storage_config.yaml
    
  5. Terapkan manifes konfigurasi penyimpanan:

    kubectl apply -f snapshot_storage_config.yaml
    
  6. Tunggu hingga konfigurasi penyimpanan siap:

    kubectl wait --for=condition=Ready podsnapshotstorageconfig/example-pod-snapshot-storage-config --timeout=60s
    
  7. Buat file bernama snapshot_policy.yaml. Konfigurasi ini menetapkan aturan retensi yang menyimpan maksimum dua snapshot untuk beban kerja sandbox Anda. Jenis pemicu ditetapkan ke manual: hal ini memungkinkan aplikasi klien mengontrol snapshot sesuai permintaan:

    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. Terapkan manifes kebijakan snapshot:

    kubectl apply -f snapshot_policy.yaml
    
  9. Buat file bernama python-counter-template.yaml. Konfigurasi ini menentukan Pod sandbox, dan menetapkan identitas akun layanan snapshot-sa kepadanya. Penetapan ini membantu memastikan bahwa sandbox berjalan dengan aman. Di dalam Pod tersebut, aplikasi yang di-sandbox (skrip Python) terus-menerus mencetak penghitung yang bertambah ke log container:

    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. Terapkan manifes template sandbox:

    kubectl apply -f python-counter-template.yaml
    

Membangun aplikasi klien

Buat image container untuk aplikasi klien dan upload ke Artifact Registry.

  1. Buat file bernama Dockerfile.client. File ini menentukan lingkungan runtime dan dependensi Python untuk aplikasi klien:

    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. Buat file bernama client_test.py. Skrip ini mengelola siklus proses sandbox dan memverifikasi bahwa status berhasil dilanjutkan setelah mengambil 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. Bangun image container klien dan upload ke Artifact Registry. Jika lingkungan Anda (seperti Cloud Shell) telah menginstal Docker, Anda dapat menggunakan Docker untuk mem-build image secara lokal. Jika Anda bekerja di lingkungan tanpa Docker, Anda dapat menggunakan Cloud Build untuk membangun dan mengirim image dari jarak jauh:

    Docker

    1. Konfigurasi autentikasi Docker untuk Artifact Registry:

      gcloud auth configure-docker "${LOCATION}-docker.pkg.dev"
      
    2. Bangun dan kirim image container klien secara lokal:

      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. Buat file bernama 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. Ganti placeholder variabel lingkungan dalam file konfigurasi:

      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. Buat bucket Cloud Storage untuk log Cloud Build:

      gcloud storage buckets create "gs://${CLOUDBUILD_BUCKET_NAME}" \
          --location="${BUCKET_LOCATION}"
      
    4. Berikan izin yang diperlukan ke akun layanan Cloud Build.

      Perintah berikut menggunakan akun layanan default Compute Engine ($PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com), yang merupakan akun default untuk sebagian besar project. Jika project Anda menggunakan akun layanan Cloud Build lama, ganti alamat email anggota dalam perintah berikut dengan $PROJECT_NUMBER@cloudbuild.s3ns-system.iam.gserviceaccount.com.

      Karena Anda menambahkan binding IAM bersyarat ke project sebelumnya dalam tutorial ini, Anda harus menyertakan flag --condition=None saat menambahkan binding baru ini. Tindakan ini mencegah gcloud dijeda untuk meminta konfirmasi interaktif:

      gcloud projects add-iam-policy-binding $PROJECT_ID \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com" \
         --role="roles/artifactregistry.writer"
      
      gcloud projects add-iam-policy-binding $PROJECT_ID \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com" \
         --role="roles/logging.logWriter"
      
      gcloud storage buckets add-iam-policy-binding "gs://$CLOUDBUILD_BUCKET_NAME" \
         --member="serviceAccount:$PROJECT_NUMBER-compute@developer.s3ns-system.iam.gserviceaccount.com" \
         --role="roles/storage.objectAdmin"
      
    5. Jalankan build menggunakan Cloud Build:

      gcloud builds submit --config cloudbuild.yaml
      

Menjalankan pengujian

Deploy aplikasi klien untuk membuat sandbox, memicu snapshot, dan memverifikasi bahwa penghitung internal berhasil dilanjutkan dari status tersimpannya.

  1. Buat file bernama client_sa.yaml. Manifes ini menentukan akun layanan agent-sandbox-client-sa dan izin RBAC yang diperlukan untuk mengelola resource kustom 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. Terapkan akun layanan klien dan manifes RBAC:

    kubectl apply -f client_sa.yaml
    
  3. Buat file bernama client_pod.yaml. Manifes ini membuat Pod aplikasi klien menggunakan image container bawaan:

    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. Ganti placeholder variabel lingkungan dalam manifes:

    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. Terapkan manifes Pod aplikasi klien:

    kubectl apply -f client_pod.yaml
    
  6. Streaming log Pod untuk memverifikasi alur eksekusi:

    kubectl logs -f agent-sandbox-client-pod
    

Jika pengujian berjalan dengan benar, output-nya akan terlihat mirip dengan ini (dipersingkat di sini agar mudah dibaca):

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

Output menunjukkan bahwa sandbox berhasil mempertahankan statusnya saat ditangguhkan dan dilanjutkan. Penghitung berhenti bertambah saat sandbox ditangguhkan (dijeda dan diskalakan ke nol), dan melanjutkan penghitung saat sandbox dipulihkan. Tanpa penangguhan, penghitung akan terus bertambah selama periode penangguhan dan jumlahnya akan jauh lebih tinggi.

Membersihkan resource

Agar akun Cloud de Confiance by S3NS Anda tidak dikenai biaya, hapus resource yang Anda buat:

  1. Hapus cluster GKE. Tindakan ini juga akan menghapus node pool dan semua akun layanan Kubernetes di dalamnya:

    gcloud beta container clusters delete ${CLUSTER_NAME} --location="${LOCATION}" --quiet
    
  2. Hapus repositori Artifact Registry untuk menghapus repositori Docker yang Anda buat untuk image pengujian:

    gcloud artifacts repositories delete ${REPOSITORY_NAME} --location="${LOCATION}" --quiet
    
  3. Hapus bucket Cloud Storage dan semua snapshot di dalamnya. Tindakan ini akan otomatis menghapus binding IAM Workload Identity tingkat bucket yang diterapkan padanya:

    gcloud storage rm --recursive "gs://${BUCKET_NAME}"
    
  4. Hapus binding IAM level project untuk agen layanan GKE:

    gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
        --member="serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.s3ns-system.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. Jika Anda menggunakan Cloud Build, bukan Docker, untuk membangun dan mengirim image container, hapus bucket log dan hapus izin akun layanan:

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

Langkah berikutnya