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:
72
orchestrator/network.go
Normal file
72
orchestrator/network.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package orchestrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// setupBridge creates the host bridge + enables NAT if it doesn't exist.
|
||||
func (o *Orchestrator) setupBridge() error {
|
||||
if o.cfg.Bridge == "none" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if bridge already exists
|
||||
if err := run("ip", "link", "show", o.cfg.Bridge); err == nil {
|
||||
return nil // already up
|
||||
}
|
||||
|
||||
if err := run("ip", "link", "add", o.cfg.Bridge, "type", "bridge"); err != nil {
|
||||
return fmt.Errorf("create bridge: %w", err)
|
||||
}
|
||||
if err := run("ip", "addr", "add", o.cfg.BridgeCIDR, "dev", o.cfg.Bridge); err != nil {
|
||||
return fmt.Errorf("add bridge addr: %w", err)
|
||||
}
|
||||
if err := run("ip", "link", "set", o.cfg.Bridge, "up"); err != nil {
|
||||
return fmt.Errorf("bring bridge up: %w", err)
|
||||
}
|
||||
|
||||
// find default route interface for NAT
|
||||
out, err := exec.Command("ip", "-4", "route", "show", "default").Output()
|
||||
if err == nil {
|
||||
fields := strings.Fields(string(out))
|
||||
for i, f := range fields {
|
||||
if f == "dev" && i+1 < len(fields) {
|
||||
iface := fields[i+1]
|
||||
_ = run("sysctl", "-qw", "net.ipv4.ip_forward=1")
|
||||
// idempotent: ignore error if rule exists
|
||||
_ = run("iptables", "-t", "nat", "-A", "POSTROUTING",
|
||||
"-o", iface, "-j", "MASQUERADE")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
o.log.Infof("bridge %s up on %s", o.cfg.Bridge, o.cfg.BridgeCIDR)
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTap creates a tap device and attaches it to the bridge.
|
||||
func (o *Orchestrator) createTap(name string) error {
|
||||
if err := run("ip", "tuntap", "add", "dev", name, "mode", "tap"); err != nil {
|
||||
return fmt.Errorf("create tap %s: %w", name, err)
|
||||
}
|
||||
if err := run("ip", "link", "set", name, "up"); err != nil {
|
||||
return fmt.Errorf("bring tap %s up: %w", name, err)
|
||||
}
|
||||
if o.cfg.Bridge != "none" {
|
||||
if err := run("ip", "link", "set", name, "master", o.cfg.Bridge); err != nil {
|
||||
return fmt.Errorf("attach tap %s to bridge: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func destroyTap(name string) {
|
||||
_ = run("ip", "link", "del", name)
|
||||
}
|
||||
|
||||
// run executes a command, returning an error if it fails.
|
||||
func run(name string, args ...string) error {
|
||||
return exec.Command(name, args...).Run()
|
||||
}
|
||||
Reference in New Issue
Block a user