-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat: add Kubernetes testing infrastructure #227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
7eec998
feat: add Kubernetes testing infrastructure
rwipfelnv 230409f
fix: add socat proxy for K8s DNS isolation workaround
rwipfelnv 88511ce
feat: NemoKlaw - NemoClaw on Kubernetes with Dynamo support
rwipfelnv 502a418
fix: rename NemoKlaw to NemoClaw and document known limitations
rwipfelnv 2441905
fix: update DYNAMO_HOST to vllm-agg-frontend
rwipfelnv ca5b5a1
docs: add Using NemoClaw section with CLI commands
rwipfelnv 412e6aa
Merge branch 'main' into rwipfelnv/k8s-testing
cv 01f944a
refactor(k8s): simplify deployment to use official installer
rwipfelnv fcf85df
Merge branch 'main' into rwipfelnv/k8s-testing
kjw3 ac40a12
fix(k8s): resolve lint errors in yaml and markdown
rwipfelnv 7c2b970
docs(k8s): add experimental warning and clarify requirements
rwipfelnv 0de0910
Merge branch 'main' into rwipfelnv/k8s-testing
kjw3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,292 @@ | ||
| # NemoKlaw | ||
|
|
||
| **NemoClaw on Kubernetes** — A safe, scalable sandbox for learning and experimenting with AI agents. | ||
|
|
||
| ``` | ||
| _ _ _ ___ | ||
| | \ | | ___ _ __ ___ ___ | |/ / | __ ___ __ | ||
| | \| |/ _ \ '_ ` _ \ / _ \| ' /| |/ _` \ \ /\ / / | ||
| | |\ | __/ | | | | | (_) | . \| | (_| |\ V V / | ||
| |_| \_|\___|_| |_| |_|\___/|_|\_\_|\__,_| \_/\_/ | ||
| ``` | ||
|
|
||
| Run [NemoClaw](https://github.com/NVIDIA/NemoClaw) on Kubernetes with GPU inference powered by [Dynamo](https://github.com/ai-dynamo/dynamo). Perfect for teams who want a shared, secure environment to explore autonomous AI agents without local GPU requirements. | ||
|
|
||
| --- | ||
|
|
||
| ## Why NemoKlaw? | ||
|
|
||
| | Challenge | NemoKlaw Solution | | ||
| |-----------|-------------------| | ||
| | "I don't have a GPU" | Connect to shared Dynamo vLLM clusters on K8s | | ||
| | "AI agents are unpredictable" | Every agent runs in an isolated sandbox with network policies | | ||
| | "Setup is complicated" | One command unattended install with environment variables | | ||
| | "I want to learn safely" | Sandboxed execution prevents agents from affecting your system | | ||
|
|
||
| --- | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - Kubernetes cluster with `kubectl` access | ||
| - A Dynamo vLLM endpoint (or any OpenAI-compatible inference API) | ||
| - Namespace with permissions to create privileged pods | ||
|
|
||
| ### 1. Deploy NemoKlaw | ||
|
|
||
| ```bash | ||
| # Set your Dynamo endpoint | ||
| export DYNAMO_ENDPOINT="http://vllm-frontend.dynamo.svc.cluster.local:8000/v1" | ||
| export DYNAMO_MODEL="meta-llama/Llama-3.1-8B-Instruct" | ||
|
|
||
| # Deploy (creates namespace 'nemoklaw' if needed) | ||
| kubectl apply -f https://raw.githubusercontent.com/NVIDIA/NemoClaw/main/k8s-testing/nemoklaw.yaml | ||
| ``` | ||
|
|
||
| ### 2. Run the Installer | ||
|
|
||
| ```bash | ||
| # Wait for pod to be ready | ||
| kubectl wait --for=condition=Ready pod/nemoklaw -n nemoklaw --timeout=120s | ||
|
|
||
| # Run unattended install | ||
| kubectl exec -it nemoklaw -n nemoklaw -c workspace -- bash -c ' | ||
| export NEMOCLAW_NON_INTERACTIVE=1 | ||
| export NEMOCLAW_PROVIDER=dynamo | ||
| export NEMOCLAW_DYNAMO_ENDPOINT="http://vllm-frontend.dynamo.svc.cluster.local:8000/v1" | ||
| export NEMOCLAW_DYNAMO_MODEL="meta-llama/Llama-3.1-8B-Instruct" | ||
| curl -fsSL https://nvidia.com/nemoclaw.sh | bash | ||
| ' | ||
| ``` | ||
|
|
||
| ### 3. Connect to Your Sandbox | ||
|
|
||
| ```bash | ||
| kubectl exec -it nemoklaw -n nemoklaw -c workspace -- nemoclaw my-assistant connect | ||
| ``` | ||
|
|
||
| You're now inside a secure sandbox with an AI agent ready to help! | ||
|
|
||
| --- | ||
|
|
||
| ## What Can You Do? | ||
|
|
||
| ### Chat with the AI Agent | ||
|
|
||
| ```bash | ||
| # Inside the sandbox | ||
| sandbox@my-assistant:~$ openclaw tui | ||
| ``` | ||
|
|
||
| This opens an interactive chat interface. Ask the agent to: | ||
| - Write and run code | ||
| - Explore files and directories | ||
| - Install packages and run tests | ||
| - Anything you'd do in a terminal — safely sandboxed | ||
|
|
||
| ### Run Single Commands | ||
|
|
||
| ```bash | ||
| sandbox@my-assistant:~$ openclaw agent -m "List all Python files and summarize what each one does" | ||
| ``` | ||
|
|
||
| ### Test Inference Directly | ||
|
|
||
| ```bash | ||
| # Verify inference.local routing works | ||
| sandbox@my-assistant:~$ curl -s https://inference.local/v1/chat/completions \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"model":"meta-llama/Llama-3.1-8B-Instruct","messages":[{"role":"user","content":"Hello!"}]}' | jq . | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| ┌─────────────────────────────────────────────────────────────────────────────┐ | ||
| │ Kubernetes Cluster │ | ||
| │ │ | ||
| │ ┌─────────────────────────────────────────────────────────────────────────┐│ | ||
| │ │ NemoKlaw Pod ││ | ||
| │ │ ││ | ||
| │ │ ┌─────────────────────┐ ┌─────────────────────────────────────────┐││ | ||
| │ │ │ Docker-in-Docker │ │ Workspace Container │││ | ||
| │ │ │ │ │ │││ | ||
| │ │ │ ┌───────────────┐ │ │ nemoclaw CLI ┌───────────────────┐ │││ | ||
| │ │ │ │ k3s │ │◄───│ openshell CLI │ socat proxy │ │││ | ||
| │ │ │ │ cluster │ │ │ │ localhost:8000 │ │││ | ||
| │ │ │ │ │ │ │ └─────────┬─────────┘ │││ | ||
| │ │ │ │ ┌───────────┐ │ │ │ │ │││ | ||
| │ │ │ │ │ Sandbox │ │ │ │ inference.local ──────────┘ │││ | ||
| │ │ │ │ │ Pods │ │ │ │ (via host.openshell.internal) │││ | ||
| │ │ │ │ └───────────┘ │ │ │ │││ | ||
| │ │ │ └───────────────┘ │ └─────────────────────────────────────────┘││ | ||
| │ │ └─────────────────────┘ ││ | ||
| │ └────────────────────────────────────────────────────────────────────────┘│ | ||
| │ │ │ | ||
| │ ▼ │ | ||
| │ ┌─────────────────────────────────────────────────────────────────────────┐│ | ||
| │ │ Dynamo vLLM Service ││ | ||
| │ │ ││ | ||
| │ │ vllm-frontend.dynamo.svc.cluster.local:8000 ││ | ||
| │ │ └── meta-llama/Llama-3.1-8B-Instruct (or your model) ││ | ||
| │ │ └── Scales across multiple GPUs ││ | ||
| │ └─────────────────────────────────────────────────────────────────────────┘│ | ||
| └─────────────────────────────────────────────────────────────────────────────┘ | ||
| ``` | ||
|
|
||
| **How it works:** | ||
| 1. NemoKlaw runs in a privileged pod with Docker-in-Docker (DinD) | ||
| 2. OpenShell creates a nested k3s cluster for sandbox isolation | ||
| 3. AI agents run inside sandboxes with network policies | ||
| 4. Inference requests route through `inference.local` to your Dynamo endpoint | ||
| 5. A socat proxy bridges the K8s network to the nested k3s environment | ||
|
|
||
| --- | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Environment Variables | ||
|
|
||
| | Variable | Required | Default | Description | | ||
| |----------|----------|---------|-------------| | ||
| | `NEMOCLAW_NON_INTERACTIVE` | Yes | - | Set to `1` for unattended install | | ||
| | `NEMOCLAW_PROVIDER` | Yes | - | Set to `dynamo` for K8s deployments | | ||
| | `NEMOCLAW_DYNAMO_ENDPOINT` | Yes | - | Full URL to vLLM API (e.g., `http://....:8000/v1`) | | ||
| | `NEMOCLAW_DYNAMO_MODEL` | No | `dynamo` | Model name to use | | ||
| | `NEMOCLAW_SANDBOX_NAME` | No | `my-assistant` | Name for your sandbox | | ||
| | `NEMOCLAW_POLICY_MODE` | No | - | Set to `skip` to skip policy setup | | ||
|
|
||
| ### Custom Dynamo Endpoint | ||
|
|
||
| ```bash | ||
| # Point to your own vLLM deployment | ||
| export NEMOCLAW_DYNAMO_ENDPOINT="http://my-vllm.my-namespace.svc.cluster.local:8000/v1" | ||
| export NEMOCLAW_DYNAMO_MODEL="mistralai/Mistral-7B-Instruct-v0.3" | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## For Teams: Multi-User Setup | ||
|
|
||
| NemoKlaw is perfect for workshops, training sessions, or team experimentation. Each user gets their own isolated sandbox. | ||
|
|
||
| ### Deploy One Pod Per User | ||
|
|
||
| ```bash | ||
| # Create user-specific pods | ||
| for user in alice bob carol; do | ||
| cat <<EOF | kubectl apply -f - | ||
| apiVersion: v1 | ||
| kind: Pod | ||
| metadata: | ||
| name: nemoklaw-${user} | ||
| namespace: nemoklaw | ||
| labels: | ||
| app: nemoklaw | ||
| user: ${user} | ||
| spec: | ||
| # ... (use nemoklaw.yaml as template) | ||
| EOF | ||
| done | ||
| ``` | ||
|
|
||
| ### Shared Dynamo Backend | ||
|
|
||
| All users share the same GPU-powered inference backend: | ||
| - Cost-effective: One Dynamo deployment serves many users | ||
| - Consistent: Everyone uses the same model version | ||
| - Scalable: Dynamo auto-scales based on demand | ||
|
|
||
| --- | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Pod won't start | ||
|
|
||
| ```bash | ||
| # Check pod status | ||
| kubectl describe pod nemoklaw -n nemoklaw | ||
|
|
||
| # Common issues: | ||
| # - Missing privileged security context (DinD requires it) | ||
| # - Insufficient memory (needs ~8GB for DinD container) | ||
| ``` | ||
|
|
||
| ### Docker daemon not starting | ||
|
|
||
| ```bash | ||
| # Check DinD container logs | ||
| kubectl logs nemoklaw -n nemoklaw -c dind | ||
|
|
||
| # Usually resolves by waiting 30-60 seconds after pod starts | ||
| ``` | ||
|
|
||
| ### Inference returns 502 Bad Gateway | ||
|
|
||
| The nested k3s cluster can't resolve Kubernetes DNS names. The socat proxy handles this automatically, but if you're setting up manually: | ||
|
|
||
| ```bash | ||
| # Start socat proxy in workspace container | ||
| kubectl exec nemoklaw -n nemoklaw -c workspace -- \ | ||
| socat TCP-LISTEN:8000,fork,reuseaddr TCP:your-vllm.namespace.svc:8000 & | ||
|
|
||
| # Configure provider to use host.openshell.internal | ||
| kubectl exec nemoklaw -n nemoklaw -c workspace -- \ | ||
| openshell provider create \ | ||
| --name dynamo \ | ||
| --type openai \ | ||
| --credential "OPENAI_API_KEY=dummy" \ | ||
| --config "OPENAI_BASE_URL=http://host.openshell.internal:8000/v1" | ||
| ``` | ||
|
|
||
| ### Can't connect to sandbox | ||
|
|
||
| ```bash | ||
| # List available sandboxes | ||
| kubectl exec nemoklaw -n nemoklaw -c workspace -- openshell sandbox list | ||
|
|
||
| # Check sandbox status | ||
| kubectl exec nemoklaw -n nemoklaw -c workspace -- nemoclaw my-assistant status | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Learn More | ||
|
|
||
| - **[NemoClaw Documentation](https://docs.nvidia.com/nemoclaw)** — Full reference for NemoClaw | ||
| - **[OpenShell](https://github.com/NVIDIA/OpenShell)** — The sandbox runtime powering NemoKlaw | ||
| - **[Dynamo](https://github.com/ai-dynamo/dynamo)** — Distributed vLLM inference for K8s | ||
| - **[OpenClaw](https://openclaw.ai)** — The AI agent framework | ||
|
|
||
| --- | ||
|
|
||
| ## Contributing | ||
|
|
||
| Found an issue? Have an idea? We'd love your input! | ||
|
|
||
| - [Report a bug](https://github.com/NVIDIA/NemoClaw/issues/new) | ||
| - [Request a feature](https://github.com/NVIDIA/NemoClaw/issues/new) | ||
| - [Join the discussion](https://github.com/NVIDIA/NemoClaw/discussions) | ||
|
|
||
| --- | ||
|
|
||
| ## Requirements | ||
|
|
||
| - **Kubernetes**: 1.25+ | ||
| - **kubectl**: Configured with cluster access | ||
| - **Dynamo/vLLM**: Running and accessible from cluster | ||
| - **Resources**: | ||
| - DinD container: 8GB RAM, 2 CPU | ||
| - Workspace container: 2GB RAM, 1 CPU | ||
| - **Permissions**: Ability to create privileged pods | ||
|
|
||
| --- | ||
|
|
||
| <p align="center"> | ||
| <b>NemoKlaw</b> — Safe AI agent experimentation on Kubernetes | ||
| <br> | ||
| <sub>Built with NemoClaw + OpenShell + Dynamo</sub> | ||
| </p> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # NemoClaw Public Installer Test on K8s | ||
| # Tests the unattended install flow with Dynamo vLLM endpoint | ||
| # | ||
| # Prerequisites: | ||
| # - Dynamo vLLM serving a model (e.g., in robert namespace) | ||
| # - PR #318 merged (non-interactive mode) + PR #365 (Dynamo provider support) | ||
| # | ||
| # Usage: | ||
| # kubectl apply -f nemoclaw-installer-test.yaml | ||
| # kubectl logs -f nemoclaw-installer -n nemoclaw -c installer | ||
| # | ||
| apiVersion: v1 | ||
| kind: Pod | ||
| metadata: | ||
| name: nemoclaw-installer | ||
| namespace: nemoclaw | ||
| labels: | ||
| app: nemoclaw-installer | ||
| spec: | ||
| containers: | ||
| # Docker daemon (DinD) - OpenShell runs k3s inside Docker | ||
| - name: dind | ||
| image: docker:24-dind | ||
| securityContext: | ||
| privileged: true | ||
| volumeMounts: | ||
| - name: docker-storage | ||
| mountPath: /var/lib/docker | ||
| - name: docker-socket | ||
| mountPath: /var/run | ||
| - name: docker-config | ||
| mountPath: /etc/docker | ||
| env: | ||
| - name: DOCKER_TLS_CERTDIR | ||
| value: "" | ||
| command: | ||
| - dockerd | ||
| - --host=unix:///var/run/docker.sock | ||
| resources: | ||
| requests: | ||
| memory: "8Gi" | ||
| cpu: "2" | ||
|
|
||
| # Installer container - runs the public NemoClaw installer | ||
| - name: installer | ||
| image: ubuntu:22.04 | ||
| command: ["sleep", "infinity"] | ||
| workingDir: /root | ||
| env: | ||
| - name: DOCKER_HOST | ||
| value: unix:///var/run/docker.sock | ||
| # Unattended install configuration (PR #318) | ||
| - name: NEMOCLAW_NON_INTERACTIVE | ||
| value: "1" | ||
| - name: NEMOCLAW_SANDBOX_NAME | ||
| value: "nemoclaw-k8s" | ||
| # Dynamo provider configuration (PR #365) | ||
| - name: NEMOCLAW_PROVIDER | ||
| value: "dynamo" | ||
| - name: NEMOCLAW_DYNAMO_ENDPOINT | ||
| value: "http://vllm-agg-frontend.robert.svc.cluster.local:8000/v1" | ||
| - name: NEMOCLAW_DYNAMO_MODEL | ||
| value: "meta-llama/Llama-3.1-8B-Instruct" | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # Skip policies for faster testing | ||
| - name: NEMOCLAW_POLICY_MODE | ||
| value: "skip" | ||
| volumeMounts: | ||
| - name: docker-socket | ||
| mountPath: /var/run | ||
| - name: docker-config | ||
| mountPath: /etc/docker | ||
| resources: | ||
| requests: | ||
| memory: "4Gi" | ||
| cpu: "1" | ||
|
|
||
| initContainers: | ||
| # Configure Docker daemon for cgroup v2 compatibility | ||
| - name: init-docker-config | ||
| image: busybox | ||
| command: | ||
| - sh | ||
| - -c | ||
| - echo '{"default-cgroupns-mode":"host"}' > /etc/docker/daemon.json | ||
| volumeMounts: | ||
| - name: docker-config | ||
| mountPath: /etc/docker | ||
|
|
||
| volumes: | ||
| - name: docker-storage | ||
| emptyDir: {} | ||
| - name: docker-socket | ||
| emptyDir: {} | ||
| - name: docker-config | ||
| emptyDir: {} | ||
|
|
||
| restartPolicy: Never | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.