Files
wehub-resource-sync e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

380 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: AKS + Kata
description: Deploy OpenSandbox onto an AKS cluster with Kata VM isolation and interact with a Kata-isolated sandbox step by step, covering ingress, egress, and Credential Vault.
---
# AKS Kata Example
Deploy OpenSandbox onto an AKS cluster with `kata-vm-isolation`, then interact with a Kata-isolated sandbox step by step.
::: warning Demo credentials
The Helm values in this example use **insecure, well-known demo credentials**
(`api_key = "aks-kata-demo-key"` and a fixed `secureAccess` signing key) so the
walkthrough works out of the box with local `kubectl port-forward`. **Change
them before exposing the server or gateway beyond local port-forwarding.** See
[Security Model](#security-model).
:::
## Prerequisites
- AKS cluster with `kubectl get runtimeclass` showing `kata-vm-isolation`
- Helm 3
- Python 3.10+ with `pip install opensandbox requests`
- Azure OpenAI resource (endpoint + API key)
## Files
The example lives in [`examples/aks-kata`](https://github.com/opensandbox-group/OpenSandbox/tree/main/examples/aks-kata):
| File | Purpose |
|------|---------|
| `main.py` | CLI tool — create, inspect, and operate on sandboxes one step at a time |
| `controller-values.yaml` | Helm values for the OpenSandbox controller |
| `server-values.yaml` | Helm values for the lifecycle server and ingress gateway |
| `batchsandbox-template-configmap.yaml` | BatchSandbox template that pins pods onto Kata nodes |
## 1. Install OpenSandbox
From the repo root:
```bash
kubectl create namespace opensandbox-system --dry-run=client -o yaml | kubectl apply -f -
kubectl create namespace opensandbox --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f examples/aks-kata/batchsandbox-template-configmap.yaml
helm upgrade --install opensandbox-controller ./kubernetes/charts/opensandbox-controller \
--namespace opensandbox-system \
-f examples/aks-kata/controller-values.yaml
helm upgrade --install opensandbox-server ./kubernetes/charts/opensandbox-server \
--namespace opensandbox-system \
-f examples/aks-kata/server-values.yaml
```
Wait for the deployments:
```bash
kubectl rollout status deploy/opensandbox-controller-manager -n opensandbox-system --timeout=180s
kubectl rollout status deploy/opensandbox-server -n opensandbox-system --timeout=180s
kubectl rollout status deploy/opensandbox-ingress-gateway -n opensandbox-system --timeout=180s
```
## 2. Start port-forwards
Open two separate terminals:
```bash
# Terminal 1 — lifecycle server
kubectl port-forward -n opensandbox-system svc/opensandbox-server 18080:80
# Terminal 2 — ingress gateway
kubectl port-forward -n opensandbox-system svc/opensandbox-ingress-gateway 28080:80
```
## 3. Set environment variables
```bash
export SANDBOX_DOMAIN=http://127.0.0.1:18080
export SANDBOX_API_KEY=aks-kata-demo-key
export AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
export AZURE_OPENAI_API_KEY=your-real-key
# Optional:
# export AZURE_OPENAI_DEPLOYMENT=gpt-4o-mini
```
## 4. Enable Pause/Resume (Optional)
Pause commits the sandbox rootfs as an OCI image and pushes it to a registry. Resume recreates the sandbox from that image. Without a registry, the `pause` and `resume` steps will log an error and continue.
The steps below use Azure Container Registry (ACR). Any OCI registry works — see the [Pause / Resume guide](/guides/pause-resume) for the full guide.
### Step 1: Grant AcrPush to the kubelet identity
```bash
KUBELET_ID=$(az aks show \
-g <resource-group> -n <cluster-name> \
--query "identityProfile.kubeletidentity.clientId" -o tsv)
ACR_ID=$(az acr show --name <acr-name> --query id -o tsv)
az role assignment create --assignee "$KUBELET_ID" --role AcrPush --scope "$ACR_ID"
```
### Step 2: Create the push/pull secret
```bash
ACR_PASSWORD=$(az acr credential show --name <acr-name> \
--query "passwords[0].value" -o tsv)
kubectl create secret docker-registry acr-snapshot-push-secret \
--docker-server=<acr-name>.azurecr.io \
--docker-username=<acr-name> \
--docker-password="$ACR_PASSWORD" \
--namespace=opensandbox
```
### Step 3: Upgrade the controller
```bash
helm upgrade opensandbox-controller ./kubernetes/charts/opensandbox-controller \
--namespace opensandbox-system \
--reuse-values \
--set controller.snapshot.registry=<acr-name>.azurecr.io/opensandbox-snapshots \
--set controller.snapshot.snapshotPushSecret=acr-snapshot-push-secret \
--set controller.snapshot.resumePullSecret=acr-snapshot-push-secret
kubectl rollout status deploy/opensandbox-controller-manager \
-n opensandbox-system --timeout=90s
```
::: tip
This example clears `controller.snapshot.containerdSocketPath` because the pinned controller image (`controller:v0.2.0`) does not accept the `--containerd-socket-path` flag. Current controller builds **do** accept it (see [`kubernetes/cmd/controller/main.go`](https://github.com/opensandbox-group/OpenSandbox/blob/main/kubernetes/cmd/controller/main.go)); if your nodes use a non-default containerd socket and you deploy a controller image that supports the flag, set this value accordingly.
:::
## 5. Use `main.py`
All `main.py` commands below are run from the example directory:
```bash
cd examples/aks-kata
```
### Run everything end to end
```bash
python3 main.py all
```
This creates a sandbox, runs every demo step, and deletes it at the end.
### Run one step at a time
#### Create a sandbox
```bash
python3 main.py create
```
Expected output:
```
Creating Kata-isolated sandbox on AKS...
OpenSandbox API: http://127.0.0.1:18080
Sandbox image: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.1.0
SANDBOX_ID=96045ee2-6614-435c-9fa8-d4b6f9592598
Use --sandbox-id 96045ee2-6614-435c-9fa8-d4b6f9592598 for subsequent steps.
```
Save the ID for the following steps:
```bash
export SANDBOX_ID=96045ee2-6614-435c-9fa8-d4b6f9592598
```
Verify the pod is running on a Kata node:
```bash
kubectl get pod -n opensandbox -o wide
# NODE column should show aks-sandboxagent-*
kubectl get pod -n opensandbox -o jsonpath='{.items[0].spec.runtimeClassName}'
# Expected: kata-vm-isolation
python3 main.py exec --sandbox-id $SANDBOX_ID -c "uname -r"
# Expected: 6.6.137.mshv1-1.azl3 (mshv = Microsoft Hypervisor = Kata VM guest kernel)
```
#### Set up Credential Vault
```bash
python3 main.py credentials --sandbox-id $SANDBOX_ID
```
Expected output:
```
[credentials] Credential Vault configured.
```
#### Ask the LLM a question
```bash
python3 main.py llm --sandbox-id $SANDBOX_ID -q "What is the capital of France? Reply in one word."
```
Expected output:
```
[llm] Question: What is the capital of France? Reply in one word.
[llm] Model: gpt-4o-mini
[llm] Answer: Paris
```
The sandbox only has a fake API key — the real key is injected by the egress sidecar via Credential Vault. You can verify:
```bash
python3 main.py exec --sandbox-id $SANDBOX_ID -c "echo \$AZURE_OPENAI_API_KEY"
# Expected: fake-key-inside-sandbox
```
#### Run commands inside the sandbox
```bash
python3 main.py exec --sandbox-id $SANDBOX_ID -c "uname -a"
# [exec][stdout] Linux ...-0 6.6.137.mshv1-1.azl3 ... x86_64 GNU/Linux
python3 main.py exec --sandbox-id $SANDBOX_ID -c "cat /etc/os-release | head -3"
# [exec][stdout] PRETTY_NAME="Ubuntu 24.04.4 LTS"
# [exec][stdout] NAME="Ubuntu"
# [exec][stdout] VERSION_ID="24.04"
python3 main.py exec --sandbox-id $SANDBOX_ID -c "ls -la /tmp/www/"
# Shows the HTTP server root directory
```
#### Fetch from the sandbox HTTP server
The sandbox runs `python3 -m http.server 8080` serving `/tmp/www/`. Traffic goes through the ingress gateway with secure-access headers.
```bash
# Write a file into the sandbox, then fetch it via the gateway
python3 main.py exec --sandbox-id $SANDBOX_ID -c "echo hello > /tmp/www/greeting.txt"
python3 main.py http --sandbox-id $SANDBOX_ID -p /greeting.txt
# [http] GET /greeting.txt -> 200 (6 bytes)
# hello
# Directory listing
python3 main.py http --sandbox-id $SANDBOX_ID -p /
# Shows HTML directory listing
# 404 for missing file
python3 main.py http --sandbox-id $SANDBOX_ID -p /nonexistent.txt
# [http] GET /nonexistent.txt -> 404
```
#### Check sandbox status
```bash
python3 main.py status --sandbox-id $SANDBOX_ID
# Sandbox: 96045ee2-6614-435c-9fa8-d4b6f9592598
# State: Running
# Image: ...code-interpreter:v1.1.0
```
#### Pause and resume
Pause snapshots the container rootfs to the registry (15 min). Resume recreates the sandbox from that snapshot. Requires step 4 above.
```bash
python3 main.py pause --sandbox-id $SANDBOX_ID
# [lifecycle] pausing sandbox...
# [lifecycle] sandbox is PAUSED
python3 main.py status --sandbox-id $SANDBOX_ID
# State: Paused
# Pod is gone, but the BatchSandbox CR and snapshot remain
kubectl get pods -n opensandbox
# No resources found
kubectl get sandboxsnapshot -n opensandbox
# Shows snapshot with phase Succeed
python3 main.py resume --sandbox-id $SANDBOX_ID
# [lifecycle] resuming sandbox...
# [setup] sandbox state: Resuming, waiting...
# [lifecycle] state after resume: Running
# [http] GET /index.html -> 200 (filesystem preserved!)
python3 main.py status --sandbox-id $SANDBOX_ID
# State: Running
```
#### Delete the sandbox
```bash
python3 main.py delete --sandbox-id $SANDBOX_ID
# [lifecycle] sandbox 96045ee2-... deleted.
kubectl get pods -n opensandbox
# No resources found
```
Without `-c`, `-q`, or `-p`, the `exec`, `llm`, and `http` steps run their built-in demo sequences.
## What gets installed
| Component | Namespace | Purpose |
|-----------|-----------|---------|
| `opensandbox-controller` | `opensandbox-system` | Kubernetes operator — manages BatchSandbox CRDs, pools, and snapshots |
| `opensandbox-server` | `opensandbox-system` | Lifecycle API — creates/deletes sandboxes, proxies SDK calls |
| `opensandbox-ingress-gateway` | `opensandbox-system` | Routes external HTTP traffic into sandbox pods with auth |
| BatchSandbox template | `opensandbox-system` | Pins sandbox pods onto Kata-capable AKS nodes via `nodeSelector` |
The server is configured with:
- `workload_provider = "batchsandbox"`
- `secure_runtime.type = "kata"` / `k8s_runtime_class = "kata-vm-isolation"`
- egress sidecar (`dns+nft` mode)
- ingress gateway (`header` routing with `secureAccess`)
## Security Model
::: warning
`server-values.yaml` ships with **demo-only** credentials: `api_key =
"aks-kata-demo-key"` and a fixed base64 `secureAccess` signing key. They exist so
the local port-forward walkthrough works without extra setup. Replace both with
unique secret values (for example a random key and
`openssl rand -base64 32` for the signing key) before exposing the server or
gateway to anything other than `127.0.0.1`.
:::
### Kata VM isolation
Each sandbox runs inside a dedicated Kata VM (`runtimeClassName: kata-vm-isolation`) on an AKS Kata node pool (`nodeSelector: kubernetes.azure.com/kata-vm-isolation: "true"`). The sandbox sees its own guest kernel, not the host kernel.
### Egress isolation
The create request sets a deny-by-default outbound policy. Only explicitly allowed hosts (Azure OpenAI, pypi.org, files.pythonhosted.org) can be reached.
### Credential Vault
The sandbox only sees a fake `AZURE_OPENAI_API_KEY`. The real key is stored in Credential Vault and injected by the egress sidecar as the `api-key` header only for matching outbound requests to `https://<your-resource>.openai.azure.com/openai/*`.
### Ingress gateway + secureAccess
All external traffic to sandbox services flows through the ingress gateway. The gateway validates the `OpenSandbox-Secure-Access` token and routes via the `OpenSandbox-Ingress-To` header. Both are returned by the SDK's `get_endpoint()` call — callers never need to construct them manually.
### Per-service auth
Even if someone bypasses the gateway and reaches the pod directly:
- **execd** (port 44772) requires its own access token header
- **egress sidecar** (port 18080) requires the `OPENSANDBOX-EGRESS-AUTH` header
- Only the user's HTTP server (port 8080) has no built-in auth — that's what secureAccess protects
## Cleanup
Remove everything installed by this example:
```bash
# 1) Delete any running sandboxes
kubectl delete batchsandbox --all -n opensandbox
# 2) Uninstall Helm releases
helm uninstall opensandbox-server -n opensandbox-system
helm uninstall opensandbox-controller -n opensandbox-system
# 3) Remove the BatchSandbox template
kubectl delete configmap aks-kata-batchsandbox-template -n opensandbox-system
# 4) Remove the snapshot push/pull secret (if created)
kubectl delete secret acr-snapshot-push-secret -n opensandbox --ignore-not-found
# 5) Delete the namespaces
kubectl delete namespace opensandbox
kubectl delete namespace opensandbox-system
```