Dienstag, 2. Mai 2023

HowTo - live backup und restore einer virtuellen Maschine mit libvirt

Einleitung

Das Management User Interface libvirt steuert QEMU welches auf KVM aufsetzt. Die Basis für dieses Backup/Restore bildet die Verkettung von Diskimages.

Da hier und in der restlichen Dokumentation nichts über ein restore zu finden ist und das backup bekanntlich erst nach einem restore erfolgreich sein kann - habe ich ein kurzes HowTo geschrieben. Das ist noch nicht perfekt! 

Bitte am Ende den Abschnitt ToDo beachten!

Wem das zu sehr ins Detail geht, es gibt auf GitHub auch ein installierbares Tool welches die API von libvirt nutzt.

Vorbereitung

  1. Der qemu-guest-agent muss im Gast System installiert werden, damit beim Backup das Filesystem im Gast kurz angehalten werden kann.
  2. Um die Befehle universell zu halten habe ich ein paar Variablen definiert.
  3. Zur Steuerung des Backup sind ein paar XML Dateien notwendig, die werden mit einem kurzen Script erzeugt. (siehe weiter unten im Artikel)

Eine offline Sicherung (virsh shutdown ...) kann man durch Kopie des Diskimages machen, z.B erste Disk von win2k22 nach ./backup kopieren:

sudo cp $(virsh domblklist win2k22|awk 'NR >2 && NR < 4 {print $2}') ./backup/ 

Das im weiteren beschriebene Verfahren funktioniert nur bei laufender VM. 

Die folgenden Variablen werden in allen folgenden Befehlsfolgen als Grundlage benötigt! Der Pfad zum backup Verzeichnis muss absolut sein!
export vm=ubuntu22.04
export path2backup=/backup                   # mkdir -p $path2backup
export cpname=FirstCheckPoint
export dev=$(virsh domblklist ${vm}|awk 'NR >2 && NR < 4 {print $1}') 
export disk=$(virsh domblklist ${vm}|awk -F\/ 'NR >2 && NR < 4 {print $NF}') 
bash createxml.sh 

Full Backup

Mit zwei Befehlszeilen sichert man die Definition und erzeugt eine Kopie des Diskimages.

virsh dumpxml ${vm} >${path2backup}/${vm}.xml
virsh backup-begin ${vm} b-${vm}.xml

Ein restore ist ziemlich simpel, man holt das Diskimage aus dem backup, definiert die Maschine neu und startet wieder.

sudo cp $(awk -F\' '/target file/{print $2}' b-${vm}.xml) /var/lib/libvirt/images/${disk}
virsh define ${path2backup}/${vm}.xml
virsh start ${vm}

Differential Backup

Man setzt den Startpunkt für die "Differenz" mit einem Fullbackup und einem Checkpoint.

virsh backup-begin ${vm} b-${vm}.xml c-${vm}.xml 

Im weiteren Verlauf wird dann jedes mal der Unterschied zum Checkpoint gesichert.

virsh backup-begin ${vm} i-${vm}.xml

Für ein Restore muss das Fullbackup und die letzte Differenz aus dem Backup geholt werden. Die gesicherte Definition wird modifiziert, so das eine Disk Image Kette erzeugt wird. Gestartet wird die Virtuelle Maschine vom Overlay. Nach erfolgreichem Start kann das Overlay mit dem Backing Image wieder verschmolzen werden.

sudo cp $(awk -F\' '/target file/{print $2}' b-${vm}.xml) /var/lib/libvirt/images/${disk}
sudo cp $(awk -F\' '/target file/{print $2}' i-${vm}.xml) /var/lib/libvirt/images/
virsh define ${path2backup}/${vm}.xml
virsh update-device ${vm} d-${vm}.xml
virsh start ${vm}
virsh blockcommit ${vm} ${dev} --active --verbose --pivot

Dies und das

Für den Test muss man die VM nicht zerstören, ein shutdown vor dem Restore reicht. 

Der Befehl backup-begin endet sofort und der Prozess läuft im Hintergrund. Um diesen abzufragen beziehungsweise in einem Script darauf zu warten, gibt es folgende Befehle:

virsh domjobinfo ${vm} --completed
virsh event ${vm} job-completed 

Will man eine VM mit einem Checkpoint entfernen, sollte man zuerst den Checkpoint entfernen. Hat man experimentiert und es existiert ein checkpoint der nicht im Image zu finden ist, kann man die Option --metadata beim delete Befehl angeben.

virsh checkpoint-list ${vm}
virsh checkpoint-delete ${vm} ${cpname}
virsh shutdown ${vm}                    # oder virsh destroy ${vm}
virsh undefine ${vm}

Will man bei backup-begin die Dateien immer wieder überschreiben, muss man einen zusätzlichen Parameter verwenden: --reuse-external

Man kann das Backup noch packen und mit einem Zeitstempel versehen. 

target=$(awk -F\' '/target file/{print $2}' b-${vm}.xml)
gzip -c ${target} > ${target}-$(date "+%F-%s").gz

Script

Der Inhalt der Script Datei createxml.sh

cat <<EOI > b-${vm}.xml
<domainbackup>
  <disks>
    <disk name='${dev}' type='file'>
      <driver type='qcow2'/>
      <target file='${path2backup}/${disk}.backup'/>
    </disk>
    #<disk name='sda' backup='no'/>
  </disks>
</domainbackup>
EOI

cat <<EOI > i-${vm}.xml
<domainbackup>
  <incremental>${cpname}</incremental>
  <disks>
    <disk name='${dev}' type='file'>
      <driver type='qcow2'/>
      <target file='${path2backup}/${disk}.backup.inc'/>
    </disk>
    #<disk name='sda' backup='no'/>
  </disks>
</domainbackup>
EOI

cat <<EOI > c-${vm}.xml
<domaincheckpoint>
 <name>${cpname}</name>
  <description>Completion of updates after OS install</description>
  <disks>
    <disk name='${dev}' checkpoint='bitmap'/>
    #<disk name='sda' checkpoint='no'/>
  </disks>
</domaincheckpoint>
EOI

cat <<EOI > d-${vm}.xml
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2' discard='unmap'/>
      <source file='/var/lib/libvirt/images/${disk}.backup.inc' index='2'/>
      <backingStore type='file'>
         <format type='qcow2'/>
         <source file='/var/lib/libvirt/images/${disk}'/>
      </backingStore>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
    </disk>
EOI

ToDo

Das Ganze ist noch nicht komplett und funktioniert so nur für virtio Festplatten (vda). 

Code Block

text

Keine Kommentare:

Kommentar veröffentlichen