LVM (Logical Volume Manager) bietet unter Linux die einfache Möglichkeit flexibel Speicherplatz zur Verfügung zu stellen. Dabei kann LVM sowohl ganze Platten oder auch nur Partitionen verwalten. Diese werden zu sogeannten Volume Groups (VG) zusammengefasst. In diesen VG kann man dann Logical Volume(s) (LV) erstellen, welche dann die Daten aufnehmen können. Diese LV werden als Block Devices zur Verfügung gestellt, damit kann man ihnen alles machen was man sonst mit Block Devices macht z.B. Partitionieren.
Hier soll es um das Backup von LVs gehen, denn dieses kann je nach Anforderung etwas komplizierter werden. Trotzdem wird das Backup durch ein sehr nettes Feature von LVM etwas erleichtert. LVM bietet die Möglichkeit sogeannte Snapshots zu machen, dabei wird ein LV in einem konsistenten Zustand gehalten, damit man davon ein Backup machen kann.
Ein Snapshot des LV meinSystem in der VG vg_system kann man so erstellen
lvcreate -s -L5G -nbackup /dev/vg_system/meinSystem
Das erstellt einen maximal 5GByte grossen Snapshot d.h. wenn sich mehr als 5GByte Daten am Dateisystem des LV verändern wird dieser Snapshot ungültig. Je nachdem wie schnell und wieviel sich Daten im LV verändern muss man diese Grösse anpassen. Wenn man den Snapshot gleich gross setzt wie die Grösse des LV, dann geht der Platz im Snapshot nie aus.
Nun hat man unter /dev/vg_system/backup bzw /dev/mapper/vg_system-backup ein Block Device mit dem Snapshot. Dieses könnte man jetzt z.B. mit einem Tool wie dd in ein Image File kopieren
dd if=/dev/mapper/vg_system-backup of=/pfad/zum/image.img bs=10M lvremove /dev/vg_system/backup
es ist wichtig den Snapshot nach Gebrauch immer zu entsorgen!
Dieses Image kann man leider meist nicht so ohne weiteres mounten v.a. wenn sich darin verschiedene Partitonen befinden. Dazu muss man sich eines Programms wie z.B. kpartx bedienen. Dieses kann aus Image Files die Partitionstabellen rauslesen und die Partitionen einbinden.
kpartx -av /pfad/zum/image.img
. Anhand der Ausgabe von kpartx kann man sehen wo die Devices erstellt wurden z.B. /dev/mapper/vg_system-image1 für die erste Partition. Wenn man das Dateisystem kennt kann man das Ganze dann so mounten
mount -t ext3 /dev/mapper/vg_system-image1 /mnt
Dann kann man in /mnt die Daten z.B. mit rsync oder rdiff-backup sichern.
Alternativ kann man sich den Weg über das Image auch sparen und den Snapshot direkt an kpartx übergeben
kpartx -av /dev/mapper/vg_system-backup
kpartx bindet die gefundenen Partitionen so ein, dass man die nachher mounten kann. Dabei ist die erste Partition des LV im obigen Beispiel /dev/mapper/vg_system-backup1 und die zweite logischerweise vg_system-backup2. kpartx -av gibt genau an welche Partitionen und wie sie erkannt wurden. Um jetzt also die zweite Partition von oben auf /mnt zu mounten, muss man erst wissen welches Dateisystem sich darin befindet. Ich habe bei mir ext3
mount -t ext3 /dev/mapper/vg_system-backup2 /mnt
Jetzt kann man von /mnt z.B. ein Backup mit rsync machen.
Es ist wichtig, dass man zum Schluss (nach dem Backup) folgendes macht:
umount /mnt kpartx -dv /dev/mapper/vg_system-backup lvremove /dev/vg_system/backup
Für das Backup der LVs meiner virtuellen Maschinen habe ich mir folgendes Script geschrieben. Allerdings ist es stark an meine Umgebung angepasst, von daher sollte man das nur als Idee sehen und entsprechend selber anpassen. Das Script erstellt bei mir zuverlässig rdiff-backups aller Partitionen der VMs. Das Script mountet dazu die Partitionen direkt aus dem Snapshot-Device, es wird also kein Image erstellt, sondern es erfolgt ein Filebackup auf Filesystembasis
#!/bin/bash # export path for use the script in cron export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # "hosts" to backup. These are patterns for matching of lv-volumes # it specifies the beginning pattern of the lvm to backup VIRT_HOSTS=' ns3.brain-force.ch mail2.brain-force.ch ' # volume group where $VIRT_HOSTS are situated VG='vg_vms' # basedir for backups BKP_DIR='/backup/backups/filesystems' # enable output to shell by setting to 1 # otherwise it will only be logged to the file VERB=0 # defines the logfile BKP_LOG=/var/log/lvm_bkp.log && touch $BKP_LOG log() { echo "$(date +%d.%m.%y\ %H:%M:%S) "$* >> $BKP_LOG [ $VERB -eq 1 ] && echo "$(date +%d.%m.%y\ %H:%M:%S) "$* } getVolumes() { [ "x$*" = 'x' ] && exit host="$*" disk='' for i in $(lvdisplay | grep \/$host | awk '{print $3}' | grep -v swap) ; do [ "$i" = 'status' ] && continue disk=$disk" "$i done log "Fetched $disk for $host" } createSnapshot() { [ "x$*" = 'x' ] && exit disk="$*" UUID=$(uuid) log "Creating snapshot of volume $disk" && lvcreate -L5G -s -n $UUID $disk >/dev/null && log "Created snapshot volume for $disk of $host as $UUID" } removeSnapshot() { [ "x$UUID" = 'x' ] && exit lvremove -f /dev/$VG/$UUID >/dev/null && log "Snapshot $UUID removed" } runBackup() { log "Getting devices from $UUID for $(basename $i)" kpartx -av /dev/$VG/$UUID > /tmp/part.tmp || exit while read line ; do PART="$(echo $line | awk '{print $3}' | grep -v 2$)" [ "x$PART" = 'x' ] && continue count=${PART: -1} #echo $count && continue FS=$(blkid /dev/mapper/$PART | awk '{print $3}' | awk -F'"' '{print $2}') [ "$FS" = 'swap' ] && continue [ "x$FS" = 'x' ] && continue # echo "/backup/backups/filesystems/$(basename $i)/partition-${count}/" && continue # echo "$PART $FS" && continue log "Mount /dev/mapper/$PART on /mnt" mount -t $FS /dev/mapper/$PART /mnt/ && log "/dev/mapper/$PART sucessfully mounted on /mnt" sleep 1 log "Create backupdir for $(basename $i)" mkdir -p $BKP_DIR/$(basename $i) >/dev/null 2>&1 log "Starting backup for partition $count on $(basename $i)" [ $VERB -eq 1 ] && rdiff-backup -v0 --print-statistics /mnt/ $BKP_DIR/$(basename $i)/partition-${count}/ | tee -a $BKP_LOG [ $VERB -ne 1 ] && rdiff-backup -v0 --print-statistics /mnt/ $BKP_DIR/$(basename $i)/partition-${count}/ >> $BKP_LOG log "Backup finished for partition $count on $(basename $i)" log "Umount /mnt" && umount /mnt && log "Sucessfully umounted /mnt" done </tmp/part.tmp sleep 2 log "Remove partitions from kpartx" kpartx -dv /dev/$VG/$UUID >/dev/null && log "Partitions removed successfully" } log "Backup started" && START=$(date +%s) for host in $VIRT_HOSTS ; do getVolumes $host for i in $disk ; do createSnapshot $i runBackup removeSnapshot done done END=$(date +%s) END=$(($END - $START)) log "Backup finished in "$END" seconds"