Julia Evans

Day 47: Using device mapper to manage Firecracker images

On Tuesday I didn’t get too much done, but I did learn how to use device mapper!

I mostly followed these directions: Fun with devicemapper snapshots but with a few changes. I basically tried to do exactly what ignite does here in snapshot.go, except in a bash script instead of a Go program.

This post isn’t going to be terribly clear because I’m tired right now, these are mostly notes for myself so I can remember how to do it later.

the problem: I don’t want to copy images every time I launch a VM

I can’t use the filesystem image directly I launch a VM because if I do, the user will accidentally end up changing the base image themself if they write files.

I was dealing with this by making a copy every time, but that’s kind of slow and it felt really inefficient. But there’s an alternative!

the solution: copy on write with device mapper!

The solution is to use copy on write! So instead of making a copy, instead we overlay another image on top. Reads come through the bottom, but any writes only go to the top level.

The weird thing I had to wrap my head around was that unlike with overlayfs, this copy-on-write thing is implemented at the disk image level – the overlay on top doesn’t contain files, it just contains sort of random blocks of data that would be totally impossible for any program to interpret by themselves.

a commented bash script

Here’s a commented bash script with how I implemented this. It was way simpler than I expected – it’s just runs both losetup and dmsetup twice.

BASEIMAGE=/path/to/base/image.ext4
OVERLAY=/path/to/overlay.ext4

# Step 1: Create an empty image
# I also tried to create the image with fallocate but it didn't work as well
for some reason I don't understand yet
qemu-img create -f raw $OVERLAY 1200M
OVERLAY_SZ=`blockdev --getsz $OVERLAY`

# Step 2: Create a loop device for the BASEIMAGE file (like /dev/loop16)
LOOP=$(losetup --find --show --read-only $BASEIMAGE)
SZ=`blockdev --getsz $BASEIMAGE`

# Step 3: Create /dev/mapper/mybase
printf "0 $SZ linear $LOOP 0\n$SZ $OVERLAY_SZ zero"  | dmsetup create mybase

# Step 4: Create another loop device for the OVERLAY file
LOOP2=$(losetup /dev/loop23 --show $OVERLAY)

# Step 5: Create the final device mapper
echo "0 $OVERLAY_SZ snapshot /dev/mapper/mybase $LOOP2 P 8" | dmsetup create myoverlay

another problem: losetup ends up in an infinite loop sometimes

I noticed that sometimes losetup, instead of finding and creating a loop device, sometimes just ends up in an infinite loop where it tries and fails to create the same loop device forever. I don’t know why that is yet.

Day 46: debugging an iptables problem Day 48: Another Go program, and a little vim configuration