Backups con restic y notificaciones en Telegram
2024-05-27
Suelo utilizar restic para realizar los backups de mis datos, sobre todo por su integración con servicios en la nube a través de rclone. Escribí una serie de artículos donde enseñaba sus principales características y su uso, si estas interesad@ los puedes leer en este enlace
A raíz de migrar mi servidor a unRaid, observé que muchos usuarios utilizan borg backup y borgmatic para realizar sus backups, al menos entre la comunidad de unRaidES, y también es muy popular un script realizado por el usuario @carpediem
Bien, pues tomando como base dicho script lo he adaptado para restic y mis necesidades.
Básicamente a este script se le pasa un fichero con una lista de directorios y/o ficheros ha salvaguardar y se realiza una copia en un pool de discos, en raid 1, que tengo exclusivamente para almacenar backups.
También se realiza el versionando (prune) y si es la primera semana del mes, se realiza un chequeo del repositorio.
Para finalizar se envía una notificación a un bot de Telegram, que previamente debes de haber creado, como es obvio, con el resultado del script y se adjunta un fichero con el log del mismo.
El script completo
#!/bin/sh
## Telegram config
chatid="xxxxxxx" #chatid is telegram chatid to send notifications
api="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" #api is telegram bot's token.
## Set current Date
datelog=`date +%Y-%m-%d`
#Log file to store restic output
log="/tmp/"$datelog"_restic_backup.log"
## Restic config
files="/mnt/user/datos/scripts/files_to_backup.txt" #files to backup
restic_repo="/mnt/backups/repositorio_restic/unraid/" #repository location
file_password='/mnt/user/datos/scripts/pass.txt' #file with repository password
#check if files exist
while IFS='' read -r line || [[ -n "$line" ]]; do
if [[ ! -d $line ]] && [[ ! -f $line ]]
then
echo -e "ERROR: $line no existe" >> $log
global_exit=1
backup_re="ERROR in $files, view log file"
fi
done < $files
#if error in files send telegram message and exit
if [[ $global_exit -eq 1 ]]
then
curl -s \
--data parse_mode=HTML \
--data chat_id=$chatid \
--data text="<b>Restic Backup</b>%0A <i>Repo:</i> NAS01->Unraid <i>Tarea:</i> <b>Backup</b>%0A <i>Estado:</i> ERROR in $files" \
"https://api.telegram.org/bot$api/sendMessage"
curl -v -4 -F \
"chat_id=$chatid" \
-F document=@$log \
-F caption="Log $datelog.log" \
https://api.telegram.org/bot$api/sendDocument 2> /dev/null
rm $log
exit
fi
echo -e "===================== Iniciando backup con fecha: $datelog ===============" > $log
# some helpers and error handling:
info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; }
trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM
# Time count start
timestart=`date +%s`
# Notification to Telegram (Start Backup)
curl -s \
--data parse_mode=HTML \
--data chat_id=$chatid \
--data text="<b>Restic Backup $tag</b>%0A <i>Repo:</i> NAS01->Unraid <i>Tarea:</i> <b>Backup</b>%0A <i>Estado:</i> Iniciando Backup" \
"https://api.telegram.org/bot$api/sendMessage"
# Running restic:
restic -r $restic_repo \
--password-file $file_password \
-v backup \
--files-from $files \
>> $log
backup_exit=$?
if [ $backup_exit -eq 0 ]; then backup_re="Backup correcto"
elif [ $backup_exit -eq 3 ]; then backup_re="Backup completado pero con advertencias"
else backup_re="ERROR EN BACKUP"
fi
echo -e "\n============================ Pruning repository =======================\n" >> $log
# Use the `prune` subcommand to maintain last 7 daily, 3 weekly, 12 monthly and
# 3 years archives of THIS machine.
restic -r $restic_repo \
--password-file $file_password \
-v forget \
--keep-daily 7 \
--keep-weekly 3 \
--keep-monthly 12 \
--keep-yearly 3 \
--prune \
>> $log
prune_exit=$?
if [ $prune_exit -eq 0 ]; then prune_re="Prune correcto"
elif [ $prune_exit -eq 3 ]; then prune_re="Prune completado pero con advertencias"
else prune_re="ERROR EN PRUNE"
fi
# use highest exit code as global exit code
global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit ))
if [ ${global_exit} -eq 0 ]; then
echo -e "\nBackup and Prune finished successfully\n" >> $log
elif [ ${global_exit} -eq 3 ]; then
echo -e "\nBackup and/or Prune finished with warnings\n" >> $log
else
echo -e "\nBackup and/or Prune finished with errors\n" >> $log
fi
# Time count stop
timestop=`date +%s`
# Total execution time (in seconds)
runtime=$(( timestop - timestart ))
# Total execution time conversion
D=$((runtime/60/60/24))
H=$((runtime/60/60%24))
M=$((runtime/60%60))
S=$((runtime%60))
totaltime="${D}d, ${H}h, ${M}m, ${S}s"
# Notification to Telegram (End Backup + Send Log)
curl -s \
--data parse_mode=HTML \
--data chat_id=$chatid \
--data text="<b>Restic Backup </b>%0A <i>Repo:</i> NAS01->unraid%0A <i>Tarea:</i> <b>Backup</b>%0A <i>Tiempo total:</i> $totaltime%0A <i>Estado:</i> $backup_re , $prune_re " \
"https://api.telegram.org/bot$api/sendMessage"
curl -v -4 -F \
"chat_id=$chatid" \
-F document=@$log \
-F caption="Log $tag: $datelog.log" \
https://api.telegram.org/bot$api/sendDocument 2> /dev/null
# Checks if it's the first week of month, and perform repository check if so.
dia=`date +%d`
if [ "$dia" -ge 1 ] && [ "$dia" -le 7 ]; then # Si el numero del dia esta entre 1 y 7 (primera semana)
curl -s \
--data parse_mode=HTML \
--data chat_id=$chatid \
--data text="<b>Restic Backup</b>%0A <i>Repo:</i> NAS01->unraid%0A <i>Tarea:</i> <b>Check</b>%0A <i>Estado:</i> Iniciando Check del Repositorio" \
"https://api.telegram.org/bot$api/sendMessage"
echo "================= Primera semana del mes. Iniciando Check =================" >> $log
restic -r $restic_repo \
--password-file $file_password \
-v check \
>> $log
check_exit=$?
if [ $check_exit -eq 0 ]; then check_re="Check correcto"
elif [ $check_exit -eq 3 ]; then check_re="Check completado pero con advertencias"
else check_re="ERROR EN CHECK"
fi
# Notification to Telegram (End Check)
curl -s \
--data parse_mode=HTML \
--data chat_id=$chatid \
--data text="<b>Restic Backup</b>%0A <i>Repo:</i> NAS01->unraid%0A <i>Tarea:</i> <b>Check del repositorio</b>%0A <i>Estado:</i> $check_re" \
"https://api.telegram.org/bot$api/sendMessage"
echo "=========================== FINALIZANDO =============================================" >> $log
exit 0
fi
# If it's not the first week of month, don't perform repository check.
echo "=========================== FINALIZANDO ================================================" >> $log
exit ${global_exit}
Seguro que este script es mejorable, pero hace lo que quiero y lo hace bien 😎
Espero que te haya gustado, pasa un gran día 🐧