Who owns what
- The daemon owns the VMs.
k7druns as a long-lived systemd service and owns every VM’s KVM file descriptors, guest memory, vsock, virtiofsd children, bridges, and snapshot trees. Clients — the containerd shim, the k7 Python backend, your RL trainer — talk to it over a Unix socket, one JSON object per line. See Daemon API. - The shim is a thin client. containerd spawns one
containerd-shim-k7-v1per pod; its sandboxCreatecalls the daemon over/run/k7d/k7d.sockand fails loudly if the daemon is not running. Because VMs outlive shims, a respawned shim reattaches to its still-running VM (reattach_sandbox). - The agent is not a Kubernetes component.
k7-agentis a tiny static binary baked into the guest initramfs, running as PID 1. It exposes a JSON-line RPC over vsock (commands likeping,exec,read_file,write_file,container_start,container_exec,container_stop,container_output). Clients reach the agent directly over vsock using theguest_cidthe daemon returns — the daemon does not proxy the agent protocol. runcruns inside the guest, never on the host. Container bundles reach the VM via erofs images on virtio-blk (or virtiofs as a read-write fallback); the agent invokesruncagainst them.
Design choices worth knowing
- virtio-MMIO only, no PCI. Each virtio device is a 4 KiB MMIO window above guest RAM, discovered from the kernel cmdline. PCI bus emulation would add ~2k LoC for nothing k7d needs. Device set: virtio-net (userspace TAP pump), vhost-vsock (kernel) for the agent, virtiofs (vhost-user) for shared directories, virtio-blk for disk images, plus two legacy serial ports for console and agent fallback.
- Guest memory is one mmap. Either memfd-backed (fresh VM) or
MAP_PRIVATEover a snapshot file (restored/forked VM) — the foundation of CoW fork. - No async in the VMM. Plain threads +
Arc<Mutex<...>>; only the shim is tokio-based (containerd’s shim protocol is async). Every cross-thread signal is an eventfd. - Errors are loud. Typed errors per module, no silent fallbacks, no
unwrap()in library code. Resources (KVM fds, TAP devices, virtiofsd children) are cleaned up viaDrop— drop order is deliberate so device worker threads never touch freed guest memory. - Rootfs via erofs on virtio-blk. Read-only host paths are packed into cached erofs images and attached as block devices. This is what keeps cluster forks CoW: no per-fork memory copies for filesystem shares (the pre-14a virtiofs path forced a full memory copy per fork and survives only as the fallback for read-write hostPath).
The “start a pod” path
kubectl apply (with runtimeClassName: k7) → kubelet → containerd → exec containerd-shim-k7-v1 → shim create_vm over the daemon socket (daemon builds the VM: kernel, initramfs, vsock, network, block devices; ~163 ms to agent-ready, or served warm from the pool) → shim tells the agent to container_start the OCI bundle → agent runs runc → an output bridge polls container_output and writes into containerd’s FIFOs — which is how kubectl logs works.
Setup steps for the RuntimeClass: RuntimeClass k7.