docker rootfs defaults

This commit is contained in:
Thales Maciel 2026-02-05 02:13:14 -03:00
parent 5f3d60ef0f
commit 93c3d1a67b
6498 changed files with 64929 additions and 14 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ state/
*.squashfs*
squashfs-root/
rootfs*
wtf/*.deb

View file

@ -26,8 +26,9 @@ Minimal Firecracker launcher.
- `--name`: must be unique and match `[a-z0-9][a-z0-9-]{0,63}`.
- `--vcpu`: defaults to 2, max 16.
- `--ram`: MiB, defaults to 1024, max 32768.
- `--rootfs`: path to the base rootfs image (default: `./rootfs.ext4`).
- `--kernel`: path to the kernel image (default: `./vmlinux`).
- `--rootfs`: path to the rootfs image (default: `./rootfs-docker.ext4`).
- `--kernel`: path to the kernel image (default: `./wtf/root/boot/vmlinux-6.8.0-94-generic`).
- `--initrd`: path to the initrd image (default: `./wtf/root/boot/initrd.img-6.8.0-94-generic`).
- `--home-size`: M/G suffixes supported (default: 2G).
- `--var-size`: M/G suffixes supported (default: 2G).
@ -62,10 +63,45 @@ Check status with:
reboot
```
## Customize Rootfs (Docker + Kernel Modules)
Use `customize.sh` to build a writable rootfs with Docker and kernel modules
preloaded so Docker works out of the box.
```
./customize.sh rootfs-docker.ext4 --size 6G --docker
```
Options:
- `--size`: optional size for the output image.
- `--kernel`: kernel path (default: `./wtf/root/boot/vmlinux-6.8.0-94-generic`).
- `--initrd`: initrd path (default: `./wtf/root/boot/initrd.img-6.8.0-94-generic`).
- `--modules`: kernel modules directory (default: `./wtf/root/lib/modules/6.8.0-94-generic`).
- `--docker`: install Docker packages into the image.
After boot, enable NAT and validate Docker:
```
./nat.sh up <id-or-name-prefix>
ssh -i "./id_ed25519" root@<guest_ip> "systemctl enable --now docker"
ssh -i "./id_ed25519" root@<guest_ip> "docker run --rm hello-world"
```
## Build Rootfs On Demand
`run.sh` defaults to `./rootfs-docker.ext4`. If it is missing, `run.sh` will
invoke `make-rootfs.sh` to build it.
```
./make-rootfs.sh
```
`make-rootfs.sh` chooses the first available base image:
- `./rootfs.ext4`
- `./ubuntu-noble-rootfs/rootfs.ext4`
- `./ubuntu-lts/rootfs.ext4`
## VM Info File
Each VM writes `state/vms/<id>/vm.json` with:
- `meta`: local metadata (id, name, pid, created_at, guest_ip, tap, api_sock, log, rootfs, kernel, snapshot info).
- `config`: full `/vm/config` response from Firecracker.
Each VM writes:
- `state/vms/<id>/vm.json`: raw `/vm/config` response from Firecracker.
- `state/vms/<id>/meta.json`: local metadata (id, name, pid, created_at, guest_ip, tap, api_sock, log, rootfs, kernel, snapshot info).
## Log Notes
- `PCI: Fatal: No config space access function found` and `MissingAddressRange` lines are expected with `pci=off` in `run.sh`.

View file

@ -7,7 +7,7 @@ log() {
usage() {
cat <<'EOF'
Usage: ./customize.sh <output-rootfs> [--size <size>]
Usage: ./customize.sh <output-rootfs> [--size <size>] [--base-rootfs <path>] [--kernel <path>] [--initrd <path>] [--docker] [--modules <dir>]
Creates a copy of rootfs.ext4, optionally resizes it, boots a VM using the
copy as a writable rootfs, then applies base configuration and packages.
@ -37,7 +37,8 @@ mkdir -p "$VM_ROOT"
BASE_ROOTFS="$DIR/rootfs.ext4"
FC_BIN="$DIR/firecracker"
KERNEL="$DIR/vmlinux"
KERNEL="$DIR/wtf/root/boot/vmlinux-6.8.0-94-generic"
INITRD="$DIR/wtf/root/boot/initrd.img-6.8.0-94-generic"
SSH_KEY="$DIR/id_ed25519"
BR_DEV="br-fc"
@ -47,12 +48,34 @@ DNS_SERVER="1.1.1.1"
OUT_ROOTFS=""
SIZE_SPEC=""
INSTALL_DOCKER=0
MODULES_DIR="$DIR/wtf/root/lib/modules/6.8.0-94-generic"
while [[ $# -gt 0 ]]; do
case "$1" in
--size)
SIZE_SPEC="${2:-}"
shift 2
;;
--base-rootfs)
BASE_ROOTFS="${2:-}"
shift 2
;;
--kernel)
KERNEL="${2:-}"
shift 2
;;
--initrd)
INITRD="${2:-}"
shift 2
;;
--docker)
INSTALL_DOCKER=1
shift
;;
--modules)
MODULES_DIR="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
@ -79,6 +102,18 @@ if [[ ! -f "$BASE_ROOTFS" ]]; then
log "base rootfs not found: $BASE_ROOTFS"
exit 1
fi
if [[ ! -f "$KERNEL" ]]; then
log "kernel not found: $KERNEL"
exit 1
fi
if [[ -n "$INITRD" && ! -f "$INITRD" ]]; then
log "initrd not found: $INITRD"
exit 1
fi
if [[ -n "$MODULES_DIR" && ! -d "$MODULES_DIR" ]]; then
log "modules dir not found: $MODULES_DIR"
exit 1
fi
if [[ -e "$OUT_ROOTFS" ]]; then
log "output rootfs already exists: $OUT_ROOTFS"
@ -197,11 +232,16 @@ sudo -E curl --unix-socket "$API_SOCK" -X PUT http://localhost/machine-config \
KCMD="console=ttyS0 reboot=k panic=1 pci=off root=/dev/vda rw ip=${GUEST_IP}::${BR_IP}:255.255.255.0:${VM_NAME}:eth0:off:${DNS_SERVER} hostname=${VM_NAME}"
INITRD_JSON=""
if [[ -n "$INITRD" ]]; then
INITRD_JSON=", \"initrd_path\": \"$INITRD\""
fi
sudo -E curl --unix-socket "$API_SOCK" -X PUT http://localhost/boot-source \
-H "Content-Type: application/json" \
-d "{
\"kernel_image_path\": \"$KERNEL\",
\"boot_args\": \"$KCMD\"
\"boot_args\": \"$KCMD\"${INITRD_JSON}
}" >/dev/null
sudo -E curl --unix-socket "$API_SOCK" -X PUT http://localhost/drives/rootfs \
@ -258,13 +298,50 @@ fi
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
DEBIAN_FRONTEND=noninteractive apt-get -y install git less tree ca-certificates curl
if [[ \"$INSTALL_DOCKER\" == \"1\" ]]; then
DEBIAN_FRONTEND=noninteractive apt-get -y remove containerd || true
if ! DEBIAN_FRONTEND=noninteractive apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then
DEBIAN_FRONTEND=noninteractive apt-get -y install docker.io
fi
if command -v systemctl >/dev/null 2>&1; then
systemctl enable --now docker || true
fi
fi
git config --system init.defaultBranch main
"
log "customization complete; shutting down"
if [[ -n "$MODULES_DIR" ]]; then
MODULES_BASE="$(basename "$MODULES_DIR")"
log "copying kernel modules ($MODULES_BASE) into guest"
tar -C "$(dirname "$MODULES_DIR")" -cf - "$MODULES_BASE" | \
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
"root@${GUEST_IP}" bash -lc "set -e
mkdir -p /lib/modules
tar -C /lib/modules -xf -
depmod -a \"$MODULES_BASE\"
mkdir -p /etc/modules-load.d
printf 'nf_tables\nnft_chain_nat\nveth\nbr_netfilter\noverlay\n' > /etc/modules-load.d/docker-netfilter.conf
mkdir -p /etc/sysctl.d
cat > /etc/sysctl.d/99-docker.conf <<'SYSCTL'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
SYSCTL
sysctl --system >/dev/null 2>&1 || true
sync
"
fi
log "shutting down guest"
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
"root@${GUEST_IP}" bash -lc "sync" || true
sudo -E curl --unix-socket "$API_SOCK" -X PUT http://localhost/actions \
-H "Content-Type: application/json" \
-d '{ "action_type": "SendCtrlAltDel" }' >/dev/null || true
sleep 2
for _ in $(seq 1 200); do
if ! ps -p "$FC_PID" >/dev/null 2>&1; then
break
fi
sleep 0.05
done
log "done"

69
make-rootfs.sh Normal file
View file

@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
log() {
printf '[make-rootfs] %s\n' "$*"
}
usage() {
cat <<'EOF'
Usage: ./make-rootfs.sh [--size <size>] [--base-rootfs <path>]
Builds rootfs-docker.ext4 using customize.sh. If --base-rootfs is omitted,
the first existing file is used:
./rootfs.ext4
./ubuntu-noble-rootfs/rootfs.ext4
./ubuntu-lts/rootfs.ext4
EOF
}
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUT_ROOTFS="$DIR/rootfs-docker.ext4"
SIZE_SPEC="6G"
BASE_ROOTFS=""
while [[ $# -gt 0 ]]; do
case "$1" in
--size)
SIZE_SPEC="${2:-}"
shift 2
;;
--base-rootfs)
BASE_ROOTFS="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
log "unknown option: $1"
usage
exit 1
;;
esac
done
if [[ -f "$OUT_ROOTFS" ]]; then
log "already exists: $OUT_ROOTFS"
exit 0
fi
if [[ -z "$BASE_ROOTFS" ]]; then
if [[ -f "$DIR/rootfs.ext4" ]]; then
BASE_ROOTFS="$DIR/rootfs.ext4"
elif [[ -f "$DIR/ubuntu-noble-rootfs/rootfs.ext4" ]]; then
BASE_ROOTFS="$DIR/ubuntu-noble-rootfs/rootfs.ext4"
elif [[ -f "$DIR/ubuntu-lts/rootfs.ext4" ]]; then
BASE_ROOTFS="$DIR/ubuntu-lts/rootfs.ext4"
else
log "no base rootfs found"
exit 1
fi
fi
log "building $OUT_ROOTFS from $BASE_ROOTFS"
exec "$DIR/customize.sh" "$OUT_ROOTFS" \
--size "$SIZE_SPEC" \
--base-rootfs "$BASE_ROOTFS" \
--docker

28
run.sh
View file

@ -15,6 +15,7 @@ Options:
--ram <mib> RAM in MiB (default: 1024)
--rootfs <path> Root filesystem image (default: ./rootfs.ext4)
--kernel <path> Kernel image (default: ./vmlinux)
--initrd <path> Initrd image (optional)
--home-size <size> Home disk size (e.g. 4G, 10240M)
--var-size <size> Var disk size (e.g. 4G, 10240M)
-h, --help Show this help
@ -29,8 +30,9 @@ VM_ROOT="$STATE/vms"
mkdir -p "$VM_ROOT"
FC_BIN="$DIR/firecracker"
DEFAULT_KERNEL="$DIR/vmlinux"
DEFAULT_ROOTFS="$DIR/rootfs.ext4"
DEFAULT_KERNEL="$DIR/wtf/root/boot/vmlinux-6.8.0-94-generic"
DEFAULT_ROOTFS="$DIR/rootfs-docker.ext4"
DEFAULT_INITRD="$DIR/wtf/root/boot/initrd.img-6.8.0-94-generic"
SSH_KEY="$DIR/id_ed25519"
NAMEGEN="$DIR/namegen"
@ -56,6 +58,7 @@ HOME_SIZE="$DEFAULT_HOME_SIZE"
VAR_SIZE="$DEFAULT_VAR_SIZE"
KERNEL="$DEFAULT_KERNEL"
ROOTFS="$DEFAULT_ROOTFS"
INITRD="$DEFAULT_INITRD"
VM_NAME=""
shopt -s nullglob
@ -109,6 +112,10 @@ while [[ $# -gt 0 ]]; do
KERNEL="${2:-}"
shift 2
;;
--initrd)
INITRD="${2:-}"
shift 2
;;
--home-size)
HOME_SIZE="${2:-}"
shift 2
@ -189,6 +196,12 @@ if ! [[ "$VM_NAME" =~ ^[a-z0-9][a-z0-9-]{0,63}$ ]]; then
log "invalid --name value: $VM_NAME"
exit 1
fi
if [[ ! -f "$ROOTFS" ]]; then
if [[ "$ROOTFS" == "$DEFAULT_ROOTFS" && -x "$DIR/make-rootfs.sh" ]]; then
log "rootfs missing; building via make-rootfs.sh"
"$DIR/make-rootfs.sh"
fi
fi
if [[ ! -f "$ROOTFS" ]]; then
log "rootfs not found: $ROOTFS"
exit 1
@ -197,6 +210,10 @@ if [[ ! -f "$KERNEL" ]]; then
log "kernel not found: $KERNEL"
exit 1
fi
if [[ -n "$INITRD" && ! -f "$INITRD" ]]; then
log "initrd not found: $INITRD"
exit 1
fi
if name_taken "$VM_NAME"; then
log "name already in use: $VM_NAME"
exit 1
@ -418,11 +435,16 @@ log "configuring machine"
log "configuring boot source"
KCMD="console=ttyS0 reboot=k panic=1 pci=off root=/dev/vda rw ip=${GUEST_IP}::${BR_IP}:255.255.255.0::eth0:off:${DNS_SERVER} hostname=${VM_NAME}"
INITRD_JSON=""
if [[ -n "$INITRD" ]]; then
INITRD_JSON=", \"initrd_path\": \"$INITRD\""
fi
"${CURL_CMD[@]}" --unix-socket "$API_SOCK" -X PUT http://localhost/boot-source \
-H "Content-Type: application/json" \
-d "{
\"kernel_image_path\": \"$KERNEL\",
\"boot_args\": \"$KCMD\"
\"boot_args\": \"$KCMD\"${INITRD_JSON}
}" >/dev/null
# Root filesystem

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more