Run:ai Fake GPU Operator¶
Run Mokka and Run:ai's fake-gpu-operator (FGO) in one cluster, each serving the nodes it is better at.
Why pair them¶
FGO simulates GPUs at the Kubernetes API level. It advertises GPU resources to the scheduler without touching the driver stack, which makes it very fast and lets it back hundreds of KWOK virtual nodes.
Mokka simulates at the driver level. A real libnvidia-ml.so inside the pod
means nvidia-smi, DCGM and the GPU Operator validator all work.
Pairing them gives a mixed cluster: a handful of nodes with full driver fidelity, and a large fleet of cheap virtual ones.
| FGO alone | Mokka alone | Together | |
|---|---|---|---|
| Advertises GPUs to the scheduler | yes | yes | yes |
| KWOK virtual nodes | yes | no | yes |
| Real NVML inside pods | no | yes | on the Mokka nodes |
Real nvidia-smi output |
limited | yes | on the Mokka nodes |
| DCGM metrics | 3 synthetic | real | real on the Mokka nodes |
| GPU Operator validation | no | yes | on the Mokka nodes |
| Scale to 1000+ nodes | yes | no | yes |
This pairing is not exercised in CI
Mokka's side of the contract is verified — the ConfigMap shape is asserted
by unit tests and by the fgo end-to-end case. But the two projects have
never been run together in CI, and FGO is neither vendored nor pinned here.
Re-check FGO's loader before relying on any of this.
The discovery contract¶
This is the part that breaks silently, so it is worth understanding before you install anything.
FGO does not watch for profile ConfigMaps or select them by label. Its
loader does a direct Get by name and reads one data key. Three things must
match exactly, and each is fatal on its own:
| Field | Value | If it is wrong |
|---|---|---|
| Name | gpu-profile-<profile> |
NotFound |
| Data key | profile.yaml |
"missing key" |
| Namespace | FGO's own release namespace | NotFound — the loader has no cross-namespace fallback |
Because the names carry no release prefix, two Mokka releases in one namespace collide on these seven ConfigMaps. Enable the integration on one release per namespace.
Set FGO's builtinProfiles.enabled=false first
FGO ships its own profiles under the same seven names, owned by its own
Helm release. Helm 3 refuses to adopt resources owned by another release,
so whichever chart installs second fails with invalid ownership metadata.
The two profile sets are alternatives, not complements.
Prerequisites¶
- Docker and
Kind — this guide creates its own cluster
(
nvml-mock-fgo-demo) and never touches your current context. - Helm 3.8 or newer — the chart is served from an OCI registry.
- kubectl
- A clone of this repository, for the Kind topology file.
Takes about 10 minutes.
Step 1 — Create a cluster¶
The topology labels one worker integration and two scale. Without those
labels Helm installs successfully and produces no pods for the missing pool.
Step 2 — Install Mokka on the integration pool¶
The ConfigMaps carry FGO's namespace in their own metadata, so that namespace has to exist before Mokka writes them:
kubectl --context kind-nvml-mock-fgo-demo create namespace gpu-operator
helm install nvml-mock oci://ghcr.io/nvidia/k8s-test-infra/chart/nvml-mock \
--kube-context kind-nvml-mock-fgo-demo \
--set integrations.fakeGpuOperator.enabled=true \
--set integrations.fakeGpuOperator.targetNamespace=gpu-operator \
--set gpu.profile=h100 \
--set gpu.count=8 \
--set "nodeSelector.run\.ai/simulated-gpu-node-pool=integration" \
--wait --timeout 120s
integrations.fakeGpuOperator.enabled=true is what emits the profile
ConfigMaps in the shape FGO's loader reads. targetNamespace puts them where
it reads: without it they land in Mokka's release namespace, which FGO never
looks in.
Step 3 — Install FGO on the scale pool¶
helm upgrade --install gpu-operator \
oci://ghcr.io/run-ai/fake-gpu-operator/fake-gpu-operator \
--kube-context kind-nvml-mock-fgo-demo \
-n gpu-operator \
--set builtinProfiles.enabled=false \
--wait --timeout 120s -f - <<EOF
topology:
nodePools:
integration:
backend: mock
gpuCount: 8
gpuProfile: h100
scale:
backend: fake
gpuCount: 8
gpuProfile: h100
EOF
backend: mock hands the pool to Mokka; backend: fake keeps it on FGO's own
shim. builtinProfiles.enabled=false is what lets this install succeed at all
now that Mokka owns those seven ConfigMap names — see
the discovery contract.
Step 4 — Verify¶
CTX=kind-nvml-mock-fgo-demo
# Mokka runs only on the integration worker.
kubectl --context $CTX get pods -l app.kubernetes.io/name=nvml-mock -o wide
# The profile ConfigMaps exist, in the namespace FGO reads.
kubectl --context $CTX -n gpu-operator get cm -l fake-gpu-operator/gpu-profile=true
# A real nvidia-smi, on a node with no GPU.
kubectl --context $CTX exec ds/nvml-mock -- nvidia-smi
# FGO runs on the scale workers.
kubectl --context $CTX get pods -l app=fake-gpu-operator -o wide
You should end up with:
| Node | Pool | Backend | GPUs come from | Mokka DaemonSet |
|---|---|---|---|---|
| worker | integration |
mock |
Mokka | yes |
| worker | scale |
fake |
FGO | no |
When FGO does not pick up the profiles¶
Check the name and namespace, not the label — the label is not on FGO's load path:
A NotFound means one of three things: the ConfigMaps are still in Mokka's
namespace (set targetNamespace), the name is wrong, or the install hit the
ownership error above. If the ConfigMap exists, confirm the body is under
profile.yaml:
Restart FGO's controller after fixing any of these:
Custom labels on the ConfigMaps¶
profileLabels adds labels but cannot remove the contract ones, so an override
here cannot break discovery:
Clean up¶
Related¶
| To read about | See |
|---|---|
| Every chart value | Installation |
| What a profile defines | Configuration |
| Mokka on its own | Quick Start |