feat(firecracker): persistent registry volume for the infra VM (Stage B, 6/n)
The infra VM's rootfs is ephemeral (rebuilt each boot), so the bottle registry DB needs durable storage across restarts. Give the infra VM a firecracker analogue of a docker volume: a host-side ext4 file attached as a second virtio-block device (guest /dev/vdb), mounted at the control plane's DB dir (/var/lib/bot-bottle, where host_db_path lives at db/bot-bottle.db). - firecracker_vm.boot/_config take an optional `data_drive` (a non-root, RW second drive). - infra_vm creates the volume on first use (`mke2fs` an empty ext4 at <fc-cache>/infra/registry.ext4) and mounts /dev/vdb in the PID-1 init before the control plane starts. It's a plain ext4 file, so `sudo mount -o loop <path>` (VM stopped) inspects bot-bottle.db directly. Verified on a KVM host: register a bottle, restart the infra VM (fresh rootfs, same volume) — /dev/vdb re-mounts and the registry `db/` dir + a marker file survive the restart. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
@@ -165,6 +165,7 @@ def boot() -> InfraVm:
|
||||
name="bot-bottle-infra", rootfs=rootfs, tap=slot.iface,
|
||||
guest_ip=slot.guest_ip, host_ip=slot.host_ip, pubkey=pubkey,
|
||||
run_dir=run_dir, mem_mib=4096, detached=True,
|
||||
data_drive=_ensure_registry_volume(),
|
||||
)
|
||||
_pid_file().write_text(str(vm.process.pid))
|
||||
return InfraVm(guest_ip=slot.guest_ip, private_key=private_key, vm=vm)
|
||||
@@ -180,6 +181,35 @@ def _pid_file() -> Path:
|
||||
return _infra_dir() / "vm.pid"
|
||||
|
||||
|
||||
# The registry "volume": a host-side ext4 file attached to the infra VM as a
|
||||
# second virtio-block device (guest /dev/vdb), mounted at the control plane's
|
||||
# DB dir. It outlives the ephemeral rootfs, so the bottle registry survives an
|
||||
# infra-VM restart — the firecracker analogue of a docker volume. It is a
|
||||
# plain ext4 file: `sudo mount -o loop <path>` on the host (with the VM
|
||||
# stopped) to inspect bot-bottle.db directly.
|
||||
_REGISTRY_SIZE = "512M"
|
||||
|
||||
|
||||
def registry_volume_path() -> Path:
|
||||
return _infra_dir() / "registry.ext4"
|
||||
|
||||
|
||||
def _ensure_registry_volume() -> Path:
|
||||
"""Create the empty ext4 registry volume on first use; reuse it after."""
|
||||
vol = registry_volume_path()
|
||||
if vol.exists():
|
||||
return vol
|
||||
info(f"creating infra registry volume {vol} ({_REGISTRY_SIZE})")
|
||||
proc = subprocess.run(
|
||||
["mke2fs", "-q", "-t", "ext4", "-F", str(vol), _REGISTRY_SIZE],
|
||||
capture_output=True, text=True, check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
vol.unlink(missing_ok=True)
|
||||
die(f"creating registry volume failed: {proc.stderr.strip()}")
|
||||
return vol
|
||||
|
||||
|
||||
def _stable_keypair() -> tuple[Path, str]:
|
||||
"""The infra VM's SSH keypair — generated once and reused, so any later
|
||||
launcher can SSH in (fetch CA / provision) even though a different process
|
||||
@@ -321,6 +351,11 @@ if [ -n "$KEY" ]; then
|
||||
fi
|
||||
chown -R 0:0 /root 2>/dev/null || true
|
||||
mkdir -p /etc/dropbear /run /var/lib/bot-bottle
|
||||
|
||||
# Persistent registry volume (second virtio-block device, /dev/vdb) mounted
|
||||
# at the control plane's DB dir, so bot-bottle.db survives infra-VM restarts.
|
||||
mount -t ext4 /dev/vdb /var/lib/bot-bottle 2>/dev/null || true
|
||||
|
||||
/bb-dropbear -R -E -p 22 &
|
||||
|
||||
# Control plane. Source is baked at /app; the package is stdlib-only.
|
||||
|
||||
Reference in New Issue
Block a user