Firecracker FreeBSD
Table of Contents
FreeBSD now available as a firecracker VM #
This last week Colin Percival posted about their work on getting FreeBSD running as a guest in Firecracker.
At the end of their post, some simple steps are shown to help you get going, however some are missing for us FreeBSD novices, my post here tries to fill those gaps.
Disclaimer: I am a novice with FreeBSD so this is probably not the most efficient way of getting a running Firecracker FreeBSD VM. The rootfs image I use is pretty large but I’ll post again once I’ve successfully build my own minimal rootfs.
The steps required are:
- build a kernel (needs to be done on a working FreeBSD system)
- build a rootfs - somehow…
- build the custom branch of firecracker
- put them all together and run it
I’ll try to explain the gaps, things like getting a FreeBSD machine up and running so we can build the custom kernel, get a root UFS filesystem to enable the firecracker VM to run, and putting it all together with a config file so at the end you have a speedy FreeBSD Firecracker VM.
Kernel build environment #
For this post, I use a throw away KVM virtual machine to do the kernel build. You can acquire the qcow2 image from the FreeBSD releases page.
You’ll need to use the xz
tool, by default it will delete the archive once it’s expanded, unless you pass the keep flag like so:
xz -k -d /tmp/FreeBSD-13.1-RELEASE-amd64.qcow2.xz
TIP: make a copy of the qcow2 file, if you follow along you will need to grow the image to be able to build a custom kernel (as there is not enough free space).
Make space for the kernel build #
The VM disk image doesn’t come with enoguh space to build a custom kernel, so this is how I made more disk space.
On the Linux host:
qemu-img resize FreeBSD-13.1-RELEASE-amd64.qcow2 +10G
# now check the space has increased...
qemu-img info FreeBSD-13.1-RELEASE-amd64.qcow2
The qcow2 file now has the space to allow the contained UFS filesystem to grow. To do this we’ll need to boot the VM. I used a quick VM command to get a FreeBSD machine I could use to build a custom kernel for the Firecracker VMs.
qemu-system-x86_64 --enable-kvm FreeBSD-13.1-RELEASE-amd64.qcow2 -net user,hostfwd=tcp::8022-:22 -net nic -monitor stdio
This command is the old style, the recommendation is to move over to passing the hostfwd
param to netdev
but when I tried it I couldn’t SSH into the machine so was stuck withthe QEMU window. It’s added to my todo list of things I need to learn how to fix.
Basically, that command will create a single CPU VM and bind the host’s port 8022 to the guest’s port 22 (which is SSH). I wanted this so I could use scp to copy the custom kernel onto my Linux host.
Anyway, onto growing the UFS rootfs, log into the throwaway VM and follow these steps to add all (nearly) of the space we just added to the qcow2 onto /:
root@freebsd:/usr # gpart show ada0
=> 3 10552571 ada0 GPT (15G) [CORRUPT]
3 123 1 freebsd-boot (62K)
126 66584 2 efi (33M)
66710 2097152 3 freebsd-swap (1.0G)
2163862 8388712 4 freebsd-ufs (4.0G)
The drive shows as being corrupt, to fix that we just need to use the recover option.
root@freebsd:/usr # gpart recover ada0
ada0 recovered
root@freebsd:/usr # gpart show ada0
=> 3 31524085 ada0 GPT (15G)
3 123 1 freebsd-boot (62K)
126 66584 2 efi (33M)
66710 2097152 3 freebsd-swap (1.0G)
2163862 8388712 4 freebsd-ufs (4.0G)
10552574 20971514 - free - (10G)
Now we want to add the free space to the UFS root partition (4)
root@freebsd:/usr # gpart resize -i 4 -s 14G -a 4k ada0
ada0p4 resized
root@freebsd:/usr # gpart show ada0
=> 3 31524085 ada0 GPT (15G)
3 123 1 freebsd-boot (62K)
126 66584 2 efi (33M)
66710 2097152 3 freebsd-swap (1.0G)
2163862 29360122 4 freebsd-ufs (14G)
31523984 104 - free - (52K)
You can skip this next step, I ran it to discover there was more to do
root@freebsd:/dev # df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/gpt/rootfs 3.9G 3.8G -255M 107% /
devfs 1.0K 1.0K 0B 100% /dev
/dev/gpt/efiesp 32M 875K 31M 3% /boot/efi
Now grow the filesystem into the space we attached to the UFS partition.
root@freebsd:/dev # growfs /dev/gpt/rootfs
Device is mounted read-write; resizing will result in temporary write suspension for /.
It's strongly recommended to make a backup before growing the file system.
OK to grow filesystem on /dev/gpt/rootfs, mounted on /, from 4.0GB to 14GB? [yes/no] yes
super-block backups (for fsck_ffs -b #) at:
8979008, 10261696, 11544384, 12827072, 14109760, 15392448, 16675136, 17957824, 19240512, 20523200, 21805888, 23088576, 24371264, 25653952, 26936640, 28219328
root@freebsd:/dev # df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/gpt/rootfs 14G 3.8G 10.2G 73% /
devfs 1.0K 1.0K 0B 100% /dev
/dev/gpt/efiesp 32M 875K 31M 3% /boot/efi
You may have slightly different figures as I ran this after I ran out of space as you can probably see from the root partition being complete full before I resized it. You will have abou a gigabyte free of disk space before the reszie.
Build the kernel #
Now there should be space, we need to enable SSH (so we can copy of the built kernel), pull the source code and build it as per Colin’s instructions.
Just to recap, to run the VM I used:
qemu-system-x86_64 --enable-kvm FreeBSD-13.1-RELEASE-amd64.qcow2 -net user,hostfwd=tcp::8022-:22 -net nic -monitor stdio
Let’s enable SSH so we can later use it to copy files off the VM.
SSH #
To enable SSH, I ran the following:
service sshd status
vi /etc/rc.conf
add the line sshd_enable=YES
to the file and save nd quit :wq
.
service sshd start
Shutdown shutdown -p now
and start again with port mapping to allow host to ssh to guest, like so:
qemu-system-x86_64 --enable-kvm FreeBSD-13.1-RELEASE-amd64.qcow2 -net user,hostfwd=tcp::8022-:22 -net nic
On your host machine, run ssh root@localhost -p8022
to prove it’s working (when the throwaway VM is running).
The instructions so far assume using root, you can however create your own user and precisely assign permissions. As I said, this is a throw away VM for me so I’m just here to build a custom kernel, copy it out to my Linux host and then delete this VM.
Pull kernel source code and build it #
We are now getting to the point where we can follow Colin’s instructions, we just need to install git first.
pkg install git
# now Colin's instructions...
git clone https://git.freebsd.org/src.git /usr/src
cd /usr/src && make buildkernel TARGET=amd64 KERNCONF=FIRECRACKER
You might want to run the VM with more cores than I did as it took quite a while to build. To do that you’ll need to modify the qemu-system-x86_64
command and I think you might need to pass another flag to the make buildkernel
command but I didn’t do it so can’t help you there.
Copy the kernel back to your host #
I created a new user as root wasn’t working and I just wanted to copy the kernel file and dump this VM. I used adduser
and added the created user to wheel (the second question about groups when following the adduser prompts).
The VM needs to stay running but now we will be running commands on our Linux host.
First create a custom ssh key ssh-keygen
on your host and copy it over to the throwaway VM using the new user’s creds (you can scp
and use the password method if you want, I just didn’t as I was experimenting with how to do all of this).
ssh-copy-id -i ~/.ssh/user2id.pub -p 8022 user2@localhost
You can now copy the kernel off the throwaway VM and onto your Linux Host that will be running firecracker.
scp -P 8022 -i ~/.ssh/user2id user2@localhost:/usr/obj/usr/src/amd64.amd64/sys/FIRECRACKER/kernel ./FreeBSD_kernel
With that, the throwaway VM can be shutdown shutdown -p now
and thrown away. We will have a file called FreeBSD_kernel which can be used with firecracker. Keep it safe!
“Building” a rootfs #
This is the quickest way of getting a filesystem that I could think of without examining how to build one from scratch (which will most likely need a running FreeBSD system to create).
I decided to create a raw filesystem from the FreeBSD release qcow2 image (the original one, not the 1GB one we resized). I tried to use firecracker with the qcow2 file but without success… So converting it to a raw file seemed like a good alternative.
qemu-img convert FreeBSD-13.1-RELEASE-amd64.qcow2 -O raw disk.ufs
With this, you will get a 5Gb (so not exactly great, but this is just discovery) file caled disk.ufs
that contains four partitions, with the rootfs one being the 4th, which turns out to be called vtdb0p4 when mounted to a firecracker VM.
Building Firecracker with FreeBSD support #
You don’t need to use the /usr/src
directory I have here.
cd /usr/src/
git clone -b pvh-v3 https://github.com/cperciva/firecracker.git freebsd_firecracker
cd freebsd_firecracker
./tools/devtool build
You should now have a custom firecracker executable that is capable of running your FreeBSD kernel and rootfs, the binary will be located at /usr/src/freebsd_firecracker/build/cargo_target/x86_64-unknown-linux-musl/debug/firecracker
if you followed along with my path from the above comamnds.
Running your first FreeBSD firecracker VM #
Firecracker can be run with multiple commands via it’s API server or by passing a configuration file. Here we will be using a config file but the API method should work just as well.
I based my config file off one of the examples from the firecracker github repo.
I’ve modified the values for:
kernel_image_path
- I just referenced the path (current directory) of the kernel I built.boot_args
- this is from Colin’s blog but the ufs path is altered to match the contents of the rootfs image we obtained earlierpath_on_host
- again, a path but this time to the rootfs file on disk.
{
"boot-source": {
"kernel_image_path": "FreeBSD_kernel",
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off vfs.root.mountfrom=ufs:/dev/vtbd0p4",
"initrd_path": null
},
"drives": [
{
"drive_id": "rootfs",
"path_on_host": "disk.ufs",
"is_root_device": true,
"partuuid": null,
"is_read_only": false,
"cache_type": "Unsafe",
"io_engine": "Sync",
"rate_limiter": null
}
],
"machine-config": {
"vcpu_count": 2,
"mem_size_mib": 1024,
"smt": false,
"track_dirty_pages": false
},
"balloon": null,
"network-interfaces": [],
"vsock": null,
"logger": null,
"metrics": null,
"mmds-config": null
}
We can now run firecracker with the following command to plug in our config:
firecracker --api-sock /tmp/firecracker.socket --config-file vm-config.json
This assumes:
- the custom firecracker executale is on your path
- you are in the directory containing the
vm-config.json
file, custom kernel and rootfs file.
You can use links or you can use absolute paths in your config file as well, I’ve just done it all in the same directory as I’m epxerimenting and want everything together right now.
If successful you should see something like this:
GDB: no debug ports present
KDB: debugger backends: ddb
KDB: current backend: ddb
---<<BOOT>>---
Copyright (c) 1992-2022 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 14.0-CURRENT #0 main-n258661-713efe05429: Thu Oct 20 03:05:12 UTC 2022
root@freebsd:/usr/obj/usr/src/amd64.amd64/sys/FIRECRACKER amd64
FreeBSD clang version 13.0.0 (git@github.com:llvm/llvm-project.git llvmorg-13.0.0-0-gd7b669b3a303)
WARNING: WITNESS option enabled, expect reduced performance.
CPU: Intel(R) Xeon(R) Processor @ 2.60GHz (2600.00-MHz K8-class CPU)
Origin="GenuineIntel" Id=0x506e3 Family=0x6 Model=0x5e Stepping=3
Features=0x1f83fbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE,SSE2,SS,HTT>
Features2=0xfffab223<SSE3,PCLMULQDQ,VMX,SSSE3,FMA,CX16,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE,OSXSAVE,AVX,F16C,RDRAND,HV>
AMD Features=0x2c100800<SYSCALL,NX,Page1GB,RDTSCP,LM>
AMD Features2=0x121<LAHF,ABM,Prefetch>
Structured Extended Features=0x9c67ab<FSGSBASE,TSCADJ,BMI1,AVX2,SMEP,BMI2,ERMS,INVPCID,NFPUSG,MPX,RDSEED,ADX,SMAP,CLFLUSHOPT>
Structured Extended Features2=0x4<UMIP>
Structured Extended Features3=0xac000400<MD_CLEAR,IBPB,STIBP,ARCH_CAP,SSBD>
XSAVE Features=0xf<XSAVEOPT,XSAVEC,XINUSE,XSAVES>
IA32_ARCH_CAPS=0x4c<RSBA,SKIP_L1DFL_VME>
AMD Extended Feature Extensions ID EBX=0x100d000<IBPB,IBRS,STIBP,SSBD>
VT-x: (disabled in BIOS) PAT,HLT,MTF,PAUSE,EPT,UG,VPID
TSC: P-state invariant
Hypervisor: Origin = "KVMKVMKVM"
real memory = 1073741824 (1024 MB)
avail memory = 1015017472 (967 MB)
MPTable: <FC 000000000000>
Event timer "LAPIC" quality 600
FreeBSD/SMP: Multiprocessor System Detected: 2 CPUs
FreeBSD/SMP: 1 package(s) x 2 core(s)
random: registering fast source Intel Secure Key RNG
random: fast provider: "Intel Secure Key RNG"
arc4random: WARNING: initial seeding bypassed the cryptographic random device because it was not yet seeded and the knob 'bypass_before_seeding' was enabled.
ioapic0: MADT APIC ID 3 != hw id 0
ioapic0: Assuming intbase of 0
ioapic0 <Version 1.1> irqs 0-23
Launching APs: 1
random: entropy device external interface
virtio_mmio0: <VirtIO MMIO adapter> at iomem 0xd0000000-0xd0000fff irq 5
vtblk0: <VirtIO Block Adapter> on virtio_mmio0
vtblk0: 5152MB (10552576 512 byte sectors)
kvmclock0: <KVM paravirtual clock>
Timecounter "kvmclock" frequency 1000000000 Hz quality 975
kvmclock0: registered as a time-of-day clock, resolution 0.000001s
aesni0: <AES-CBC,AES-CCM,AES-GCM,AES-ICM,AES-XTS>
cpu0
cpu1
isa0: <ISA bus>
ns8250: UART FCR is broken
ns8250: UART FCR is broken
uart0: <Non-standard ns8250 class UART with FIFOs> at port 0x3f8 irq 4 flags 0x10 on isa0
ns8250: UART FCR is broken
uart0: console (9600,n,8,1)
Timecounter "TSC-low" frequency 1295998999 Hz quality 1000
Timecounters tick every 10.000 msec
random: unblocking device.
Trying to mount root from ufs:/dev/vtbd0p4 []...
WARNING: WITNESS option enabled, expect reduced performance.
Setting hostuuid: 3a006aa7-524a-11ed-b1e3-9da069ca1f6d.
Setting hostid: 0x1406eba8.
Starting file system checks:
/dev/vtbd0p4: FILE SYSTEM CLEAN; SKIPPING CHECKS
/dev/vtbd0p4: clean, 128277 free (21 frags, 16032 blocks, 0.0% fragmentation)
/dev/gpt/efiesp: FILESYSTEM CLEAN; SKIPPING CHECKS
Mounting local filesystems:.
Setting up harvesting: PURE_RDRAND,[CALLOUT],[UMA],[FS_ATIME],SWI,INTERRUPT,NET_NG,[NET_ETHER],NET_TUN,MOUSE,KEYBOARD,ATTACH,CACHED
Feeding entropy: .
ELF ldconfig path: /lib /usr/lib /usr/lib/compat
32-bit compatibility ldconfig path: /usr/lib32
Setting hostname: freebsd.
lo0: link state changed to UP
Starting Network: lo0.
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
inet 127.0.0.1 netmask 0xff000000
groups: lo
nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
Starting devd.
add host 127.0.0.1: gateway lo0 fib 0: route already in table
add host ::1: gateway lo0 fib 0: route already in table
add net fe80::: gateway ::1
add net ff02::: gateway ::1
add net ::ffff:0.0.0.0: gateway ::1
add net ::0.0.0.0: gateway ::1
Updating /var/run/os-release done.
Creating and/or trimming log files.
Clearing /tmp (X related).
Updating motd:.
Starting syslogd.
Mounting late filesystems:.
Starting sendmail_submit.
Starting sendmail_msp_queue.
Starting cron.
Starting background file system checks in 60 seconds.
Sat Oct 22 21:07:37 UTC 2022
FreeBSD/amd64 (freebsd) (ttyu0)
login: root
Oct 22 21:07:45 freebsd login[529]: ROOT LOGIN (root) ON ttyu0
Last login: Sat Oct 22 21:04:02 on ttyu0
FreeBSD 14.0-CURRENT #0 main-n258661-713efe05429: Thu Oct 20 03:05:12 UTC 2022 root@freebsd:/usr/obj/usr/src/amd64.amd64/sys/FIRECRACKER
Welcome to FreeBSD!
Release Notes, Errata: https://www.FreeBSD.org/releases/
Security Advisories: https://www.FreeBSD.org/security/
FreeBSD Handbook: https://www.FreeBSD.org/handbook/
FreeBSD FAQ: https://www.FreeBSD.org/faq/
Questions List: https://lists.FreeBSD.org/mailman/listinfo/freebsd-questions/
FreeBSD Forums: https://forums.FreeBSD.org/
Documents installed with the system are in the /usr/local/share/doc/freebsd/
directory, or can be installed later with: pkg install en-freebsd-doc
For other languages, replace "en" with a language code like de or fr.
Show the version of FreeBSD installed: freebsd-version ; uname -a
Please include that output and any error messages when posting questions.
Introduction to manual pages: man man
FreeBSD directory layout: man hier
To change this login announcement, see motd(5).
root@freebsd:~ #
All the FreeBSDs belong to you, have fun.
Potential Issues #
As I foolishly tried to get the qcow2 image to work as a rootfs without any alteration (it was worth a shot, I thought at least), I’ve seen a couple of issues with the rootfs.
Trying to mount root from ufs:/dev/ada0p4 []...
WARNING: WITNESS option enabled, expect reduced performance.
mountroot: waiting for device /dev/ada0p4...
Mounting from ufs:/dev/ada0p4 failed with error 19.
Loader variables:
vfs.root.mountfrom=ufs:/dev/ada0p4
Manual root filesystem specification:
<fstype>:<device> [options]
Mount <device> using filesystem <fstype>
and with the specified (optional) option list.
eg. ufs:/dev/da0s1a
zfs:zroot/ROOT/default
cd9660:/dev/cd0 ro
(which is equivalent to: mount -t cd9660 -o ro /dev/cd0 /)
? List valid disk boot devices
. Yield 1 second (for background tasks)
<empty line> Abort manual input
mountroot>
The above was caused by assuming I should use ada0p4 rather than the actual value which should be vtbd0p4. I found it by trial and error, slowly and painfully…
If you have issues with mounting the filesystem and get to a similar looking mountroot
prompt, you can just type ?
then
mountroot>?
List of GEOM managed disk devices:
diskid/DISK-6631009484011p4 diskid/DISK-6631009484011p3 diskid/DISK-6631009484011p2 diskid/DISK-6631009484011p1 ufs/rootfs ufsid/627cd95609b1c293 gptid/9658b8ee-d1d9-11ec-b6df-0cc47ad8b808 gpt/rootfs gptid/9658b8eb-d1d9-11ec-b6df-0cc47ad8b808 gpt/swapfs msdosfs/EFISYS gptid/9658b8e8-d1d9-11ec-b6df-0cc47ad8b808 gpt/efiesp gptid/9658b8e2-d1d9-11ec-b6df-0cc47ad8b808 gpt/bootfs diskid/DISK-6631009484011 vtbd0p4 vtbd0p3 vtbd0p2 vtbd0p1 vtbd0
Admitedly, it’s not the easiest chunk of text to read, but it will show you each mount point that is available. If you can’t see vtbd0p4
then there is something wrong with the rootfs you’ve configured (maybe a config issue, maybe the filesystem hasn’t been created correctly).
I knew it was the 4th partition from the gpart
work we did ealier as the first 3 partitions on the GPT partitioned disk were boot
, efi
& swap
with the 4th being ufs
which is the filesystem format that FreeBSD uses in the release VM image.