Table des matières

Externalisation des sauvegardes par synchronisation RAID 1 logiciel

Ressources

Une des méthodes les plus simples, efficace et fiable d'externalisation de sauvegarde est via une synchronisation RAID1. Évidement, cela nécessite un volume dédié à BackupPC (ce qui devrait toujours être le cas). Ce volume doit être configuré en RAID1 logiciel, via mdadm, et ce, même si en temps normal le volume ne comporte qu'un seul disque (on peut très bien créé un volume RAID niveau 1 avec un seul disque )

Créer un volume RAID1

Dans cet exemple, nous avons un disque /dev/sdc (qui peut être un disque seul, ou un périphérique RAID géré par une carte matérielle) que nous allons dédier aux sauvegardes. La première étape est de créer un périphérique RAID. Pour cela, il est conseillé de créer une partition recouvrant tout l'espace, de type Linux Raid Auto Detect (type fd)

gdisk /dev/sdc
[...](création d'une partition primaire /dev/sdc1 type fd00)

Maintenant, nous allons créer le volume RAID /dev/md0

mdadm --create /dev/md0 --bitmap=internal --level=1 --force --raid-devices=1 /dev/sdc1

Si nous avons plusieurs disques connecté en permanence pour les sauvegardes, la commande serait

mdadm --create /dev/md0 --bitmap=internal --level=1 --raid-devices=2 /dev/sdc1 /dev/sdd1

La suite est classique, on crée un pv sur /dev/md0, puis un vg, et enfin un lv (en laissant toujours un peu d'espace non alloué au cas où), puis on formate ce lv en xfs, et on le configure pour se monter sur /var/lib/BackupPC.

Repérer les informations des disques d'externalisations

Maintenant, nous voulons externaliser ces sauvegardes sur des disques externe (USB ou eSATA).

Attention: ces disques doivent être de taille identique ou supérieur à notre volume RAID

En tout premier, nous allons créer la partition /dev/sdd1 (partition primaire, qui couvre tout le disque, de type Linux Raid Auto Detect (fd), en utilisant fdisk)

Maintenant, nous allons repérer l'ID de notre disque (ou plutôt de notre partition)

EL5 ou supérieur (valable pour SME8)

ll /dev/disk/by-id/

Ce qui va nous renvoyer quelque chose comme ça:

lrwxrwxrwx 1 root root  9 déc  7 17:31 scsi-3600605b000344d40128c7c6efd3ca27a -> ../../sda
lrwxrwxrwx 1 root root 10 déc  7 17:31 scsi-3600605b000344d40128c7c6efd3ca27a-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 déc  7 17:31 scsi-3600605b000344d40128c7c6efd3ca27a-part2 -> ../../sda2
lrwxrwxrwx 1 root root  9 déc  7 17:31 scsi-3600605b000344d40128c919ff77ef903 -> ../../sdc
lrwxrwxrwx 1 root root 10 déc  7 17:31 scsi-3600605b000344d40128c919ff77ef903-part1 -> ../../sdc1
lrwxrwxrwx 1 root root  9 déc  7 17:31 scsi-3600605b000344d40128f33a6255b7003 -> ../../sdb
lrwxrwxrwx 1 root root  9 déc  8 14:49 scsi-SATA_ST31500341AS_9VS2PNS6 -> ../../sdd
lrwxrwxrwx 1 root root 10 déc  8 15:24 scsi-SATA_ST31500341AS_9VS2PNS6-part1 -> ../../sdd1

Dans notre exemple, c'est /dev/sdd1 la partition qui nous intéresse, donc son ID est scsi-SATA_ST31500341AS_9VS2PNS6-part1 (trop facile, dire que j'ai galéré à essayer de faire ça avec des règles udev à la con ;))

EL4 (valable pour SME7)

Sous CentOS 4, il n'y a pas de /dev/disk/by-id/, il faut donc jouer avec des règles udev pour obtenir l'équivalent (un chemin fixe pour un disque dur donné)

Il faut ajouter une règle de ce type dans /etc/udev/rules.d/10-local_backup.rules

BUS="usb", SYSFS{idProduct}="0606", SYSFS{idVendor}="05e3", KERNEL="sd?1", SYMLINK="backup-raid"

Pour repérer les idProduct et idVendor, on peut utiliser:

udevinfo -a -p $(udevinfo -q path -n /dev/sdd) | grep id

