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
Create a sandbox for interactive inspection
You don’t need an SSH server or SSH keys inside the sandbox. kubectl exec uses your Kubernetes authentication to open a process in the selected container.
Download inspect_filesystem.py into your working directory:
cd "$LP_WORKDIR"
curl -fL https://raw.githubusercontent.com/ArmDeveloperEcosystem/arm-learning-paths/main/content/learning-paths/servers-and-cloud-computing/gke-agent-sandbox/files/inspect_filesystem.py \
-o inspect_filesystem.py
Create a sandbox claim and wait for the Pod to be ready:
rm -rf "$LP_WORKDIR/runs/shell"
python3 claim.py create \
--context "$GKE_CONTEXT" --namespace "$NAMESPACE" \
--pool axion-python-pool --purpose shell --output-dir "$LP_WORKDIR/runs/shell"
Load the Pod name into your shell, verify the binding, then open a shell in the sandbox:
source "$LP_WORKDIR/runs/shell/binding.env"
python3 claim.py check --record "$LP_WORKDIR/runs/shell/run.json"
kubectl --context "$GKE_CONTEXT" -n "$NAMESPACE" exec -it "$POD_NAME" -c runtime -- /bin/sh
You now have a shell inside the sandbox.
Inspect the user and Linux environment
Run the following commands inside the sandbox shell:
id
uname -m
uname -sr
pwd
python3 --version
cat /etc/os-release
ls -la /workspace
The expected output is:
uid=1000 gid=1000 groups=1000
aarch64
Linux 4.19.0-gvisor
/workspace
Python 3.12.15
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
...
total 4
drwxrwxrwt 2 root root 40 ...
drwxr-xr-x 2 root root 4096 ...
The kernel release can include gvisor.
gVisor supplies an advertised kernel identity rather than reporting the real kernel version of the node. The Debian release identifies the operating system environment of the container.
Retrieve CPU and memory information within the sandbox:
lscpu
head -n 12 /proc/meminfo
The CPU shows Neoverse-V2 with 2 cores, confirming this is a Google Axion node. Memory shows 512MB total, matching the 512Mi limit set in the sandbox template.
Write a test file to the writable workspace, then leave the sandbox shell:
printf 'created in my sandbox shell\n' > /workspace/shell-marker.txt
cat /workspace/shell-marker.txt
exit
You’ve now returned to your local machine terminal. Exiting the shell doesn’t delete its Pod or claim.
Compare filesystem writes
inspect_filesystem.py attempts to write a file to several paths in the sandbox and reports whether each write succeeded or was denied.
Review the script before running it:
cat inspect_filesystem.py
Run the script from your local machine in the same Pod:
python3 claim.py check --record "$LP_WORKDIR/runs/shell/run.json"
kubectl --context "$GKE_CONTEXT" -n "$NAMESPACE" exec -i "$POD_NAME" -c runtime -- \
python3 - < inspect_filesystem.py | tee "$LP_WORKDIR/output/filesystem-probes.txt"
The output is similar to:
/workspace: write/read/remove succeeded
/tmp: write/read/remove succeeded
/etc: denied (errno 13: Permission denied)
/usr/local: denied (errno 13: Permission denied)
Denied-write errno values and messages vary.
The output shows which paths are writable and which are blocked. /workspace and /tmp succeed because they’re writable volumes in the template. /etc and /usr/local are blocked because the root filesystem is read-only and the process runs as a non-root user.
Verify sandbox configuration
Reconnect to confirm that the test file is still there:
python3 claim.py check --record "$LP_WORKDIR/runs/shell/run.json"
kubectl --context "$GKE_CONTEXT" -n "$NAMESPACE" exec -it "$POD_NAME" -c runtime -- /bin/sh
Inside the sandbox, read and remove the test file. Check for the default service-account token:
cat /workspace/shell-marker.txt
rm /workspace/shell-marker.txt
if test -e /var/run/secrets/kubernetes.io/serviceaccount/token; then
echo 'Unexpected: default service-account token exists'
else
echo 'Default service-account token is absent'
fi
exit
The token should be absent. If it exists, stop and inspect the Pod configuration before continuing.
Save the Pod configuration and check the resource limits:
kubectl --context "$GKE_CONTEXT" -n "$NAMESPACE" get pod "$POD_NAME" -o yaml \
> "$LP_WORKDIR/output/interactive-pod.yaml"
kubectl --context "$GKE_CONTEXT" -n "$NAMESPACE" get pod "$POD_NAME" \
-o jsonpath='{.spec.runtimeClassName}{"\n"}{.spec.containers[0].resources.limits}{"\n"}'
Confirm RuntimeClass gvisor and limits 500m CPU and 512Mi memory.
Release the claim for interactive inspection
Remove the claim when you’re done:
python3 claim.py delete --record "$LP_WORKDIR/runs/shell/run.json"
Keep the cluster and warm pool running.
What you’ve accomplished and what’s next
You’ve inspected the environment interactively and verified permitted and denied writes. You’ve also confirmed that the sandbox runs as a non-root user with the expected resource limits.
Next, you’ll connect the Antigravity CLI to the sandbox and run agent-generated Python remotely.