From e70a0584c99571b56830b337819552839c0a8ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Sun, 26 Apr 2026 15:44:22 +0200 Subject: [PATCH] Add more Firecracker microVM scripts --- .gitignore | 5 +- .../{get_rootfs.bash => 1_get_rootfs.bash} | 2 +- firecracker/2_download_firecracker.bash | 11 ++ firecracker/3_start_firecracker.bash | 10 ++ firecracker/4_start_microvm.bash | 110 ++++++++++++++++++ firecracker/5_start_with_config.bash | 7 ++ firecracker/vm_config.json | 43 +++++++ 7 files changed, 185 insertions(+), 3 deletions(-) rename firecracker/{get_rootfs.bash => 1_get_rootfs.bash} (95%) create mode 100755 firecracker/2_download_firecracker.bash create mode 100755 firecracker/3_start_firecracker.bash create mode 100755 firecracker/4_start_microvm.bash create mode 100755 firecracker/5_start_with_config.bash create mode 100644 firecracker/vm_config.json diff --git a/.gitignore b/.gitignore index aa87842..0382df3 100644 --- a/.gitignore +++ b/.gitignore @@ -162,6 +162,7 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -# Ignore files related to Firecracker microVM, except for get_rootfs.bash which is used in image preparation. +# Ignore files related to Firecracker microVM, except for .bash files which are used in image preparation. firecracker/* -!firecracker/get_rootfs.bash +!firecracker/*.bash +!firecracker/vm_config.json diff --git a/firecracker/get_rootfs.bash b/firecracker/1_get_rootfs.bash similarity index 95% rename from firecracker/get_rootfs.bash rename to firecracker/1_get_rootfs.bash index b8bb602..9b89b2a 100755 --- a/firecracker/get_rootfs.bash +++ b/firecracker/1_get_rootfs.bash @@ -1,5 +1,5 @@ #!/bin/bash -# This script downloads the latest Linux kernel and rootfs images from Firecracker CI, patches the rootfs with an SSH key, and creates an ext4 filesystem image. +set -euo pipefail required_commands=( uname diff --git a/firecracker/2_download_firecracker.bash b/firecracker/2_download_firecracker.bash new file mode 100755 index 0000000..909a682 --- /dev/null +++ b/firecracker/2_download_firecracker.bash @@ -0,0 +1,11 @@ +#!/bin/bash +set -euo pipefail + +ARCH="$(uname -m)" +release_url="https://github.com/firecracker-microvm/firecracker/releases" +latest=$(basename "$(curl -fsSLI -o /dev/null -w "%{url_effective}" "${release_url}/latest")") +curl -L "${release_url}/download/${latest}/firecracker-${latest}-${ARCH}.tgz" \ +| tar -xz + +# Rename the binary to "firecracker" +mv "release-${latest}-${ARCH}/firecracker-${latest}-${ARCH}" firecracker diff --git a/firecracker/3_start_firecracker.bash b/firecracker/3_start_firecracker.bash new file mode 100755 index 0000000..43cf49c --- /dev/null +++ b/firecracker/3_start_firecracker.bash @@ -0,0 +1,10 @@ +#!/bin/bash +set -euo pipefail + +API_SOCKET="/tmp/firecracker.socket" + +# Remove API unix socket +sudo rm -f $API_SOCKET + +# Run firecracker +sudo ./firecracker --api-sock "${API_SOCKET}" --enable-pci diff --git a/firecracker/4_start_microvm.bash b/firecracker/4_start_microvm.bash new file mode 100755 index 0000000..5a04ff5 --- /dev/null +++ b/firecracker/4_start_microvm.bash @@ -0,0 +1,110 @@ +#!/bin/bash +set -euo pipefail + +TAP_DEV="tap0" +TAP_IP="172.16.0.1" +MASK_SHORT="/30" + +# Setup network interface +sudo ip link del "$TAP_DEV" 2> /dev/null || true +sudo ip tuntap add dev "$TAP_DEV" mode tap +sudo ip addr add "${TAP_IP}${MASK_SHORT}" dev "$TAP_DEV" +sudo ip link set dev "$TAP_DEV" up + +# Enable ip forwarding +sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +sudo iptables -P FORWARD ACCEPT + +# This tries to determine the name of the host network interface to forward +# VM's outbound network traffic through. If outbound traffic doesn't work, +# double check this returns the correct interface! +HOST_IFACE=$(ip -j route list default |jq -r '.[0].dev') + +# Set up microVM internet access +sudo iptables -t nat -D POSTROUTING -o "$HOST_IFACE" -j MASQUERADE || true +sudo iptables -t nat -A POSTROUTING -o "$HOST_IFACE" -j MASQUERADE + +API_SOCKET="/tmp/firecracker.socket" +LOGFILE="./firecracker.log" + +# Set log file +sudo curl -X PUT --unix-socket "${API_SOCKET}" \ + --data "{ + \"log_path\": \"${LOGFILE}\", + \"level\": \"Debug\", + \"show_level\": true, + \"show_log_origin\": true + }" \ + "http://localhost/logger" + +KERNEL="./$(ls vmlinux* | tail -1)" +KERNEL_BOOT_ARGS="console=ttyS0 reboot=k panic=1" + +ARCH=$(uname -m) + +if [ ${ARCH} = "aarch64" ]; then + KERNEL_BOOT_ARGS="keep_bootcon ${KERNEL_BOOT_ARGS}" +fi + +# Set boot source +sudo curl -X PUT --unix-socket "${API_SOCKET}" \ + --data "{ + \"kernel_image_path\": \"${KERNEL}\", + \"boot_args\": \"${KERNEL_BOOT_ARGS}\" + }" \ + "http://localhost/boot-source" + +ROOTFS="./$(ls *.ext4 | tail -1)" + +# Set rootfs +sudo curl -X PUT --unix-socket "${API_SOCKET}" \ + --data "{ + \"drive_id\": \"rootfs\", + \"path_on_host\": \"${ROOTFS}\", + \"is_root_device\": true, + \"is_read_only\": false + }" \ + "http://localhost/drives/rootfs" + +# The IP address of a guest is derived from its MAC address with +# `fcnet-setup.sh`, this has been pre-configured in the guest rootfs. It is +# important that `TAP_IP` and `FC_MAC` match this. +FC_MAC="06:00:AC:10:00:02" + +# Set network interface +sudo curl -X PUT --unix-socket "${API_SOCKET}" \ + --data "{ + \"iface_id\": \"net1\", + \"guest_mac\": \"$FC_MAC\", + \"host_dev_name\": \"$TAP_DEV\" + }" \ + "http://localhost/network-interfaces/net1" + +# API requests are handled asynchronously, it is important the configuration is +# set, before `InstanceStart`. +sleep 0.015s + +# Start microVM +sudo curl -X PUT --unix-socket "${API_SOCKET}" \ + --data "{ + \"action_type\": \"InstanceStart\" + }" \ + "http://localhost/actions" + +# API requests are handled asynchronously, it is important the microVM has been +# started before we attempt to SSH into it. +sleep 2s + +KEY_NAME=./$(ls *.id_rsa | tail -1) + +# Setup internet access in the guest +ssh -i $KEY_NAME root@172.16.0.2 "ip route add default via 172.16.0.1 dev eth0" + +# Setup DNS resolution in the guest +ssh -i $KEY_NAME root@172.16.0.2 "echo 'nameserver 8.8.8.8' > /etc/resolv.conf" + +# SSH into the microVM +ssh -i $KEY_NAME root@172.16.0.2 + +# Use `root` for both the login and password. +# Run `reboot` to exit. diff --git a/firecracker/5_start_with_config.bash b/firecracker/5_start_with_config.bash new file mode 100755 index 0000000..d93955f --- /dev/null +++ b/firecracker/5_start_with_config.bash @@ -0,0 +1,7 @@ +#!/bin/bash +set -euo pipefail + +API_SOCKET="/tmp/firecracker.socket" +sudo rm -f $API_SOCKET + +sudo ./firecracker --api-sock $API_SOCKET --config-file ./vm_config.json diff --git a/firecracker/vm_config.json b/firecracker/vm_config.json new file mode 100644 index 0000000..4cca327 --- /dev/null +++ b/firecracker/vm_config.json @@ -0,0 +1,43 @@ +{ + "boot-source": { + "kernel_image_path": "vmlinux-6.1.155", + "boot_args": "console=ttyS0 reboot=k panic=1", + "initrd_path": null + }, + "drives": [ + { + "drive_id": "rootfs", + "partuuid": null, + "is_root_device": true, + "cache_type": "Unsafe", + "is_read_only": false, + "path_on_host": "ubuntu-24.04.ext4", + "io_engine": "Sync", + "rate_limiter": null, + "socket": null + } + ], + "machine-config": { + "vcpu_count": 2, + "mem_size_mib": 1024, + "smt": false, + "track_dirty_pages": false, + "huge_pages": "None" + }, + "cpu-config": null, + "balloon": null, + "network-interfaces": [ + { + "iface_id": "net1", + "guest_mac": "06:00:AC:10:00:02", + "host_dev_name": "tap0" + } + ], + "vsock": null, + "logger": null, + "metrics": null, + "mmds-config": null, + "entropy": null, + "pmem": [], + "memory-hotplug": null +}