feat: initial Firecracker snapshot orchestrator (fc-orch)

A "poor man's" Firecracker VM orchestrator that boots a single golden VM,
snapshots it, then restores N clone VMs from that snapshot with minimal
per-clone overhead.

How it works:
- `init`   — downloads a Linux 6.1 kernel and builds a minimal Alpine 3.20
             rootfs (512 MiB ext4) with a basic init script
- `golden` — boots the golden VM, lets it settle, then pauses and snapshots
             it (vmstate + memory file); the golden VMM is then terminated
             since only the artifacts are needed
- `spawn N` — restores N clone VMs concurrently from the golden snapshot:
               * rootfs: filesystem-level COW copy via `cp --reflink` (falls
                 back to a plain copy if reflinks are not supported)
               * memory: shared golden `mem` file; Firecracker's MAP_PRIVATE
                 lets the kernel handle COW page-by-page at no up-front cost
               * vmstate: small file, cheap regular copy per clone
               * networking: per-clone TAP device (fctapN) bridged to fcbr0
                 with iptables MASQUERADE NAT on the default route interface
- `status`  — reads PID files and checks /proc to report alive/dead clones
- `kill`    — stops in-memory clones, kills any stragglers via PID files,
              and tears down all fctap* devices
- `cleanup` — kill + remove all state dirs and the bridge

All tunables (binary path, base dir, kernel/rootfs paths, vCPUs, memory,
bridge name/CIDR) are configurable via environment variables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 09:53:16 +02:00
commit 640bf5065a
8 changed files with 2061 additions and 0 deletions

83
main.go Normal file
View File

@@ -0,0 +1,83 @@
// fc-orchestrator — Poor man's Firecracker snapshot orchestrator in Go.
//
// Creates a golden VM, snapshots it, then spawns N clones that share the
// base memory file via Firecracker's MAP_PRIVATE (kernel-level COW).
// Rootfs gets a filesystem-level COW copy (reflink where supported).
//
// Usage:
//
// go build -o fc-orch .
// sudo ./fc-orch init
// sudo ./fc-orch golden
// sudo ./fc-orch spawn 10
// sudo ./fc-orch status
// sudo ./fc-orch kill
// sudo ./fc-orch cleanup
package main
import (
"fmt"
"os"
"github.com/you/fc-orchestrator/orchestrator"
)
func main() {
if len(os.Args) < 2 {
usage()
os.Exit(1)
}
orch := orchestrator.New(orchestrator.DefaultConfig())
switch os.Args[1] {
case "init":
fatal(orch.Init())
case "golden":
fatal(orch.Golden())
case "spawn":
n := 1
if len(os.Args) > 2 {
fmt.Sscanf(os.Args[2], "%d", &n)
}
fatal(orch.Spawn(n))
case "status":
orch.Status()
case "kill":
fatal(orch.Kill())
case "cleanup":
fatal(orch.Cleanup())
default:
usage()
os.Exit(1)
}
}
func usage() {
fmt.Fprintf(os.Stderr, `Usage: %s <command> [args]
Commands:
init Download kernel + create Alpine rootfs
golden Boot golden VM → pause → snapshot
spawn [N] Restore N clones from golden snapshot (default: 1)
status Show running clones
kill Kill all running VMs
cleanup Kill VMs + remove all state
Environment:
FC_BIN firecracker binary path (default: firecracker)
FC_BASE_DIR working directory (default: /tmp/fc-orch)
FC_KERNEL vmlinux path
FC_ROOTFS rootfs.ext4 path
FC_VCPUS vCPUs per VM (default: 1)
FC_MEM_MIB MiB per VM (default: 128)
FC_BRIDGE bridge name or "none" (default: fcbr0)
`, os.Args[0])
}
func fatal(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "fatal: %v\n", err)
os.Exit(1)
}
}