martes, 18 de julio de 2017

Escribiendo Scripts para crear un respaldo usando mariadb como base de datos

Hacer un respaldo con mysqldump es algo que mola mucho y va de pasada, pero a veces tenemos un servidor con tantos usuarios y tantas bd que estar creando un script con un comando mysql que por parámetro con la opción '-e' le pasemos las consultas para hacer una query para obtener todas las bd, a veces puede resultar en catastrofes al darnos cuenta que mysqldump no respaldó toda la bd desde hace meses y que no lo ha venido haciendo correctamente, por lo tanto necesitamos un metodo más fiable que haga en verdad su trabajo y es acá donde entra xtrabackup.

Como me gusta trabajar en scripts les comparto un script diseñado por mi para que alivie sus necesidades. Si encuentran algun bug me avisan y recuerden que deben ajustarlo a sus necesidades, aún voy a liberar una nueva versión donde pregunte si el respaldo es nfs o no, si lo es, que se pase por parámetro el ip remoto, por ahora es estático y se debe comentar la parte donde se monta el nfs remoto en caso de no tener uno:

#!/bin/bash

Scripts_path="/Scripts"

femail="webmaster@dominio.ejemplo.test"
temail="soporte@dominio.ejemplo.test" 

#Aqui va el ip del servidor nfs donde se ha de montar el respaldo
ipnfs="192.168.1.20"

dest="/backup"

host="localhost"
port=3306
user="backup"
password="U2FsdGVkX18WWIZ2IUg2hs+E8UWC82jp9jml40ruOJM="

hostname=$(hostname)
tool_mode="xtrabackup"

error_log=/var/log/$(echo $tool_mode)-backupm.log
lock_file=/tmp/$(echo $tool_mode)-backupm.lck

main_path="$dest/$hostname/$tool_mode"

last_full_backup="$main_path/last_full_backup.txt"
incremental_reg_backup="$main_path/incremental_reg_backup.txt"

history_full_backup="$main_path/history_full_backup.txt"
history_incremental_reg_backup="$main_path/history_incremental_reg_backup.txt"

function montar(){
  montado=$(cat /etc/mtab | grep backup | wc -l)
  if let "montado==0";then
    echo "Mounting $ipnfs:/nfs/dbserver on $dest" >> $error_log 2>&1
    mount -t nfs $ipnfs:/nfs/dbserver $dest
  fi
}
function desmontar(){
  montado=$(cat /etc/mtab | grep backup | wc -l)
  if let "montado==1";then
    echo "Unmounting $ipnfs:/nfs/dbserver on $dest" >> $error_log 2>&1
    umount $dest
  fi
}

function chk_files(){
  if [[ ! -d $main_path ]];then
    mkdir -pv $main_path >> $error_log 2>&1
  fi
  if [[ ! -f $last_full_backup ]];then
    echo "Creating $last_full_backup" >> $error_log 2>&1
    > $last_full_backup
  fi
  if [[ ! -f $incremental_reg_backup ]];then
    echo "Creating $incremental_reg_backup" >> $error_log 2>&1
    > $incremental_reg_backup
  fi
}

function do_backup(){

  host=$3
  port=$4
  user=$5
  password=$(echo $6 | openssl enc -aes-128-cbc -a -d -salt -pass pass:wtf)
  
  if [[ ! -f "$lock_file" ]];then
    > $lock_file
  fi

  if [[ "$( cat $lock_file )" != "$(date +%Y%m%d)" ]];then

    ##########################################################
    # Tomamos la fecha y ordenador al que el backup pertenece.

    Yr=$(date +"%Y")
    Mh=$(date +"%m")
    Dy=$(date +"%d")
    day=$(echo "$Yr$Mh$Dy")
    let $folder "folder=(Mh-1)%3"
    echo $day > $lock_file
    echo " day $day --- folder $folder --- month $Mh" >> $error_log 2>&1
    echo "---------------- $(date +"%Y/%m/%d - %H:%M:%S") ----------------" >> $error_log 2>&1

    backup_path="$main_path/monthly/$folder"
    if [[ ! -d $backup_path ]];then
      mkdir -pv $backup_path >> $error_log 2>&1
    fi

    #If force yes then delete
    if [[ "$2" == "1" ]]; then
      if [[ -d $backup_path ]]; then
        rm -rfv $backup_path >> $error_log 2>&1
      fi
    else
      if [[ -d $backup_path ]]; then
        error="La carpeta existe y no se hace nada use la opcion -(f) para forzar el respaldo!\n"
        print "%s" "$error" >> $error_log 2>&1
        print "%s" "$error"
        exit 1
      fi
    fi

    initialt=$(date +"%H:%H:%S")
    if [[ ! -d $dest/files ]];then
      mkdir -pv $dest/files >> $error_log 2>&1
    fi

    echo "---------------- [$day / $initialt] - [Backup of files and Scripts] ----------------" >> $error_log 2>&1

    tar -czpf $dest/files/etc.tar.gz /etc/ >> $error_log 2>&1
    tar -czpf $dest/files/Scripts.tar.gz $Scripts_path >> $error_log 2>&1

    printf "Last Script runned on %d and stored in folder %s\n" "$day" "$backup_path" >> $main_path/date.txt

    echo "---------------- [$day / $initialt] - [Initial Backup] ----------------" >> $error_log 2>&1
    xtrabackup -u $user -p $password --backup --datadir=/var/lib/mysql/ --target-dir=$backup_path >> $error_log 2>&1
    finalt=$(date +"%H:%H:%S")
    echo "---------------- [$day / $finalt] - [Final Backup] ----------------" >> $error_log 2>&1

    cat $last_full_backup >> $history_full_backup
    echo "$day [$initialt - $finalt] | $backup_path" > $last_full_backup

    echo "Cleaning old week backup" >> $error_log 2>&1
    while read weekline;do
      weekfolder=$(echo $weekline | awk '{print $6}' )
      if [ -d $weekfolder ];then
        rm -rfv $weekfolder >> $error_log 2>&1
      fi
    done < $history_incremental_reg_backup

    #Updateing incremental registry
    cat $incremental_reg_backup >> $history_incremental_reg_backup
    > $incremental_reg_backup

    rm $lock_file
  fi
}

