1614172423
First slice of Stage B: the orchestrator control plane runs as a
persistent Firecracker infra VM instead of a Docker container. The host
CLI reaches it over HTTP at the orchestrator link's guest IP; agent VMs
will reach its gateway ports (added next) over VM-to-VM routing.
- Dockerfile.orchestrator bakes the stdlib-only control-plane source
(COPY bot_bottle) so the image is self-contained and runs from a built
image with no runtime bind-mount — a guest VM can't bind-mount host
source. (Build-from-source stays the default; a pull-from-registry mode
lands later. The docker backend's dev bind-mount still overlays this.)
- util.build_base_rootfs_dir / inject_guest_boot take a `variant` +
`init_script`, so the same orchestrator image is prepared two ways
without a cache collision: the builder VM keeps the SSH-only agent init;
the infra VM gets a control-plane PID-1 init.
- new firecracker/infra_vm.py: boot the infra VM on the orchestrator link,
run `python -m bot_bottle.orchestrator` as PID 1, and poll /health.
Verified on a KVM host: infra VM boots, control plane answers
`GET /health -> 200 {"status":"ok"}` from the host over the TAP link.
Next: fold gateway_init (egress/git-gate/supervise) into the same VM,
then agent->gateway routing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
63 lines
3.1 KiB
Docker
63 lines
3.1 KiB
Docker
# Orchestrator control-plane image (PRD 0070, #384) + in-VM agent-image
|
|
# builder (PRD 0069 Stage 3).
|
|
#
|
|
# The per-host orchestrator runs `python3 -m bot_bottle.orchestrator`.
|
|
# The `bot_bottle` package is **stdlib-only** by design, so the control
|
|
# plane itself needs nothing but a Python runtime — none of the
|
|
# gateway's mitmproxy / git / gitleaks payload (that is the separate
|
|
# `bot-bottle-gateway` image, Dockerfile.gateway). Splitting them keeps
|
|
# the secret-dense control plane (it concentrates every bottle's egress
|
|
# tokens — see PRD 0070's "secret concentration") on a minimal
|
|
# dependency surface.
|
|
#
|
|
# The repo is bind-mounted read-only into the container at run time (see
|
|
# `orchestrator/lifecycle.py`), so the source is NOT copied in here: the
|
|
# image is just the runtime. `ensure_running` recreates the container
|
|
# only when the bind-mounted source hash changes (#381), which is why
|
|
# the code stays a mount rather than a baked layer.
|
|
|
|
FROM python:3.12-slim
|
|
|
|
# --- in-VM agent-image builder (PRD 0069 Stage 3) -------------------
|
|
# The Firecracker backend removes the host Docker daemon by building
|
|
# users' agent Dockerfiles *inside the orchestrator VM* with buildah
|
|
# (rootless, daemonless) instead of on the host. The host then needs no
|
|
# Docker daemon and no root-equivalent `docker` group; an untrusted
|
|
# Dockerfile builds inside the confined orchestrator VM, never on the
|
|
# host — strictly more isolated than host `docker build`.
|
|
#
|
|
# `git` lets buildah resolve git-context builds and lets the backend
|
|
# clone/pull; `ca-certificates` is needed for `FROM` pulls over TLS.
|
|
# buildah's runtime helpers (stripped by --no-install-recommends, so
|
|
# listed explicitly): `crun` is the OCI runtime that executes build
|
|
# steps; `netavark` + `aardvark-dns` are the network backend buildah
|
|
# uses to give `FROM` pulls and `RUN` steps network access.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
buildah crun netavark aardvark-dns git ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# vfs storage + chroot isolation: buildah then needs neither
|
|
# fuse-overlayfs / an overlay kernel module nor configured subuid maps
|
|
# (newuidmap/newgidmap), so it works unconditionally as root in a
|
|
# minimal microVM rootfs. vfs copies layers rather than stacking them —
|
|
# slower and heavier than overlay; a build-cache optimization (overlay
|
|
# where available, a persistent cache disk) is tracked for later.
|
|
ENV STORAGE_DRIVER=vfs \
|
|
BUILDAH_ISOLATION=chroot
|
|
|
|
# No third-party *Python* deps — the control plane stays stdlib only.
|
|
WORKDIR /app
|
|
|
|
# Bake the (stdlib-only) control-plane source so the image is
|
|
# self-contained — it runs from a built image, no runtime bind-mount.
|
|
# This is what the Firecracker infra VM boots (a guest can't bind-mount
|
|
# host source); the docker backend may still bind-mount /app for dev
|
|
# live-reload, which simply overlays this baked copy. `.dockerignore`
|
|
# keeps .git/docs/*.md out of the context.
|
|
COPY bot_bottle /app/bot_bottle
|
|
|
|
# Documentation only; lifecycle.py overrides the entrypoint to
|
|
# `python3 -m bot_bottle.orchestrator` with the runtime flags.
|
|
ENTRYPOINT ["python3", "-m", "bot_bottle.orchestrator"]
|