From 092700b2418abe7d9594366928d92e4d97fd5d1d Mon Sep 17 00:00:00 2001 From: Thales Maciel Date: Thu, 29 Jan 2026 21:41:33 -0300 Subject: [PATCH] Use shared rootfs with per-VM home --- README.md | 9 ++++-- customize.sh | 4 +++ run.sh | 77 ++++++++++++++++++++++------------------------------ 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 7fed2ba..f68665f 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,17 @@ Minimal Firecracker launcher. ## Run Options ``` -./run.sh --name calm_otter --vcpu 4 --ram 2048 --disk-size 6G +./run.sh --name calm_otter --vcpu 4 --ram 2048 --home-size 6G ``` - `--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. -- `--disk-size`: M/G suffixes supported; must be >= base `rootfs.ext4` size. Requires `resize2fs`. +- `--home-size`: M/G suffixes supported (default: 2G). + +## Storage Layout +- `rootfs.ext4` is mounted read-only as `/` and shared across VMs. +- Each VM gets a writable ext4 disk mounted at `/home`. +- The base image must include an `/etc/fstab` entry for `/dev/vdb` → `/home`. ## SSH ``` diff --git a/customize.sh b/customize.sh index 33e796b..8238b61 100755 --- a/customize.sh +++ b/customize.sh @@ -231,6 +231,10 @@ ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ printf 'nameserver %s\n' \"$DNS_SERVER\" > /etc/resolv.conf echo \"$VM_NAME\" > /etc/hostname printf '127.0.0.1 localhost\n127.0.1.1 %s\n' \"$VM_NAME\" > /etc/hosts +mkdir -p /home +if ! grep -q '^/dev/vdb ' /etc/fstab; then + echo '/dev/vdb /home ext4 defaults 0 2' >> /etc/fstab +fi apt-get update DEBIAN_FRONTEND=noninteractive apt-get -y upgrade DEBIAN_FRONTEND=noninteractive apt-get -y install git less tree ca-certificates curl diff --git a/run.sh b/run.sh index 70d7a89..a4f96cf 100755 --- a/run.sh +++ b/run.sh @@ -13,7 +13,7 @@ Options: --name VM name (lowercase letters, digits, -, _) --vcpu vCPU count (default: 2) --ram RAM in MiB (default: 1024) - --disk-size Root disk size (e.g. 4G, 10240M); must be >= base image + --home-size Home disk size (e.g. 4G, 10240M) -h, --help Show this help EOF } @@ -37,6 +37,7 @@ CIDR="24" DEFAULT_VCPU=2 DEFAULT_RAM=1024 +DEFAULT_HOME_SIZE="2G" MIN_VCPU=1 MAX_VCPU=16 MIN_RAM=256 @@ -46,7 +47,7 @@ DNS_SERVER="1.1.1.1" VCPU_COUNT="$DEFAULT_VCPU" RAM_MIB="$DEFAULT_RAM" -DISK_SIZE="" +HOME_SIZE="$DEFAULT_HOME_SIZE" VM_NAME="" shopt -s nullglob @@ -92,8 +93,8 @@ while [[ $# -gt 0 ]]; do RAM_MIB="${2:-}" shift 2 ;; - --disk-size) - DISK_SIZE="${2:-}" + --home-size) + HOME_SIZE="${2:-}" shift 2 ;; -h|--help) @@ -113,7 +114,7 @@ if ! [[ "$VCPU_COUNT" =~ ^[0-9]+$ ]]; then exit 1 fi if (( VCPU_COUNT < MIN_VCPU || VCPU_COUNT > MAX_VCPU )); then - log "vcpu must be between 1 and $MAX_VCPU" + log "vcpu must be between $MIN_VCPU and $MAX_VCPU" exit 1 fi @@ -122,20 +123,18 @@ if ! [[ "$RAM_MIB" =~ ^[0-9]+$ ]]; then exit 1 fi if (( RAM_MIB < MIN_RAM || RAM_MIB > MAX_RAM )); then - log "ram must be between 256 and $MAX_RAM MiB" + log "ram must be between $MIN_RAM and $MAX_RAM MiB" exit 1 fi -DISK_BYTES="" -if [[ -n "$DISK_SIZE" ]]; then - if ! DISK_BYTES="$(parse_disk_size "$DISK_SIZE")"; then - log "invalid --disk-size value: $DISK_SIZE" - exit 1 - fi - if (( DISK_BYTES > MAX_DISK_BYTES )); then - log "disk-size exceeds max of $((MAX_DISK_BYTES / 1024 / 1024 / 1024))G" - exit 1 - fi +HOME_BYTES="" +if ! HOME_BYTES="$(parse_disk_size "$HOME_SIZE")"; then + log "invalid --home-size value: $HOME_SIZE" + exit 1 +fi +if (( HOME_BYTES > MAX_DISK_BYTES )); then + log "home-size exceeds max of $((MAX_DISK_BYTES / 1024 / 1024 / 1024))G" + exit 1 fi if [[ -z "$VM_NAME" ]]; then @@ -193,7 +192,7 @@ sudo -v VM_STARTED=0 CLEANUP_ON_EXIT=0 KEEP_VM_DIR_ON_FAIL=1 -DISK_PATH="$VM_DIR/rootfs.ext4" +HOME_PATH="$VM_DIR/home.ext4" cleanup() { local exit_code=$? @@ -242,35 +241,12 @@ else log "setcap not available; firecracker may need root to open TAP" fi -cp --reflink=auto "$ROOTFS" "$DISK_PATH" - -if [[ -n "$DISK_BYTES" ]]; then - if ! command -v resize2fs >/dev/null 2>&1; then - log "resize2fs required for --disk-size" - exit 1 - fi - BASE_BYTES="$(stat -c%s "$ROOTFS")" - if (( DISK_BYTES < BASE_BYTES )); then - log "disk-size must be >= base image size" - exit 1 - fi - if (( DISK_BYTES > BASE_BYTES )); then - log "resizing rootfs to $DISK_SIZE" - truncate -s "$DISK_BYTES" "$DISK_PATH" - resize2fs "$DISK_PATH" >/dev/null - fi -fi - -if ! command -v debugfs >/dev/null 2>&1; then - log "debugfs required to set resolv.conf" +if ! command -v mkfs.ext4 >/dev/null 2>&1; then + log "mkfs.ext4 required to create home disk" exit 1 fi -RESOLV_TMP="$VM_DIR/resolv.conf" -printf 'nameserver %s\n' "$DNS_SERVER" >"$RESOLV_TMP" -debugfs -w -R "write $RESOLV_TMP /etc/resolv.conf" "$DISK_PATH" >/dev/null 2>&1 || { - log "failed to write /etc/resolv.conf into rootfs" - exit 1 -} +truncate -s "$HOME_BYTES" "$HOME_PATH" +mkfs.ext4 -F "$HOME_PATH" >/dev/null # Host bridge if ! ip link show "$BR_DEV" >/dev/null 2>&1; then @@ -352,8 +328,19 @@ log "attaching root filesystem" -H "Content-Type: application/json" \ -d "{ \"drive_id\": \"rootfs\", - \"path_on_host\": \"$DISK_PATH\", + \"path_on_host\": \"$ROOTFS\", \"is_root_device\": true, + \"is_read_only\": true + }" >/dev/null + +# Home filesystem +log "attaching home filesystem" +"${CURL_CMD[@]}" --unix-socket "$API_SOCK" -X PUT http://localhost/drives/home \ + -H "Content-Type: application/json" \ + -d "{ + \"drive_id\": \"home\", + \"path_on_host\": \"$HOME_PATH\", + \"is_root_device\": false, \"is_read_only\": false }" >/dev/null