Table of Contents

Sauvegarde KVM à chaud par Snap

Le but est de minimiser les temps d'arrêt application des VM pour faire un backup, voici les étapes.

J'ai essayé de rendre un peu plus robuste le script, il reste peut être des coquilles. J'utilise une debian 12.12 à ce jour.

Pour lancer un backup, lancer le script avec en paramètre le fichier environnement.

Par exemple:

Fichier environnement

BACKUP_PATH="/mnt/backup_vm/kvm"
SNAP="/mnt/kvm/.snapshot"
DOMAIN=opnsense
MAIL_FROM="__EMAIL__"
MAIL_TO="__EMAIL__"
MAIL_SUBJECT="KVM backup"

Script de backup

#!/bin/bash
#

HOT=1 #backup a chaud

if [ $# -ne 1 ]; then
        echo "Arguments error"
        exit 1
fi

if [ ! -f "$1" ]; then
    echo "File '$1' does not exist"
    exit 1
fi

source $1

if [ ! -d $BACKUP_PATH ]; then
    echo "Directory '$BACKUP_PATH' does not exist "
    exit 1
fi

if [ ! -d $SNAP ]; then
    echo "Directory '$SNAP' does not exist "
    exit 1
fi

virsh domuuid $DOMAIN
if [ $? -ne 0 ]; then
    echo "Domain '$DOMAIN' does not exist "
    exit 1
fi

if [ ! -d $BACKUP_PATH/$DOMAIN ]; then
        echo "Directory '$BACKUP_PATH/$DOMAIN' does not exist "
        echo "Create directory $BACKUP_PATH/$DOMAIN"
        mkdir $BACKUP_PATH/$DOMAIN
fi

#check if domain is running
if [ "$( LANG=C virsh domstate $DOMAIN )" != "running" ]; then
    echo "Domain $DOMAIN not start - Cold backup mode"
    HOT=0 #on backup a froid
fi


EXTENSION=$(virsh domblklist "$DOMAIN" --details | awk '$2=="disk" {n=split($4,a,"."); print a[n]}')
BACKUPDATE=$(date "+%Y-%m-%d.%H%M%S")
TARGET=$(virsh domblklist "$DOMAIN" --details | awk '$2=="disk" {print $3}')
DISK=$(virsh domblklist "$DOMAIN" --details | awk '$2=="disk" {print $4}')
SNAPFILE=${SNAP}/${DOMAIN}-snapshot-${BACKUPDATE}.${EXTENSION}
BACKUP_DOMAIN=${BACKUP_PATH}/${DOMAIN}
DISKSPEC="--diskspec ${TARGET},file=${SNAPFILE},snapshot=external"
MESSAGE=""
STATUS=""

send_mail()
{
  printf "From: $MAIL_FROM\nTo: $MAIL_TO\nSubject: $MAIL_SUBJECT : $STATUS\n\n$MESSAGE\n" | msmtp $MAIL_TO
}

echo "creation du repertoire de sauvegarde ${BACKUP_DOMAIN}"
#creation du repertoire de sauvegarde
mkdir -p ${BACKUP_DOMAIN}
virsh dumpxml ${DOMAIN} > ${BACKUP_DOMAIN}/${BACKUPDATE}-${DOMAIN}.xml
if [ $? -ne 0 ]; then
    echo "Failed to dumpxml $DOMAIN"
    MESSAGE="Failed to dumpxml $DOMAIN"
    STATUS="ERROR"
    send_mail
    exit 1
fi

if [ "$HOT" -eq 1 ]; then
        echo "creation du snap ${DOMAIN}-snapshot-${BACKUPDATE}"
        #creation du snap
        virsh snapshot-create-as --domain ${DOMAIN} --name ${DOMAIN}-snapshot-${BACKUPDATE} --no-metadata --atomic --disk-only ${DISKSPEC}
        if [ $? -ne 0 ]; then
                echo "Failed to create snapshot for $DOMAIN"
                MESSAGE="Failed to create snapshot for $DOMAIN"
                STATUS="ERROR"
                send_mail
                exit 1
        fi
fi

# On affiche le disk actif en écriture pour la vm, le snap normalement
virsh domblklist ${DOMAIN}

echo "copy ${DISK} to ${BACKUP_PATH}/${DOMAIN}/${BACKUPDATE}-${DOMAIN}.${EXTENSION}"
#copy
#cp ${DISK} ${BACKUP_PATH}/${DOMAIN}/${BACKUPDATE}-${DOMAIN}.${EXTENSION}
_FILE=${BACKUP_PATH}/${DOMAIN}/${BACKUPDATE}-${DOMAIN}.${EXTENSION}
#zpaq a ${_FILE}.zpaq ${DISK} -m2
tar --zstd -cvf ${_FILE}.tar.zstd ${DISK}
if [ $? -ne 0 ]; then
    echo "Failed to copy ${DISK} to ${_FILE}"
    MESSAGE="Failed to copy ${DISK} to ${_FILE}"
    STATUS="ERROR"
    send_mail
    exit 1
fi

if [ "$HOT" -eq 1 ]; then
        echo "retour sur snap blockcommit ${DOMAIN}"
        #On arrêt le snap pour revenir écrire sur le disk d'origine
        virsh blockcommit ${DOMAIN} ${TARGET} --active --pivot
        if [ $? -ne 0 ]; then
                echo "Failed to blockcommit snapshot for $DOMAIN"
                MESSAGE="Failed to blockcommit snapshot for $DOMAIN"
                STATUS="ERROR"
                send_mail
                exit 1
        fi
fi

# On affiche le disk actif en écriture pour la vm, le disk d'origine
virsh domblklist ${DOMAIN}

echo "delete snap file $SNAPFILE"
#on peut delete le snap
if [ "$HOT" -eq 1 ]; then
        rm $SNAPFILE
fi

#test de l'archive
#zpaq x ${_FILE}.zpaq -test
tar --zstd -tvf ${_FILE}.tar.zstd
if [ $? -ne 0 ]; then
    echo "Failed : archive test ${_FILE}"
    MESSAGE="Failed test file : {_FILE}"
    STATUS="ERROR"
    send_mail
    exit 1
fi

MESSAGE="${MAIL_SUBJECT} [OK] Domain: $DOMAIN"
STATUS="OK"
send_mail

echo "[OK] - Status - Backup completed"
exit