zfs

Backup zfs

date
23 / 6 / 2011
comments
1

J'ai mis en place un système de backup de mon serveur FreeBSD (dédié) vers un autre serveur FreeBSD (@home). Les deux machines sont en zfs, c'était pour moi l'occasion de tester zfs (send|recv) over ssh. L'idée c'est qu'on envoie les donnés de manière incrémentale entre un snapshot du jour et un snapshot de la veille. Ce que l'on gagne par rapport à des solutions comme rsync c'est de la rapidité et moins d'accès disques (zfs sait exactement ce qui a bougé entre deux snapshot), l'autre avantage c'est sur la machine de backup on a un snapshot par jour, on peut donc facilement retrouver des fichiers supprimés.

#!/bin/sh

set -e

# Used in ssh command, example user@host -p 2222
REMOTE_HOST="diophante"

# Source zfs pool
POOL_SRC="tank"

# zfs sets to backup (relative to POOL_SRC)
SETS="usr/local/vmail usr/local/data usr/local/pgsql/backups usr/home usr/local/git var/backups"

# Destination pool
POOL_DST="tank/backup/${REMOTE_HOST}"

# We use ssh connection sharing
SSH_ARGS="-o ControlPath=~/.ssh/%r@%h:%p"

# zfs snapshot prefix => tank/foo@bck-2011-05-25
PREFIX="bck-"

# Remote zfs command
REMOTE_ZFS="ssh ${SSH_ARGS} ${REMOTE_HOST} sudo zfs"

# Local zfs command
LOCAL_ZFS="sudo zfs"

# Launch master ssh for sharing connections
ssh -MNn ${SSH_ARGS} ${REMOTE_HOST} &
ssh_master_pid=$!

for zfs_set in ${SETS}
do
    # Test if destination exist
    ${LOCAL_ZFS} list -H ${POOL_DST}/${zfs_set} >/dev/null

    date_suffix="${PREFIX}`date +%F`"
    snap="${POOL_SRC}/${zfs_set}@${date_suffix}"
    old_snap=`${REMOTE_ZFS} list -Ht snapshot 2>/dev/null | grep "^${POOL_SRC}/${zfs_set}@${PREFIX}" 2>/dev/null| awk -F' ' '{ print $1 }' 2>/dev/null`
    if [ "${old_snap}" ]
    then

        if [ "${old_snap}" != "`echo ${old_snap} | head -n 1`" ]
        then
            echo "[!] Multiple zfs snapshot found: ${old_snap}"
            echo "[!] Consider changing PREFIX or fix the issue yourself"
            continue
        fi

        if [ "${old_snap}" = "${snap}" ]
        then
            echo "[!] ${snap} exists"
            continue
        fi
    fi
    echo ${REMOTE_ZFS} snapshot "${snap}"
    ${REMOTE_ZFS} snapshot "${snap}"

    if [ "${old_snap}" ]
    then
        extra_args="-i ${old_snap}"
    else
        extra_args=""
    fi

    echo ${REMOTE_ZFS} send $extra_args "${snap}" '|' ${LOCAL_ZFS} recv -F "${POOL_DST}/${zfs_set}"
    ${REMOTE_ZFS} send $extra_args "${snap}" | ${LOCAL_ZFS} recv -F "${POOL_DST}/${zfs_set}"

    if [ "${old_snap}" ]
    then
        echo ${REMOTE_ZFS} destroy "${old_snap}"
        ${REMOTE_ZFS} destroy "${old_snap}"
    fi
done

kill $ssh_master_pid

Le script est aussi disponible sous forme de gist github.

Sur mes deux machines j'ai un user backup qui peut exécuter zfs avec sudo, l'user backup sur la machine de backup peut accéder en ssh à l'user backup (vous suivez ?) sur la machine à sauvegarder au moyen d'une clé ssh dédiée sans mot de passe.

