Virtual Kubelet provider that runs vcluster pods as real pods in a host Kubernetes cluster, typically using Kata Containers for isolation.
vnode-bridge implements the Virtual Kubelet PodLifecycleHandler and NodeProvider interfaces to register a virtual node inside a vcluster while offloading pod execution to a separate host cluster and namespace. Pods scheduled to the vnode in the vcluster are translated into real pods on the host cluster (with a configurable RuntimeClass) and their status is continuously synced back.
- Virtual Kubelet–based virtual node: Implements Virtual Kubelet provider interfaces so the vnode looks and behaves like a regular Kubernetes node from the vcluster’s perspective.
- vcluster → host bridging: Pods scheduled onto the vnode in the vcluster are created as real pods in a configurable namespace on a host Kubernetes cluster.
- Kata Containers support: Uses a configurable
--runtime-class(defaultkata) to run host pods with Kata Containers or anotherRuntimeClass. - Status synchronization: Periodic sync loop keeps vcluster pod status aligned with the actual status of host pods.
- Label-based observability: Adds clear labels to host pods and the vnode to trace workloads back to their originating vcluster pods and node.
- Container-friendly distribution: Ships with a multi-stage Dockerfile that builds a minimal, non-root distroless image.
At a high level, vnode-bridge consists of:
- CLI / daemon (
cmd/vnode): Parses configuration, creates Kubernetes clients for both the vcluster and the host cluster, constructs the provider and translator, registers the virtual node, and runs Virtual Kubelet controllers. - Provider (
internal/provider): Implements pod lifecycle and node provider logic using the host cluster client. It creates, updates, deletes, and lists pods in the host namespace and maintains a status cache reflected back into the vcluster. - Translator (
internal/translator): Converts vcluster pod specs into host-cluster-ready pods:- Moves pods into the configured host namespace.
- Applies the configured
RuntimeClassName. - Strips vcluster-specific service account fields, projected volumes, and API token mounts.
- Adds labels to map host pods back to their originating vcluster pods and vnode.
- Pod controller (
internal/provider/podcontroller.go): Watches vcluster pods whosespec.nodeNamematches the vnode name, and feeds events into the provider.
The result is a virtual node in the vcluster whose pods are actually executed in another Kubernetes cluster/namespace.
- Go toolchain: Go 1.25+ to build from source (module targets
go 1.25.7). - Kubernetes clusters:
- A vcluster control plane where the virtual node will be registered.
- A host Kubernetes cluster that will actually run the pods.
- A namespace in the host cluster where
vnode-bridgewill create pods. - A
RuntimeClassin the host cluster matching the value passed via--runtime-class(default:kata).
- Permissions / RBAC:
- In the vcluster: permissions to manage Nodes, Leases, Pods (list/watch), and Events in at least the
defaultnamespace. - In the host cluster: permissions to create, update, delete, and list/watch Pods in the configured host namespace.
- In the vcluster: permissions to manage Nodes, Leases, Pods (list/watch), and Events in at least the
Clone the repository:
git clone https://github.com/kroderdev/vnode-bridge.git
cd vnode-bridgeBuild the binary:
make buildThis will compile the vnode binary from cmd/vnode.
Run tests:
make testRun linters (if you have golangci-lint installed):
make lintTo build a container image using the provided multi-stage Dockerfile:
make dockerBy default this runs:
docker build -t kroderdev/vnode-bridge:dev .You can change the tag or push the resulting image to your own registry.
vnode-bridge is configured entirely via CLI flags. The most important flags are:
-
--node-name(required):
Name of the virtual node that will appear in the vcluster. -
--host-namespace(required):
Namespace in the host cluster where translated pods will be created. -
--cpu(default:1000m):
CPU capacity to advertise on the vnode. -
--memory(default:2Gi):
Memory capacity to advertise on the vnode. -
--runtime-class(default:kata):
RuntimeClassNamefor pods on the host cluster (for example, a Kata Containers runtime class). -
--kubeconfig-host:
Path to the kubeconfig file for the host cluster. If omitted,vnodewill try to use in-cluster configuration (suitable when running inside the host cluster). -
--kubeconfig-vcluster:
Path to the kubeconfig file for the vcluster.
-
Prepare kubeconfigs and clusters
- Ensure you have a working vcluster and a host Kubernetes cluster.
- Create or choose a namespace in the host cluster where
vnode-bridgewill create pods (for example,vcluster-workloads). - Make sure a
RuntimeClass(e.g.kata) is available in the host cluster.
-
Run
vnodeas a binaryvnode \ --node-name=vnode-01 \ --host-namespace=vcluster-workloads \ --cpu=2000m \ --memory=4Gi \ --runtime-class=kata \ --kubeconfig-host=/path/to/host.kubeconfig \ --kubeconfig-vcluster=/path/to/vcluster.kubeconfig
This process:
- Registers a virtual Node named
vnode-01in the vcluster. - Watches pods in the vcluster whose
spec.nodeNameisvnode-01. - Translates those pods into host cluster pods in
vcluster-workloads. - Periodically syncs host pod status back to the corresponding vcluster pods.
- Registers a virtual Node named
-
Run
vnodeas a containerAfter building and pushing an image (or using a prebuilt one), you can run it with Docker:
docker run --rm \
-v /path/to/host.kubeconfig:/host.kubeconfig:ro \
-v /path/to/vcluster.kubeconfig:/vcluster.kubeconfig:ro \
kroderdev/vnode-bridge:dev \
--node-name=vnode-01 \
--host-namespace=vcluster-workloads \
--kubeconfig-host=/host.kubeconfig \
--kubeconfig-vcluster=/vcluster.kubeconfigIn a real deployment, you would typically run vnode as a Deployment or DaemonSet in either the host cluster or the vcluster, mounting appropriate kubeconfigs or using in-cluster credentials.
vnode-bridge adds labels to host pods and the vnode to help correlate workloads and debug issues:
-
Host pods created by
vnode-bridgeinclude labels such as:app.kubernetes.io/managed-by = kroderdev-vnode-bridgevnode.kroderdev.io/node-name = <vnode name>vnode.kroderdev.io/vcluster-pod-name = <original vcluster pod name>vnode.kroderdev.io/vcluster-pod-namespace = <original vcluster pod namespace>
-
The virtual Node in the vcluster includes labels such as:
type = virtualkubernetes.io/role = agentnode.kubernetes.io/instance-type = vnodevnode.kroderdev.io/managed = "true"
This makes it easy to discover which pods are managed by vnode-bridge and trace them back to their originating vcluster pods.
-
Startup
- The
vnodeprocess parses CLI flags to build a configuration. - It creates Kubernetes clients for both the vcluster and the host cluster using the provided kubeconfigs (or in-cluster config).
- It constructs:
- A Translator that knows the host namespace, runtime class, and vnode name.
- A Provider that uses the host client and translator to manage host pods and maintain an in-memory pod cache.
- A virtual Node object representing the vnode in the vcluster, including capacity and labels.
- The
-
Virtual Kubelet controllers
- A node controller registers the vnode in the vcluster and keeps node status up to date.
- A pod controller in the vcluster watches pods assigned to the vnode and sends events (create, update, delete) to the Provider.
-
Pod lifecycle
- When a pod is scheduled onto the vnode in the vcluster:
- The Provider calls the Translator to produce a host pod spec.
- The host pod is created in the configured host namespace, with the configured
RuntimeClassNameand labels linking it back to the original vcluster pod.
- Updates to vcluster pods are propagated to host pods by re-translating relevant parts of the spec.
- Deletions in the vcluster trigger deletions in the host cluster; the Provider marks the vcluster pod as
Succeededwith reasonProviderTerminated.
- When a pod is scheduled onto the vnode in the vcluster:
-
Status sync
- A periodic sync loop lists host pods matching the vnode’s labels and updates the Provider’s cache.
- The pod controller then uses this cache to serve up-to-date status for pods in the vcluster.
Contributions, bug reports, and feature requests are welcome.
If you plan to contribute:
- Run
make testandmake lintbefore submitting changes. - Try to add or update tests alongside your code changes.
- Keep commit messages and pull request descriptions focused and clear.
This project is licensed under the terms of the license found in the LICENSE file.