script

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.

Ma conf NanoBSD

Je suis l'heureux possesseur d'une alix 2D3, un temps elle a tournée sous OpenBSD mais quand est venue la carte wifi non supportée j'ai du changer d'OS pour FreeBSD et les drivers madwifi. Un jour j'en ai eu marre de me battre avec mount -uw / et les devices md(4) et j'y ais mis une pfsense, au début l'interface web c'est convi, mais vite on se sent limité, et ne pas éditer mon pf.cont(5) avec vim me rendait malade.

Puis j'apprends qu'il existe un script dans les sources FreeBSD qui permet de faire une FreeBSD pour l'embarqué (ie, petite taille, lecture seule + ram disk). C'est marqué dans le titre, il s'agit de nanobsd(8).

C'est très bien foutu et on est assez émerveillé quand on connait pas les techniques de l'embarqué comme moi.

En gros vous faites un fichier de config en shell, /usr/src/tools/tools/nanobsd/nanobsd.sh se charge de vous compiler le world et le kernel et mettre tout ça dans une image disque.

Il y a beaucoup de paramètres customisables, une simple lecture du script vous les donnera tous. Nanobsd génère 2 images, une image disque complete (.disk.full) et une image avec seulement une partition (.disk.image). L'image complète comporte 3 partitions, les deux premières sont identiques en tailles et contiennent les mêmes données que .disk.image, lors de la première installation vous utilisez .disk.full, et pour mettre à jour vous utilisez _.disk.image à écrire sur la partition que vous n'utilisez pas actuellement (comme ça pas besoin de sortir la carte flash pour mettre à jour).

Nanobsd monte une des deux partitions sur / (en read-only), et deux ram disk pour /etc et /var (/tmp est linké avec /var/tmp, /usr/local/etc sur /etc/local). Pour rendre les changements sur /etc persistants, on utilise la troisième partition qui se monte sur /cfg, tout les fichiers de /cfg sont écrits sur /etc au boot.

Il y a un script simple pour comparer /etc et /cfg pour sauvegarder les changements.

% mount /cfg
% touch /cfg/rc.conf # si le fichier n'existe pas dans /cfg il n'est pas copié
% umount /cfg
% sh /root/save_cfg
/etc/rc.conf --> /cfg/rc.conf
%

Voyez le tgz avec ma config, vous y trouverez la config kernel, la config nanobsd, et les fichiers que j'ai mis dans /usr/src/tools/tools/nanobsd/Files/

Avec en particulier dnsmasq.conf qui bloque au niveau dns les publicités (avec une liste updatée tous les jours).

Mon alix sert donc de routeur, serveur dns/dhcp/impression. Sendmail relaye vers mon serveur interne, et syslog envoie tout à mon serveur.