qui devrait donner quelque chose du genre:

    SYSFS{product}=="USB to ATA/ATAPI Bridge"
    SYSFS{idProduct}=="2338"
    SYSFS{idVendor}=="152d"
    SYSFS{idProduct}=="0000"
    SYSFS{idVendor}=="0000"

ici, l'idProduct est 2338 et l'idVendor est 152d

Avec la règle udev créée précédemment, le nom du device pour la synchro raid est /dev/backup-raid

Mettre en place le script d'externalisation

Nous pouvons maintenant mettre en place notre script d'externalisation: (Par exemple dans /usr/local/bin/BackupPC_raidsync)

#!/bin/bash
 
# Read configuration
if [ -e /etc/BackupPC/raidsync.conf ]; then
    . /etc/BackupPC/raidsync.conf
else
    echo "Configuration file /etc/BackupPC/raidsync.conf does not exist"
    exit 1
fi
 
 
 
# Find if one of the removable disk is present
# The first one will be used
for INDEX in $(seq 0 9); do
    if [[ ( -e ${REMOVABLE_DEVICES[INDEX]} && ! -z ${REMOVABLE_DEVICES[INDEX]} ) ]]; then
        REMOVABLE_DEVICE=${REMOVABLE_DEVICES[INDEX]}
        logger "Found device $REMOVABLE_DEVICE"
        break
    fi
done
 
# Check if a lock file exists
# And that the removable device exists
if [ -e $LOCK ]; then
    logger "lock file ($LOCK) exists, a sync may already be running"
    exit 1
elif [ ! -e $REMOVABLE_DEVICE ]; then
    logger "$REMOVABLE_DEVICE dosn't exist, check it's connected"
    exit 1
elif [ -z $REMOVABLE_DEVICE ]; then
    logger "Your remouvable device is not connected on your system"
    exit 1
else
    touch $LOCK
fi
 
RAID_DEVICES=$(/sbin/mdadm --detail $RAID | /bin/grep 'Raid Devices' | /bin/awk '{print $4}')
logger "The array $RAID has $RAID_DEVICES active member(s)"
 
# Grow the RAID device to include one more drive (missing for now)
logger "Growing RAID array $RAID"
/sbin/mdadm --grow $RAID --raid-devices=$(($RAID_DEVICES+1)) --force
 
if [ -e "/sys/block/$(basename $RAID)/queue/max_sectors_kb" ]; then
    # Check the max_sectors_kb before adding the new drive
    OLDSIZE=$(cat /sys/block/$(basename $RAID)/queue/max_sectors_kb)
 
    # Freeze the FS
    /sbin/fsfreeze -f $TOPDIR
fi
 
# Add the removable device to the raid array
logger "Adding $REMOVABLE_DEVICE to RAID array $RAID"
/sbin/mdadm --manage $RAID --add --write-mostly $REMOVABLE_DEVICE
 
if [ -e "/sys/block/$(basename $RAID)/queue/max_sectors_kb" ]; then
    # Now check the new max_sectors_kb
    NEWSIZE=$(cat /sys/block/$(basename $RAID)/queue/max_sectors_kb)
 
    REMOUNT="no"
    # max_sectors_kb has changed ?
    if [ "$OLDSIZE" != "$NEWSIZE" ]; then
        # We need to update the max_sectors_kb of the device backing the filesystem
        # to prevent bio too big errors
        FSDEV=$(basename $(readlink /dev/disk/by-uuid/$(/sbin/blkid $(df -P $TOPDIR | tail -1 | awk '{print $1}') | sed 's/.*UUID="\([a-z0-9-]*\)".*/\1/')))
        FSDEVSIZE=$(cat /sys/block/$FSDEV/queue/max_sectors_kb)
        if [ $FSDEVSIZE -gt $NEWSIZE ]; then
            cat /sys/block/$(basename $RAID)/queue/max_sectors_kb > /sys/block/$FSDEV/queue/max_sectors_kb
            REMOUNT="yes"
        fi
    fi
 
    # Unfreez the FS
    /sbin/fsfreeze -u $TOPDIR
 
    # Remount the FS if needed (not sure if it's really needed)
    if [ "$REMOUNT" == "yes" ]; then
        mount -o remount $TOPDIR
    fi
fi
 
SYNCING=1
 
# Check if the sync is still running
while [ $SYNCING -ne 0 ]; do
    /bin/sleep 60
    SYNCING=$(/sbin/mdadm --detail $RAID | /bin/grep State | /bin/grep -c recovering)
