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 🐧


Ingrese la dirección de su instancia