> $error_log

option=""
force=1
nfs_option=1

flag1=0
flag2=0

print_usage() {
  backup_path="$main_path/monthly/[0-2]"

  echo "Modo de empleo: $0.sh [-F|--force] [N|--noforce] [-T|--timestamp] [-O|--notimestamp] [-U|--user=\$user] [-P|--port=\$port] [-H|--host=\$host] [-p|--password=\$password] [--no_nfs] [-h|--help]
  Realiza un respaldo completo de la base de datos usando el PATH $backup_path

  Las opciones validas son:
  -F|--force   Forzar la carpeta destino si existe reescribe
  -H|--host=\$host  Nombre del servidor a conectarse para el respaldo (default=dbserver) 
  -N|--noforce   Si la carpeta destino existe no reescribe
  -O|--notimestamp  Escribe directamente en $backup_path sin estampado de tiempo
  -P|--port=\$port  Puerto a conectarse para el respaldo (default=3306)
  -T|--timestamp  Agrega al $backup_path un directorio \$Y-\$m-\$d-\$H-\$M-\$s
  -U|--user=\$user  Usuario con el que se conectara al servidor para hacer el respaldo (default=backup)
  -h|--help   Imprime esta ayuda
  --no_nfs   No intenta montar directorio de respaldo
  -p|--password=\$password Password para la autenticacion.

  Las opciones por defectos son --force y --notimestamp

  OPTIONS: -[F|H|N|O|P|T|U|h|p] 
  " >&2
}
while [[ "$1" != "" ]]; do

  case $1 in
    -F|--force)
      if let "flag1==0";then
        let $force "force=1"
        let $flag1 "flag=1"
      fi
      ;;
    -N|--noforce)
      if let "flag1==0";then
        let $force "force=0"
        let $flag1 "flag=1"
      fi
      ;;
    --no_nfs)
      nfs_option=0
      ;;
    -T|--timestamp)
      if let "flag2==0";then
        option=""
        let $flag2 "flag2=1"
      fi
      ;;
    -O|--notimestamp)
      if let "flag2==0";then
        option="--no-timestamp "
        let $flag2 "flag2=1"
      fi
      ;;
    -U)
      user=$2
      shift
      ;;
    -P)
      port=$2
      shift
      ;;
    -H)
      host=$2
      shift
      ;;
    -p)
      password=$(echo $2 | openssl enc -aes-128-cbc -a -salt -pass pass:wtf)
      shift
      ;;
    --user=*)
      user=$(echo $1 | awk -F= '{print $2}')
      ;;
    --port=*)
      port=$(echo $1 | awk -F= '{print $2}')
      ;;
    --host=*)
      host=$(echo $1 | awk -F= '{print $2}')
      ;;
    --password=*)
      password=$(echo $1 | awk -F= '{print $2}' | openssl enc -aes-128-cbc -a -salt -pass pass:wtf)
      ;;
    -h|\?|--help)
      print_usage
      exit 0
      ;;
    *)
      printf "Comando $1 no reconocido, favor consulte ($0 -h) para ver las opciones aceptadas.\n"
      #print_usage
      exit 1
      ;;
  esac
  shift
done

if [[ "$flag2" == "0" && "$option" == "" ]];then
  option="--no-timestamp"
fi

if let "nfs_option==1";then
  montar
fi
chk_files
do_backup $option $force $host $port $user $password 
#if let "nfs_option==1";then
#  desmontar
#fi