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:
BACKUP_PATH="/mnt/backup_vm/kvm" SNAP="/mnt/kvm/.snapshot" DOMAIN=opnsense MAIL_FROM="__EMAIL__" MAIL_TO="__EMAIL__" MAIL_SUBJECT="KVM 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