done
 
logger "RAID array $RAID is now fully synced"
 
if [ "$CLEAN_REMOVE" == "yes" ]; then
    # Stop backuppc and wait a few seconds to be sure the fs is clean
    logger "Stopping BackupPC server in order to have a consistent state"
    /sbin/service backuppc stop
    /bin/sync
    /bin/sleep 10
 
    # Now we can umount the volume
    logger "Unmounting $TOPDIR in order to have a consistent state"
    umount $TOPDIR
else
    sync; sync
fi
 
# Mark the disk as faulty
logger "Removing $REMOVABLE_DEVICE from the RAID array $RAID"
/sbin/mdadm --manage $RAID --fail $REMOVABLE_DEVICE
sleep 1
 
# Remove it
/sbin/mdadm --manage $RAID --remove $REMOVABLE_DEVICE
 
# And shrink the raid array so it doesn't report degraded state
# Only use the force flag if $RAID_DEVICES is one
logger "Now shrinking the number of active members of $RAID, so it's not in a degraded state"
FORCE=''
if [ $RAID_DEVICES == 1 ]; then
    FORCE='--force'
fi
/sbin/mdadm --grow $RAID $FORCE --raid-devices=$RAID_DEVICES
 
if [ "$CLEAN_REMOVE" == "yes" ]; then
    # We can remount the volume
    logger "$TOPDIR can be re-mounted"
    mount $TOPDIR
 
    # and restart BackupPC
    logger "And BackupPC server can be restarted"
    /sbin/service backuppc start
fi
 
# Send a mail
if [ ! -z "$MAIL_TO" ]; then
    for M in $MAIL_TO; do
        logger "Sending mail to $M"
        /bin/mail -s 'Externalisation des sauvegardes' $M < /etc/BackupPC/raidsync_mail.txt
    done
fi
 
# Now loop until the link to $REMOVABLE_DEVICE is no longer here (ie. the drive is disconnected)
# This is to prevent the sync to start over and over
logger "Waiting for $REMOVABLE_DEVICE to be removed"
while [ -e $REMOVABLE_DEVICE ]; do
    /bin/sleep 10
done
 
# And remove the lock
logger "Remouving the lock file $LOCK"
/bin/rm -f $LOCK

Puis lui donner les permissions:

chmod +x /usr/local/bin/BackupPC_raidsync

On crée ensuite le fichier de configuration dans /etc/BackupPC/raidsync.conf

# Mount Point for BackupPC volume
# Default: /var/lib/BackupPC
TOPDIR='/var/lib/BackupPC'
 
# Raid device on which BackupPC data are stored
# Example: /dev/md3
RAID='/dev/md3'
 
# Path to the removable block device.
REMOVABLE_DEVICES[0]='/dev/disk/by-id/scsi-SATA_ST31500341AS_9VS2PNS6-part1'
REMOVABLE_DEVICES[1]='/dev/disk/by-id/scsi-SATA_ST31500341AS_9VS2PNS7-part1'
 
# Set this to no if you don't want BackupPC to be stopped while the external device
# is removed from the RAID array
CLEAN_REMOVE="yes"
 
# Path for the lock file
# Default: /var/lock/BkpcRaidSync
LOCK='/var/lock/bkpc-raidsync'
 
# A valid mail adress to send a repport, and ask to remove the drive once the sync
# is completed
# Just comment this line to disable email notification
# You can enter several mail addresses separated by space
# eg: MAIL_TO='admin@domain.com backups@domain.com'
MAIL_TO='admin@domain.com'

En adaptant bien sûre les ID des disques à utiliser, le device md et l'adresse mail de la personne qui recevra les mails d'alertes (ou plutôt de demande d'échange de disque)

Et on place également le fichier text qui contient le corps du mail à envoyer, dans /etc/BackupPC/raidsync_mail.txt

Les sauvegardes ont été copiées sur le support externe. Vous devriez le déconnecter.
L'externalisation se lancera à nouveau dès qu'un support configuré sera connecté.

Ajout d'une tâche cron

Il ne reste plus qu'à appeler ce script régulièrement via une tâche cron. Le script s'occupe de tout (vérification d'une tâche en cours, disque présent etc…), le cron job reste donc très simple:

*/10 * * * * root /usr/local/bin/BackupPC_raidsync 2>&1 > /dev/null

FIXME: Voire si besoin de nettoyer le fichier lock au démarrage de la machine.