KVM Live Backup Script

From InToSSH's Tutorials & Scripts
Revision as of 14:37, 25 September 2019 by Intossh (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This is a simple script to backup live running KVM virtual machines.

No parameters are used as all the variables are already specified in the script. Please go through them and set them as needed.

This script is based on cabel95's gist implementing some of the features from the comments.

How it works

  1. Uses virsh snapshot-create-as to create a snapshot of live VM to freeze the state of the image while copying.
  2. The base image is then copied to the backup folder
  3. The snapshot is merged back to the base image using virsh blockcommit and pivoted to return to base image
  4. If the snapshot is successfully merged, the remaining snapshot file is deleted

Features

  • Uses --quiesce parameter on virsh snapshot-create-as command to assure the consistent state of VHD (requires qemu-ga on guest)
  • Uses --wait on virsh blockcommit command to wait for the current blockjob to finish
  • Logging feature
  • Backs up all the VMs

Requirements

  1. Install qemu-ga (apt install qemu-guest-agent on Debian based distros)
  2. Add virtio device to the VM XML file in the <devices> section
    • <channel type='unix'>
        <source mode='bind' path='/var/lib/libvirt/qemu/f16x86_64.agent'/>
        <target type='virtio' name='org.qemu.guest_agent.0'/>
      </channel>
      

The script

 1 #!/bin/bash
 2 
 3 #To exclude a domain, please add to its name "nobackup"
 4 #First shutdown the guest, then use this command: virsh domrename oldname newname.
 5 
 6 DATE=`date +%Y-%m-%d.%H:%M:%S`
 7 LOG=/var/log/kvm/backup.$DATE.LOG
 8 BACKUPROOT=/nfs/mcz-nas-backup/hal
 9 
10 
11 #DOMAINS=$(virsh list --all | tail -n +3 | awk '{print $2}')
12 
13 DOMAINS=web-server
14 for DOMAIN in $DOMAINS; do
15         echo "-----------WORKER START $DOMAIN-----------" > $LOG
16         echo "Starting backup for $DOMAIN on $(date +'%d-%m-%Y %H:%M:%S')"  >> $LOG
17 
18         if [[ $DOMAIN == *"nobackup"* ]];then
19                 echo "Skipping $DOMAIN , because its excluded." >> $LOG
20                 exit 1
21         fi
22 
23         VMSTATE=`virsh list --all | grep $DOMAIN | awk '{print $3}'`
24         if [[ $VMSTATE != "running" ]]; then
25                 echo "Skipping $DOMAIN , because its not running." >> $LOG
26                 exit 1
27         fi
28 
29         BACKUPFOLDER=$BACKUPROOT/$DOMAIN
30         mkdir -p $BACKUPFOLDER
31         TARGETS=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $3}')
32         IMAGES=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $4}')
33         DISKSPEC=""
34         for TARGET in $TARGETS; do
35                 DISKSPEC="$DISKSPEC --diskspec $TARGET,snapshot=external"
36         done
37 
38         virsh snapshot-create-as --domain $DOMAIN --name "backup-$DOMAIN" --no-metadata --quiesce --atomic --disk-only $DISKSPEC >> $LOG
39         if [ $? -ne 0 ]; then
40                 echo "Failed to create snapshot for $DOMAIN" >> $LOG
41                 exit 1
42         fi
43 
44         for IMAGE in $IMAGES; do
45                 NAME=$(basename $IMAGE)
46                 if test -f "$BACKUPFOLDER/$NAME"; then
47                 echo "Backup exists, merging only changes to image" >> $LOG
48                 rsync -apvz --inplace $IMAGE $BACKUPFOLDER/$NAME >> $LOG
49                 else
50                 echo "Backup does not exist, creating a full sparse copy" >> $LOG
51                 rsync -apvz --sparse $IMAGE $BACKUPFOLDER/$NAME >> $LOG
52                 fi
53 
54         done
55 
56         BACKUPIMAGES=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $4}')
57         for TARGET in $TARGETS; do
58                 virsh blockcommit $DOMAIN $TARGET --active --wait --pivot >> $LOG
59 
60                 if [ $? -ne 0 ]; then
61                         echo "Could not merge changes for disk of $TARGET of $DOMAIN. VM may be in invalid state." >> $LOG
62                         exit 1
63                 fi
64         done
65 
66         for BACKUP in $BACKUPIMAGES; do
67                 if [[ $BACKUP == *"backup-"* ]];then
68 
69                 echo "deleted temporary image $BACKUP" >> $LOG
70                 rm -f $BACKUP
71                 fi
72         done
73 
74         virsh dumpxml $DOMAIN > $BACKUPFOLDER/$DOMAIN.xml
75         echo "-----------WORKER END $DOMAIN-----------" >> $LOG
76         echo "Finished backup of $DOMAIN at $(date +'%d-%m-%Y %H:%M:%S')" >> $LOG
77 done
78 
79 exit 0