Le script se lance donc sur la machine de backup (en ayant pris soin de créer tous les sets zfs qu'on va sauvegarder). La première fois il va transférer le set en entier, et les jours suivants il va envoyer de l'incrémental, il faut au minimum un jour entre chaque backup mais rien ne vous empêche de mettre l'heure dans le nom du snapshot.

Commentaires, patchs bienvenus.

PS: Ça fait 6 mois que j'ai pas posté sur ce blog, c'est parce que je travaille et même si je trouve encore un peu de temps pour faire mon propre code c'est moins le cas quand il faut le décrire ici. Par contre je maintient une liste de posts à faire sur ce blog, je rattraperais mon retard bientôt.

Installer et booter une FreeBSD zfs on root depuis Debian/Grub2

date
29 / 11 / 2010
comments
3

J'ai une partition primaire de libre sur mon laptop sous Debian , je comptais y installer une FreeBSD full zfs. Le problème c'est que mon laptop n'a pas (plus) de lecteur cd, et que je me voyais mal faire une installation zfs un peu ardue en pxe et sans avoir la doc dans $BROWSER.

Alors je me dis qu'installer FreeBSD sur la partition libre depuis ma Debian testing avec zfs-fuse est possible. Évidement pas de chroot possible, mais y'a même pas besoin. Le plus dur ça a été de démarrer la FreeBSD avec grub2, mais je suis tombé sur la bonne doc. Voilà la manip sans attendre que qemu se lance.

On installe les petits (gros) tools qu'il nous faut (zfs-fuse est dans testing ou sid) :

sudo apt-get install bsdtar zfs-fuse lftp

On récupère une distribution binnaire de FreeBSD (ici 8.1-RELEASE i386)

mkdir ~/freebsd && cd ~/freebsd
lftp -c "open ftp://ftp.fr.freebsd.org/pub/FreeBSD/releases/i386/8.1-RELEASE/; mirror base"
lftp -c "open ftp://ftp.fr.freebsd.org/pub/FreeBSD/releases/i386/8.1-RELEASE/; mirror kernels"
# Et si vous voulez les man
lftp -c "open ftp://ftp.fr.freebsd.org/pub/FreeBSD/releases/i386/8.1-RELEASE/; mirror manpages"

Ensuite on crée sa pool zfs sur la partition cible (en forçant la version en 14, version de zfs sur FreeBSD)

sudo zpool create -o version=14 tank /dev/sda3
sudo zfs create tank/root
# Ici on peut créer tous les datasets qu'il nous faut

On extract tout ça :

cd ~/freebsd/base && cat base.?? | sudo bsdtar --unlink -xpzf - -C /tank/root
cd ~/freebsd/kernels && cat generic.?? | sudo bsdtar --unlink -xpzf - -C /tank/root/boot
cd ~/freebsd/manpages && cat manpages.?? | sudo bsdtar --unlink -xpzf - -C /tank/root

Les finitions :

# On utilisera le kernel GENERIC
sudo rmdir /tank/root/boot/kernel && sudo mv /tank/root/boot/GENERIC /tank/root/boot/kernel

# On copie le cache zpool pour que la freebsd retrouve "tank"
sudo cp /var/lib/zfs/zpool.cache /tank/root/boot/zfs/

# On évite un warning
sudo touch /tank/root/etc/fstab

# On se fait un rc.conf
cat << EOF | sudo tee /tank/root/etc/rc.conf
hostname="shen.philpep.org"
keymap="fr.iso.acc"
zfs_enable="YES"
EOF

# On met mountpoint / pour tank/root (comme ça tank/root/... se montera à partir de /)
sudo zpool export tank
sudo zpool import -R /mnt tank
sudo zpool set mountpoint=/ tank/root

Et maintenant le plus dur, grub2 :

menuentry 'FreeBSD' {
    insmod zfs
    search -s -l tank
    kfreebsd /root@/boot/kernel/kernel
    kfreebsd_module_elf /root@/boot/kernel/opensolaris.ko
    kfreebsd_module_elf /root@/boot/kernel/zfs.ko
    kfreebsd_module /root@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache
    set kFreeBSD.vfs.root.mountfrom=zfs:tank/root
}

Donc avec cette syntaxe on peut charger des modules au boot et écrire dans des variables sysctl (chose qu'on fait dans /boot/loader.conf avec le bootloader de FreeBSD).

Ça juste marche, et je trouve ça bien pratique, surtout qu'on peut se servir du pool zfs depuis Debian, et on peut aussi monter l'ext3 depuis FreeBSD.

EDIT: Je ne peut pas garantir que ce soit très fiable tout ça. Prudence donc. Si vous avez des problèmes au boot de FreeBSD (truc du style "alloc magic is broken") vous pouvez essayer de remplacer search -s -l tank par set root=(hd0,3) (avec la pool en /dev/sda3 chez moi).