Удалённые инкрементные резервные копии ZFS. Проще некуда

в 9:31, , рубрики: increment, proxmox, receive, remote, send, ssh, zfs

Подстрахуй братуху, подстрахуй...

В 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, хотя бы HL).

Всем мир! Спасибо, что дочитали!

Автор: Дегтярёв Константин

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js