Подстрахуй братуху, подстрахуй...
В ZFS 0.8 появилась возможность отправлять инкрементные снимки зашифрованных данных не расшифровывая. То есть имея зашифрованный массив, можно организовать его зеркальную копию на удалённой машине, не доверяя владельцу удалённого помещения. Ключ и расшифрованные данные не покидают доверенное помещение.
Никто из моих клиентов (микроконтор) не имеет более одного серверного помещения, если туда придёт к примеру пожар — всему звезда. Договорившись со своим коллегой по опасному админскому бизнесу, вы можете взаимно страховать друг друга не опасаясь, что данными могут воспользоваться.
Любой сервис в виде виртуальной машины ставится на Proxmox, где данные размещены на ZFS, и у вас всегда будет возможность хранить удалённую копию виртуальной машины. Есть шанс, что в один прекрасный день вместо впадания в уныние вы съездите в удалённое помещение, заберёте железяку и запустите все службы в полном здравии. Я уже перевёл почти все свои сервисы на Proxmox около 2 лет назад и ни капли не жалею, всё отлично работает. На ZFS у меня работает терминальный сервер на 30 человек, на который завязаны тонкие клиенты (то есть 30 полноценных рабочих мест). Помимо актуальной копии у вас будут ещё и снапшоты за последние x дней и локально и удалённо.
Да, вы потеряете производительность от самого использования zfs и от шифрования, но для себя я пришёл к выводу, что готов заплатить эту цену за такой функционал.
Ложка дёгтя
В подкате сразу вам ложечка дёгтя. Да, ZFS работает медленее. Существенно. Но использовать её как обычную фс нельзя. Она должна работать в массиве дисков, где увеличение количества дисков только приветствуется, диски желательно должны быть SAS, контроллер должен работать в режиме HBA (а не raid0 в качестве одиночных дисков), должна быть ОЗУ ECC и её должно быть много, обязательно должен быть один SSD (лучше PCI-E) в качестве кэша на чтение второго уровня (небольшой, гигов от 64) и один SSD для ZIL можно совсем небольшой, но желательно MLC.
Касательно SSD я положил на эти рекомендации и купил 2 PCI-E SSD по 128 и поделил каждый на 16 гигов и остальную часть. Два 16 гиговых раздела запустил в ZIL как mirror и а оставшиеся части запустил под кэш второго уровня как stripe. На растерзание zfs отдал 16 гигов оперы. (Один слот на купленной с ebay мамке оказался нерабочим, если бы работал — отдал бы 32)
zfskeeper.sh
От скрипта мне нужны были автоматические уведомления о неполадках, контроль количества снапов на обоих концах и контроль скорости. Из попавших мне в руки аналогов удовлетворивших меня скриптов не нашлось. Ниже я приведу часть скриптов, с которыми я ознакомился перед тем как делать этот. Для получения уведомлений по электронной почте замените в функции send_error_to_mail () мои данные на свои
zfskeeper.sh help покажет вам примеры использования
zfskeeper.sh prepare подскажет вам порядок подготовительных действий (рекомендуемых)
zfskeeper.sh bank/encrypted/subvol-100-disk-0 10 сделает текущий снимок и оставит 10 последних снимков
zfskeeper.sh bank/encrypted/subvol-100-disk-0 10 Remotehost 22 10m replicator bank/rtl/subvol-100-disk-0 20 Отправит незашифрованные данные на remotehost:22 под пользователем replicator со скоростью до 10MB/s оставит 10 снимков на источнике и 20 на удалённом хосте
zfskeeper.sh bank/encrypted/subvol-100-disk-0 10 Remotehost 22 10m replicator bank/rtl/subvol-100-disk-0 20 -w тоже самое, но данные будут зашифрованы
#!/bin/bash
zpool=$1
lsnaps=$2
ip=$3
port=$4
speedl=$5
remoteuser=$6
backpool=$7
rsnaps=$8
sendargs=$9
ZFSLOGDIR=~/zfskeeplogs
if [ ! -d "$ZFSLOGDIR" ]; then
mkdir -p $ZFSLOGDIR
fi
NAMESNAP=`date "+%Y_%m_%d_%T"|sed s/:/_/g`
logfile=$ZFSLOGDIR/$NAMESNAP.log
write_to_log ()
{
echo `date +"%T"`" $1" >> $logfile
}
log_last_command ()
{
if [ $? -ne 0 ]; then
write_to_log "$1 FAILED."
if [ $2 = true ]; then
write_to_log "Script execution stops"
send_error_to_mail "$1" "$?" "$*"
exit 1
fi
else
write_to_log "$1 COMPLETED."
fi
}
send_error_to_mail ()
{
Subject=`hostname`" ZFS backup $zpool missing"
swaks --header "Subject: $Subject" --to admin@mydomain.ru --from "alertbot@mydomain.ru" -a LOGIN -au alertbot@mydomain.ru -ap МailPass --server mail.mydomain.ru -tls --body \
"$Subject
command: zfskeeper.sh $3
$1
$2" \
--attach $logfile
# --attach-type "$(get_mimetype $logfile)" --attach $logfile
}
case "$#" in
1)
case "$1" in
prepare)
echo "
Sender mashine:
apt install swaks mbuffer
useradd -s /bin/bash replicator
mkdir -p /home/replicator
chown replicator:replicator /home/replicator
zfs allow -u replicator send,snapshot,destroy,mount bank/encrypted
su replicator
ssh-keygen
Receiver machine:
ln -s /usr/sbin/zfs /usr/local/bin/zfs
useradd -s /bin/bash replicator
mkdir -p /home/replicator
chown replicator:replicator /home/replicator
passwd replicator
Sender mashine:
ssh-copy-id -i .ssh/id_rsa.pub Remotehost
zfs allow -u replicator compression,create,receive,destroy,mount bank/companyname
"
;;
*)
echo "
Usage:
To show the prepare instrutions:
zfskeeper.sh prepare
localkeeping:
keep last 10 snapshots of bank/encrypted/subvol-100-disk-0
zfskeeper.sh bank/encrypted/subvol-100-disk-0 10
remotekeeping:
keep last 10 snapshots bank/encrypted/subvol-100-disk-0 and send it by ssh to Remotehost:22 bank/rtl/subvol-100-disk-0
and keep last 20 copies by replicator user and limit transferspeed 10Mb/s
zfskeeper.sh bank/encrypted/subvol-100-disk-0 10 Remotehost 22 10m replicator bank/rtl/subvol-100-disk-0 20
If you need to send encrypted data, then you need add -w to the end of the line
zfskeeper bank/encrypted/subvol-100-disk-0 10 Remotehost 22 10m replicator bank/rtl/subvol-100-disk-0 20 -w
"
;;
esac
exit 0
;;
2)
echo `date +"%T"`" Local keeping of $NAMESNAP being started" > $logfile
;;
8|9)
echo `date +"%T"`" Remote keeping of $NAMESNAP being started" > $logfile
;;
*)
echo "illegal number of parameters" >&2
exit 1
;;
esac
####################################### Local part #######################################################
# Remove waste local snaps
numsnap=`zfs list -H -o name -t snapshot -s creation|grep "${zpool}@"|wc|awk '{ print $1 }'`
let MAXSNAP=numsnap-lsnaps+1 >/dev/null
if [ $MAXSNAP -gt 0 ] ; then
for d in `zfs list -H -o name -t snapshot -s creation| grep "${zpool}@"|/usr/bin/head -n"$MAXSNAP"`; do
zfs destroy ${d}
log_last_command "Remove local snapshot $d"
done
fi
# Create fresh local snap
zfs snapshot $zpool@backup_$NAMESNAP
log_last_command "Create local $zpool@backup_$NAMESNAP" true
####################################### Local part #######################################################
if [ -z "$backpool" ]; then
write_to_log "Local keeping is complete"
exit 0
fi
####################################### Remote part #######################################################
# Check remote address
if [ -n "$ip" ]; then
if ping -c 1 $ip > null ; then
sship="ssh -c aes128-ctr -p"$port" -l"$remoteuser" "$ip
else
echo "URL $ip is not accesiible, abort"
send_error_to_mail "Try to ping remote host" "URL $ip is not accesiible, abort" "$*"
exit 1
fi
else
# Remote backup is unnecessary
exit 0
fi
# Remove waste local snaps
numsnap=`$sship zfs list -H -o name -t snapshot -s creation|grep "${backpool}@"|wc|awk '{ print $1 }'`
let MAXSNAP=numsnap-rsnaps+1 >/dev/null
if [ $MAXSNAP -gt 0 ] ; then
for d in `$sship zfs list -H -o name -t snapshot -s creation| grep "${backpool}@"|/usr/bin/head -n"$MAXSNAP"`; do
$sship zfs destroy ${d}
log_last_command "Remove local snapshot $d"
done
fi
# Receive last remote snap
MOST_SLAVE_SNAP_NAME=`$sship zfs list -H -o name -t snapshot -s creation|grep "$backpool@backup"|tail -n1|awk -F "@" '{print $2}'`
# If localbackup has same snap then send incremental copy. If has't then send full
if [ -n "`zfs list -H -o name -t snapshot -s creation|grep -w "$zpool@$MOST_SLAVE_SNAP_NAME"`" ]; then
#zfs send $sendargs -i $MOST_SLAVE_SNAP_NAME $zpool@backup_$NAMESNAP | pv -L $speedl | $sship zfs receive -vF $backpool@backup_$NAMESNAP &>> $logfile
zfs send $sendargs -i $MOST_SLAVE_SNAP_NAME $zpool@backup_$NAMESNAP | mbuffer -q -v 0 -s 128k -m 1G | pv -L $speedl | $sship "mbuffer -q -v 0 -s 128k -m 1G | zfs receive -vF $backpool@backup_$NAMESNAP" &>> $logfile
log_last_command "Sending incremental backup to the remote machine" true
else
#If backpool exist we remove it. You can rename it
if [ -n "`$sship zfs list -H -o name -s creation|grep -w "$backpool"`" ]; then
#$sship zfs rename -p $backpool `$sship zfs list -H -o name|grep -w "$backpool"|awk -F "/" '{print $1}'`/old_$NAMESNAP
$sship zfs destroy -r $backpool 2>> $logfile
log_last_command "Need to destroy remotepool for full sending $backpool" true
fi
#zfs send $sendargs $zpool@backup_$NAMESNAP | pv -L $speedl | $sship zfs receive -vF $backpool &>> $logfile
zfs send $sendargs $zpool@backup_$NAMESNAP | mbuffer -q -v 0 -s 128k -m 1G | pv -L $speedl | $sship "mbuffer -q -v 0 -s 128k -m 1G | zfs receive -vF $backpool" &>> $logfile
log_last_command "Sending full backup to the remote machine" true
fi
####################################### Remote part #######################################################
write_to_log "Remote keeping is complete"
exit 0
zfskeepall.sh
Для того, чтобы держать в тонусе несколько разделов, нужен управляющий скрипт. Его уже каждый может сделать на свой вкус и цвет, в зависимости от конфигурации. Мой тестовый выглядит вот так:
#!/bin/bash
scriptpath=/home/replicator/zfskeeper.sh
$scriptpath bank/encrypted/subvol-100-disk-0 5 RemoteHost 22 30m replicator bank/rtl/subvol-100-disk-0 5 -w
Существующие решения
Нашёл его первым, на основе его проверял вообще работоспособность механизма. Спасибо авторам, но стиль написания довольно грязный, его нужно было однозначно переписывать.
zfs-auto-snapshot.sh — просто глыба. Есть что позаимствовать, красивый, аккуратный, но громоздкий и малофункциональный
zfsbackup.sh — тоже аккуратный, но малофункциональный
P.S. Разгрыз zfs я с третьего раза, каждый раз грыз где-то по месяцу. Мне не удавалось заставить её работать быстро, да и в полной мере не удалось. В целом ситуация изменилась после установки SAS дисков (хотя бы 7200, хотя бы NL).
Всем мир! Спасибо, что дочитали!