Run agent-generated Python in GKE Agent Sandbox on Google Axion
Introduction
Prepare your local machine for Agent Sandbox on GKE
Create the GKE cluster and Axion node pool
Claim a sandbox and test its restrictions
Analyze a CSV file in the sandbox
Open a shell and inspect the sandbox
Connect Antigravity CLI and run code in the sandbox
Preserve results and delete cloud resources
Next Steps
Run agent-generated Python in GKE Agent Sandbox on Google Axion
Introduction
Prepare your local machine for Agent Sandbox on GKE
Create the GKE cluster and Axion node pool
Claim a sandbox and test its restrictions
Analyze a CSV file in the sandbox
Open a shell and inspect the sandbox
Connect Antigravity CLI and run code in the sandbox
Preserve results and delete cloud resources
Next Steps
Choose the cluster configuration
Continue in the same terminal with LP_WORKDIR still set. Run cd "$LP_WORKDIR" if you opened a new terminal.
You’ll create one small system node and one Arm sandbox node. The system pool will use an e2-standard-2 node. Your Python workloads will use the separate c4a-standard-2 pool which uses Google Axion processors.
Set the names and networking for a dedicated tutorial cluster:
cd "$LP_WORKDIR"
export LOCATION=us-central1-c
export REGION=us-central1
export CLUSTER_NAME=gke-agent-sandbox
export NODE_POOL_NAME=axion-sandbox-pool
export MACHINE_TYPE=c4a-standard-2
export NAMESPACE=axion-sandbox-prototype
export NETWORK=gke-sandbox-vpc
export SUBNETWORK=gke-sandbox-subnet
Create a dedicated VPC network and subnet for the cluster. VPC networks and subnets have no cost on their own:
gcloud compute networks create "$NETWORK" \
--project="$PROJECT_ID" --subnet-mode=custom
gcloud compute networks subnets create "$SUBNETWORK" \
--project="$PROJECT_ID" --network="$NETWORK" \
--region="$REGION" --range=10.0.0.0/20
You’ll use these environment variables for subsequent commands, so keep this terminal open or re-export them if you start a new session. us-central1-c is the default zone. If you hit C4A capacity issues, us-east4-a is a good alternative. Update both LOCATION and REGION, then recreate the subnet in the new region.
If you saved env.sh earlier, append these variables to it:
cat >> "$LP_WORKDIR/env.sh" << EOF
export LOCATION="$LOCATION"
export REGION="$REGION"
export CLUSTER_NAME="$CLUSTER_NAME"
export NODE_POOL_NAME="$NODE_POOL_NAME"
export MACHINE_TYPE="$MACHINE_TYPE"
export NAMESPACE="$NAMESPACE"
export NETWORK="$NETWORK"
export SUBNETWORK="$SUBNETWORK"
EOF
Check access, quota, and versions
The following commands enable the Google Kubernetes Engine (GKE) and Compute APIs on your project, which are required before you can create a cluster.
The commands also perform the following tasks:
- Confirm that billing is active.
- Verify that the Axion machine type is available in your zone.
- Save the quota and available GKE versions for your chosen region to
output/server-config.jsonfor troubleshooting.
Run the commands:
gcloud billing projects describe "$PROJECT_ID"
gcloud services enable container.googleapis.com compute.googleapis.com --project="$PROJECT_ID"
gcloud compute machine-types describe "$MACHINE_TYPE" --zone="$LOCATION" --project="$PROJECT_ID"
gcloud compute regions describe "$REGION" --project="$PROJECT_ID" \
--format='table(quotas.metric,quotas.limit,quotas.usage)'
gcloud container get-server-config --location="$LOCATION" --project="$PROJECT_ID" \
--format=json > output/server-config.json
cat output/server-config.json
The saved server config lists the available GKE versions for your location. Query the config to see which versions are available in the regular release channel:
jq -r '.channels[] | select(.channel=="REGULAR") | .validVersions[]' output/server-config.json
Agent Sandbox requires GKE 1.36.3-gke.1767000 or later with the v1beta1 API. Pick the latest available version in the regular channel that meets this requirement. The version used for testing was 1.36.4-gke.1391000, but your version might be newer.
Set your chosen version and channel:
export CLUSTER_VERSION=1.36.4-gke.1391000 # replace with your chosen version
export RELEASE_CHANNEL=regular
If you’re using env.sh, append these variables to the file:
cat >> "$LP_WORKDIR/env.sh" << EOF
export CLUSTER_VERSION="$CLUSTER_VERSION"
export RELEASE_CHANNEL="$RELEASE_CHANNEL"
EOF
Provision the cluster
Run each provisioning command separately and check its result before continuing.
First, create the system pool:
gcloud beta container clusters create "$CLUSTER_NAME" \
--project="$PROJECT_ID" --location="$LOCATION" \
--cluster-version="$CLUSTER_VERSION" --release-channel="$RELEASE_CHANNEL" \
--network="$NETWORK" --subnetwork="$SUBNETWORK" \
--num-nodes=1 --machine-type=e2-standard-2 \
--image-type=COS_CONTAINERD --disk-size=30 --enable-ip-alias
Add the Axion pool with gVisor enabled. C4A uses Hyperdisk boot storage, so set its disk type explicitly:
gcloud container node-pools create "$NODE_POOL_NAME" \
--project="$PROJECT_ID" --cluster="$CLUSTER_NAME" --location="$LOCATION" \
--machine-type="$MACHINE_TYPE" --num-nodes=1 \
--image-type=COS_CONTAINERD --disk-type=hyperdisk-balanced --disk-size=100 \
--sandbox=type=gvisor
Enable the managed Agent Sandbox feature:
gcloud beta container clusters update "$CLUSTER_NAME" \
--project="$PROJECT_ID" --location="$LOCATION" --enable-agent-sandbox
Confirm that the Axion node is running before continuing:
gcloud container node-pools describe "$NODE_POOL_NAME" \
--project="$PROJECT_ID" --cluster="$CLUSTER_NAME" --location="$LOCATION" \
--format="table(name,status,config.machineType,config.sandboxConfig.type)"
The node pool status should be RUNNING, the machine type c4a-standard-2, and the sandbox type gvisor.
Handle capacity failures
If the node pool can’t allocate a node, check the operation status:
gcloud container operations list --project="$PROJECT_ID" --location="$LOCATION" \
--filter="operationType=CREATE_NODE_POOL" --format="table(name,status,statusMessage)"
An insufficient-capacity error means that no c4a-standard-2 nodes were available in LOCATION. Delete the cluster, then retry in a different zone or with a larger machine type:
gcloud container clusters delete "$CLUSTER_NAME" \
--project="$PROJECT_ID" --location="$LOCATION" --quiet
To try a different zone, update LOCATION and re-run the commands under
Provision the cluster
:
export LOCATION=us-central1-a
To try a larger machine type, update MACHINE_TYPE and re-run the commands under
Provision the cluster
:
export MACHINE_TYPE=c4a-standard-4
For a quota error, the statusMessage names the quota that was exceeded. Go to IAM & Admin > Quotas in the Google Cloud console, find that quota, and request an increase before retrying.
Install the matching kubectl version
Now that the cluster exists, install a kubectl version within one minor version of its control plane, following the
Kubernetes version skew policy
. Set KUBECTL_VERSION to match the minor of the CLUSTER_VERSION that you chose earlier.
The following block detects your OS and architecture, downloads the matching binary, and verifies its checksum. Run it as a subshell so that a download failure stops the block:
(
set -eu
KUBECTL_VERSION=v1.36.3
case "$(uname -s)" in
Linux) KUBECTL_OS=linux ;;
Darwin) KUBECTL_OS=darwin ;;
*) echo 'Use a native kubectl build for your OS.' >&2; exit 1 ;;
esac
case "$(uname -m)" in
aarch64|arm64) KUBECTL_ARCH=arm64 ;;
x86_64|amd64) KUBECTL_ARCH=amd64 ;;
*) echo 'Use a native kubectl build for your architecture.' >&2; exit 1 ;;
esac
DOWNLOAD_DIR=$(mktemp -d)
cd "$DOWNLOAD_DIR"
KUBECTL_URL="https://dl.k8s.io/release/$KUBECTL_VERSION/bin/$KUBECTL_OS/$KUBECTL_ARCH/kubectl"
curl -fL "$KUBECTL_URL" -o kubectl
curl -fL "$KUBECTL_URL.sha256" -o kubectl.sha256
if [ "$KUBECTL_OS" = linux ]; then
printf '%s kubectl\n' "$(cat kubectl.sha256)" | sha256sum --check
else
printf '%s kubectl\n' "$(cat kubectl.sha256)" | shasum -a 256 --check
fi
mkdir -p "$HOME/.local/bin"
chmod 755 kubectl
cp kubectl "$HOME/.local/bin/kubectl"
)
export PATH="$HOME/.local/bin:$PATH"
hash -r
kubectl version --client
Add the PATH entry to your shell startup file if desired.
Verify the Arm sandbox pool
Obtain cluster credentials and save the explicit context for later helper commands:
gcloud container clusters get-credentials "$CLUSTER_NAME" \
--project="$PROJECT_ID" --location="$LOCATION"
export GKE_CONTEXT="$(kubectl config current-context)"
If you’re using env.sh, append GKE_CONTEXT:
echo "export GKE_CONTEXT=\"$GKE_CONTEXT\"" >> "$LP_WORKDIR/env.sh"
Check that the nodes are ready and note the architecture, machine type, and sandbox runtime:
kubectl --context "$GKE_CONTEXT" get nodes \
-L kubernetes.io/arch,node.kubernetes.io/instance-type,sandbox.gke.io/runtime
The sandbox node row should show arm64, c4a-standard-2, and gvisor:
NAME STATUS ROLES AGE VERSION ARCH INSTANCE-TYPE RUNTIME
gke-gke-agent-sandbox-axion-sandbox-pool-<hash> Ready <none> 2m v1.36.x arm64 c4a-standard-2 gvisor
gke-gke-agent-sandbox-default-pool-<hash> Ready <none> 4m v1.36.x amd64 e2-standard-2
Confirm that the gVisor runtime class is registered:
kubectl --context "$GKE_CONTEXT" get runtimeclass gvisor
The output is similar to:
NAME HANDLER AGE
gvisor runsc 2m
Confirm that the Agent Sandbox API resources are available:
kubectl --context "$GKE_CONTEXT" api-resources --api-group=extensions.agents.x-k8s.io
The output is similar to:
NAME SHORTNAMES APIVERSION NAMESPACED KIND
sandboxclaims extensions.agents.x-k8s.io/v1beta1 true SandboxClaim
sandboxtemplates extensions.agents.x-k8s.io/v1beta1 true SandboxTemplate
sandboxwarmpools extensions.agents.x-k8s.io/v1beta1 true SandboxWarmPool
Save the cluster and node pool configuration to the output/ directory:
gcloud beta container clusters describe "$CLUSTER_NAME" \
--project="$PROJECT_ID" --location="$LOCATION" --format=json > output/cluster.json
gcloud container node-pools describe "$NODE_POOL_NAME" \
--project="$PROJECT_ID" --cluster="$CLUSTER_NAME" --location="$LOCATION" \
--format=json > output/node-pool.json
What you’ve accomplished and what’s next
You’ve enabled Agent Sandbox and prepared a gVisor node pool on Google Axion.
Next, you’ll define the Python environment, claim a sandbox, and verify its filesystem restrictions.