% cd /usr/src/tools/tools/nanobsd/
% cp ~/solo/alix.cfg .
% cp -r ~/solo/Files/* Files/
% cp ~/solo/SOLO /usr/src/sys/i386/conf/
% sh nanobsd.sh -c alix.cfg
% wait
% qemu -hda /usr/obj/nanobsd.solo/_.disk.full -m 512 -no-acpi -nographic -cpu pentium
# Hmm quelque chose ne va pas, mais j'ai pas envie de rebuild world et/ou kernel
% sh nanobsd.sh -h

Mon script utilise des packages préconstruits, si vous voulez les compiler vous même il y a une fonction pour ça dans nanobsd.sh

Pour finir (ou commencer), voici la doc nanobsd

Voilà, ma config est certainement loin d'être parfaite niveau taille système mais ma carte flash fait 4G (donc 2G utilisable pour le système) et c'est largement suffisant pour faire tourner une FreeBSD "pas trop dépouillée".

Pour mettre à jours (j'ai gzip mon image avant) en supposant que le système tourne sur la partition 1:

% ssh phil@aldo cat /usr/obj/nanobsd.solo/_.disk.image.gz | zcat | sh updatep2
% reboot

J'ai modifié updatep{1|2} en ajoutant gpart set -a active -i PARTITION_INDEX ad0 en raison d'un problème que vous aurez peut être (la nouvelle partition est pas marquée active...)

Si le sujet vous intéresse, il y a un projet linux embarqué sympa qui s'appelle buildroot où là on peut carrément construire sa chaîne de compilation croisée (arm, mips etc), et construire un système embarqué très personalisé. Dommage qu'il n'existe pas un tel projet basé sur le kernel BSD (un jour peut être...)

Upgrader ses packages FreeBSD

date
28 / 12 / 2009
comments
0

J'ai un problème depuis longtemps avec les ports FreeBSD. Dans un premier temps je compilais tout, puis à la longue c'est assez lassant parce que ça prend du temps, des ressources et sur une machine peu puissante c'est pas franchement conseillé. Donc dans la plupart des cas j'utilise maintenant des paquet binaires compilés et distribués par le projet FreeBSD.

Je ne vais pas m'attarder à peser le pour et le contre puisqu'en réalité j'utilise aussi bien les ports que les paquets binaires suivant les dépendances du logiciel et le temps de compilation.

Environnement : FreeBSD 8.0-RELEASE i386 (à vous de changer suivant votre version et votre CPU)

Déjà une bonne chose à savoir :

$ ftp ftp://ftp.fr.freebsd.org/pub/FreeBSD/ports/i386/
....
250-descriptions of each subdirectory.
...
250-packages-*-release directories are built from the ports collection
250-shipped with the release, and are not updated thereafter.
250-
250-packages-*-stable and packages-*-current are updated roughly once a
250-week.
....

Les paquets qui nous intéressent sont donc dans packages-8-stable.

Pour installer un soft, il suffit d'un

# export PACKAGESITE="ftp://ftp.fr.freebsd.org/pub/FreeBSD/ports/i386/packages-8-stable/Latest/"
# pkg_add -r firefox3

Pour mettre à jours c'est nettement moins marrant, surtout si vous utilisez conjointement un arbre des ports à jours dans /usr/ports. Les outils comme portmaster et portupgrade sont assez dur à utiliser pour mettre à jours en se basant non pas sur l'INDEX de /usr/ports mais sur celui du ftp.

D'ailleurs si quelqu'un à déjà réussi à faire proprement la manip je veux bien son /usr/local/etc/pkgtools.conf en commentaire de ce billet.

bapt est en train de porter pkgin sur FreeBSD, mais il n'est pas encore tout à fait prêt (il le sera bientôt j'espère, je ferrai un billet pour l'occasion).

Il y a une solution que j'ai trouvé sympatique en attendant pkgin, c'est les bsdadminscripts avec l'outil pkg_upgrade. C'est du shell, pas très rapide donc, mais au moins ça marche et c'est facile.

# make -C /usr/ports/sysutils/bsdadminscripts install clean

Ensuite c'est enfantin, pour tout mettre à jours et en mode verbeux :

# setenv PACKAGEROOT ftp://ftp.fr.freebsd.org
# setenv BRANCH 8-stable
# pkg_upgrade -av

Et ça marche ! Il se base sur le md5 du fichier INDEX du ftp pour savoir s'il faut mettre à jours sa base de donnée sur les ports puis compare les version installés avec les versions disponibles.

La page de man de pkg_upgrade vous donnera beaucoup d'information sur la manière dont il fonctionne.

Scriptez portable !

date
5 / 9 / 2009
comments
1

Vous avez fait un script pratique que vous partagez avec la communauté mais que ce soit en python, en shell, en perl ou je ne sais quoi encore, sachez que les shebang sous ces formes ne sont pas portables :

#!/bin/bash
#!/bin/zsh
#!/usr/bin/perl
#!/usr/bin/python

En effet, même si ces binaires sont à cet emplacement chez vous, il est possible qu'ils ne le soient pas chez les autres, par exemple les logiciels installées par compilation depuis les ports FreeBSD placent souvent leurs binaires dans /usr/local/bin, il en est de même quand vous faites un ./configure && make && make install. Ainsi si on lance un script mal codé :

./script.sh: bad interpreter: /bin/zsh: no such file or directory

Hum, bon vous me direz, ce n'est pas grave, il suffit de changer la première ligne du script. Mais imaginez un peu le travail à fournir si vous deviez changer cette première ligne sur tous vos script... La meilleure manière de procéder est d'utiliser un shebang portable , et on peut le faire avec la commande env. Exemples de shebang portables :

#!/usr/bin/env bash
#!/usr/bin/env python

env ira chercher le programme là ou il est et l'exécutera. Bon, vous me direz, le problème est le même puisqu'il faut que env soit au même endroit pour tout le monde, ben figurez vous que c'est le cas !! Donc le problème est réglé, et vous pouvez poster votre script sur #freebsd-fr@irc.freenode.org sans vous faire insulter (poil au nez).