Вдруг осознал, что я тут двигаю всякие классные технологии, а сам пребываю в каменном веке. Перенёс /home в LVM. Сразу захотелось использовать моментальные инкрементальные бэкапы LVM. Как это сделать в сети написано (точнее, раскопировано и переведено) уже не один десяток раз. Но я пока не встретил ни одной статьи, где простые примеры создания снимка эволюционировали до полноценного скрипта. Восполню этот пробел quick programming’ом.
Но сначала немного технических деталей. В снапшотах ZFS есть неоспоримые преимущества перед снапшотами LVM:
- В LVM надо заранее выделять место под снапшот (по сути, создавать ещё один logical volume). Если размер изменений в основном разделе превысили это место, снапшот становится неактивным. Его нельзя ни подмонтировать, ни растянуть. Только удалить. Но место под снапшоты можно налету увеличивать, это сомнительный, но плюс.
- В LVM в каждом снапшоте хранится разница с основным разделом, а не с предыдущим разделом. В итоге, если мы сделали снапшот backup1, затем залили в FS файлик 30MB, затем сделали снапшот backup2, затем залили ещё один файлик на 30MB, то в backup1 будет занято 60MB, а в backup2 30MB. Логично было бы делать снапшот от снапшота, тогда разница накапливалась по-человечьи. Но LVM такое извращение не поддерживается.
Но есть и преимущества:
- Снапшот является RW. Честно говоря, так и не придумал, зачем оно может понадобиться.
- LVM живёт в kernel space, в отличие от порта ZFS на Linux zfs-fuse.
- ZFS предоставляет и не нужный мне слой навороченной FS.
Итак, надо периодически делать снапшот раздела, хранить определённое количество часовых и суточных копий. Для этого написал shell-скриптик lvm-home-snapshot. Вот он:
#!/bin/sh name=$1 size=$2 vgname=$3 lvname=$4 copies=$5 time=`date +'%Y-%m-%d--%H-%M-%S'` sname="backup-$time-$name" /sbin/lvcreate -p r -L$size -s -n $sname /dev/$vgname/$lvname lst=`/sbin/lvdisplay | egrep '^ +LV Name' | egrep "/dev/$vgname/backup-.*-$name"|awk '{print $3}'` snapshots_cnt=`echo $lst|wc -w` if [ $snapshots_cnt -gt $copies ]; then to_delete=`(for i in $lst; do echo $i; done) | head $(($copies-$snapshots_cnt))` for i in $to_delete; do /sbin/lvremove -f $i; done fi
Как хорошо видно, принимает 5 параметров: имя снапшота, размер, имя группы разделов, имя раздела, количество хранимых копий. Пример использования:
lvm-home-snapshot hourly 768M home john 3
Создаст снапшот “/dev/home/backup-2009-06-19–14-45-01-hourly” раздела /dev/home/john размером 768MB. Если накопилось более трёх снапшотов с именем hourly — наиболее старые будут удалены. Итого, в крон написал:
0 14 * * * /root/bin/lvm-home-snapshot day 2G home john 4
0 0-4,15-23 * * * /root/bin/lvm-home-snapshot hour 256M home john 8
15,30,45 * * * * /root/bin/lvm-home-snapshot hourquot 128M home john 3
Итак снапшоты создаются, всё чудно. Но надо как-то минимизировать вероятность перезаполнения снапшотов. Для этого был написан ещё один скрипт, который заодно удаляет INACTIVE (переполнившиеся) снапшоты:
#!/usr/bin/perl use warnings; use strict; my ($name, $vgname, $maxpct, $addpct) = @ARGV; open my $proc, "/sbin/lvdisplay|"; my ($d, $s, $u); while (<$proc>) { chomp; if (/^ +LV Name +(.*)/) { $d=$1 } elsif (/^ +COW-table size +(\d+)\S* +(.*)/) { $s=int $1; $u=$2 } elsif (/^ +Allocated to snapshot +(\d+)/) { my $pct = $1; next if $d !~ m|^/dev/$vgname/backup-.*-$name$| || $pct<$maxpct; my $newsize = int $s*($addpct/100+1); `/sbin/lvextend -L$newsize$u $d`; } elsif (m|^ +(/dev/$vgname/backup-.*-$name) \[INACTIVE\]|) { `/sbin/lvremove -f $1`; } } close $proc
Принимает параметры: имя снапшота, имя группы разделов, максимальный процент занятости (при превышении снапшот увеличивается), на сколько процентов от текущего увеличить. Пример использования:
lvm-home-snapshot-extend hourly home 15 30
Все снапшоты с именем hourly в группе разделов home, занятые более чем на 15%, будут увеличены на 30%. В крон добавил строчки:
* * * * * /root/bin/lvm-home-snapshot-extend hourquot home 15 30
* * * * * /root/bin/lvm-home-snapshot-extend hour home 25 30
* * * * * /root/bin/lvm-home-snapshot-extend day home 35 20
Не слишком эффективно, но меня устраивает. Да и лень как-то дальше развивать.