This will break down how to use LVM and thin-provisioned LVs in front of a disk image file allowing new writes to go to a separate LV from the original image. This will function similarly to something like UnionFS but using the LVM layer instead and using an thin provisioned LV for the new data itself.
This might be useful when you already have a filesystem image (maybe from a backup or similar) and it’s big enough that copying it before using it is not feasible, but you still want to be able to revert to the previous state if something blows up. This will handle all of that at the block layer.
My main use-case for this is on an older system where some of the modern union or overlay filesystems (unionfs, overlayfs, aufs, etc) are not available or stable. I’m sure there may be other uses as well, but let’s get to it.
Prep
First, let’s dummy up a filesystem image. Normally, you’d already have an image to work with, but for our purposes, i’ll just make a small 50MB image for testing. We’ll put a filesystem on it, mount it, touch a couple files, etc. Then unmount and we’re ready to go.
root@server:~# dd if=/dev/zero of=fs.img bs=1M count=50 50+0 records in 50+0 records out 52428800 bytes (52 MB) copied, 0.155694 s, 337 MB/s root@server:~# mkfs.xfs fs.img root@server:~# mount -o loop fs.img /mnt/test root@server:~# cd /mnt/test root@server:/mnt/test# mkdir foo root@server:/mnt/test# mkdir bar root@server:/mnt/test# touch foo/baz root@server:/mnt/test# touch bar/quux root@server:/mnt/test# echo "hello world" > myfile root@server:/mnt/test# cd root@server:~# umount /mnt/test
Set Up LVM Devices
For this example, I am going to use 2 LVs for data and metadata for my thin pool. To properly size the metadata volume, we’ll use the thin_metadata_size command. It is available from the thin-provisioning-tools package (on Ubuntu. for others, YMMV). I specify a value of 500MB for the pool size. This would allow me 10 full copies, which i’ll never have. I also used a max of 100 thin snapshots. Obviously, tune these to your liking. The 128sectors indicates a 64K block; this is actually the default. Again, tune this as necessary.
root@server:~# thin_metadata_size -b128s -s500m -m100 thin_metadata_size - 1312 sectors estimated metadata area size for "--block-size=128sectors --pool-size=500megabytes --max-thins=100"
1312 sectors =~ 650K, so I’ll use a 1MB LV for this. (You’ll see below this gets rounded up to 4MB anyway due to the LV extent size in my VG)
Let’s create our LVs:
root@server:~# lvcreate -L500M -n thin-data vg0 Logical volume "thin-data" created. root@server:~# lvcreate -L1M -n thin-metadata vg0 Rounding up size to full physical extent 4.00 MiB Logical volume "thin-metadata" created.
Setting up the DM devices
These steps turn our LVs into DM devices that can be used in our thin pool
root@server:~# dmsetup create test-thin-metadata --table '0 8192 linear /dev/vg0/thin-metadata 0' root@server:~# dmsetup create test-thin-data --table '0 1024000 linear /dev/vg0/thin-data 0'
Set up the Thin Pool
root@server:~# dmsetup create test-thin-pool --table '0 1024000 thin-pool /dev/mapper/test-thin-metadata /dev/mapper/test-thin-data 128 0'
And now for the real work:
First we turn the image into a block device using losetup. you can pick any loopx that’s not in use:
root@server:~# losetup /dev/loop0 /root/fs.img
Then we allocate an ordinal. This creates a dm device we can use, with no storage allocated. The ordinal is a numeric identifier and must not be already in use.
dmsetup message /dev/mapper/test-thin-pool 0 "create_thin 438"
Then create a thin snapshot, specifying the ordinal as well as the origin device (/dev/loop0) as an argument.
dmsetup create test-thin-snap --table '0 102400 thin /dev/mapper/test-thin-pool 438 /dev/loop0'
Mount It!
That’s it. Now we can mount it up:
root@server:~# mount /dev/mapper/test-thin-snap /mnt/cowtest root@server:~# cd /mnt/cowtest root@server:/mnt/cowtest# ls -R .: bar foo myfile ./bar: quux ./foo: baz root@server:/mnt/cowtest# cat myfile hello world
Test
Ok, Let’s mess it up!
root@server:/mnt/cowtest# rm -rf * root@server:/mnt/cowtest# ls root@server:/mnt/cowtest# touch OH_NOES_ITS_ALL_GONE root@server:/mnt/cowtest# ls -l total 0 -rw-r--r-- 1 root root 0 Aug 16 09:20 OH_NOES_ITS_ALL_GONE
Revert to Original
Now let’s blow that away:
root@server:/mnt/cowtest# cd .. root@server:/mnt# umount /mnt/cowtest root@server:/mnt# dmsetup remove test-thin-snap root@server:/mnt# mount /dev/loop0 /mnt/test
… and we’re still ok 🙂
root@server:/mnt# cd /mnt/test root@server:/mnt/test# ls -R .: bar foo myfile ./bar: quux ./foo: baz
References: