User Tools

Site Tools


Sidebar

js#vista.png msort nsort

mysql_mariadb_galera:galera_autoxtrabackup

Bash based autoxtrabackup

Back in 2014 Gregory Storme wrote a simple and effective innobackupex based Bash script for automated MySQL/MariaDB/Galera backups. The only other option I have found in a while was a Python script that carries the same name but is a MAJOR pain to install and configure when compared to the simplicity of a Bash script.

Considering the fact that I am quite turned on by simple and elegant solutions, the script from Gregory was the one that caught my eye. Unfortunately, the script is VERY bare bones and offered only 2 features I wanted for my solution. Therefore I made the attempt to get the Python version running.

The Python version offers a lot more flexibility and options but is not an option for us based on the following factors:

1. Only trusted .deb packages are allowed on our servers.
2. We RARELY build our own packages any more.
3. Python, Perl, PHP, Ruby or any other language will NEVER be installed from source on a server.

Due to the fact that I had to build like 2 different .deb packages from source to even get the script working was the automatic out for that solution.

After going back to the Bash version I took a closer look under the hood and noticed it would only take a bit or work to get it very stable, portable and able to offer the features we needed.

So I took a day from my busy schedule and rewrote his script a bit. I hope you find it as useful as we have.

Enjoy

P.S. I am sure someone will find a bug or two so just let me know and I will clear it up.

#!/bin/bash
# Galera MariaDB/MySQL backup script
#
# Copyright (c) 2016 Charles Williams
#       Based on the original work from Gregory Storme
#       Copyright (c) 2014 Gregory Storme
#
# Version: 0.1 beta

backupDir=/var/backups/autoxtrabackup
tmpDir=/tmp
hoursBeforeFull=48
hoursBeforeInc=24
mysqlUser=dbuser
mysqlPwd=password
compression=true
keepDays=7
keepWeeklys=5
keepMonthlys=12
sendEmail=never
emailAddress=
backupLog=$tmpDir/autoxtrabackup.log

#####
# No editing should be required below this line
#####

usage () {
        echo -e " Configuration";
        echo -e "\t/etc/default/autoxtrabackup";
        echo -e "\t\t# Backup directory location
                # Dated folders inside this one will be created
                backupDir=\"/var/backups/autoxtrabackup\"

                # Temp directory
                tmpDir=/tmp

                # Log file
                backupLog=/tmp/autoxtrabackup

                # How many hours between full backups?
                # For example if set to 12 hours and the script runs every hour, the
                # script will create a full backup at 12:00, and create incremental
                # backups until 23:00. At 00:00, it will create the next full backup, and so on.
                # Keep in mind, incremental backups are only applicable for XtraDB/InnoDB tables.
                # If you have MyISAM tables, these will be copied entirely each time
                hoursBeforeFull=72
                hoursBeforeInc=20

                # Username to access the MySQL server. On Debian with mysql packaged installs,
                # you can use the defaults. On other distributions, fill in your MySQL credentials
                mysqlUser=\`grep user /etc/mysql/debian.cnf | tail -n 1 | cut -d\"=\" -f2 | awk '{print $1}'\`
                mysqlPwd=\`grep password /etc/mysql/debian.cnf | tail -n 1 | cut -d\"=\" -f2 | awk '{print $1}'\`

                # Compress the backup or not. Set compress to true/false. Compression is enabled by default
                compression=true

                # Set number of compress threads. Default is 1
                compressThreads=1

                # Number of days, weeks and months to keep backups (includes full and incremental)
                keepDays=7
                keepWeeklys=5
                keepMonthlys=12

                # Send e-mail notifications? Possible values: always, onerror, never
                sendEmail=never

                # Send to which e-mail address
                emailAddress=";
        echo
        echo -e " Restore a full backup";
        echo -e "\tRestore a compressed backup:";
        echo -e "\t\t1: innobackupex --decompress $backupDir/(daily/weekly/monthly)/BACKUP-DIR";
        echo -e "\t\t2: Follow same steps as for non-compressed backups";
        echo -e "\tRestore a non-compressed backup:";
        echo -e "\t\t1: innobackupex --apply-log $backupDir/(daily/weekly/monthly)/BACKUP-DIR";
        echo -e "\t\t2: Stop your MySQL server";
        echo -e "\t\t3: Delete everything in the MySQL data directory (usually /var/lib/mysql)";
        echo -e "\t\t4: innobackupex --copy-back $backupDir/(daily/weekly/monthly)/BACKUP-DIR";
        echo -e "\t\t5: Restore the ownership of the files in the MySQL data directory (chown -R mysql:mysql /var/lib/mysql/)";
        echo -e "\t\t6: Start your MySQL server";
        echo
        echo -e " Restore an incremental backup";
        echo -e "\t1: If compressed, first decompress the backup (see above)";
        echo -e "\t2: First, prepare the base backup";
        echo -e "\t3: innobackupex --apply-log --redo-only $backupDir/(daily/weekly/monthly)/FULL-BACKUP-DIR";
        echo -e "\t4: Now, apply the incremental backup to the base backup.";
        echo -e "\t5: If you have multiple incrementals, pass the --redo-only when merging all incrementals";
        echo -e "\t\texcept for the last one. Also, merge them in the chronological order that the backups were made";
        echo -e "\t6: innobackupex --apply-log --redo-only $backupDir/(daily/weekly/monthly)/FULL-BACKUP-DIR";
        echo -e "\t\t--incremental-dir=$backupDir/(daily/weekly/monthly)/INC-BACKUP-DIR";
        echo -e "\t7: Once you merge the base with all the increments, you can prepare it to roll back the uncommitted transactions:";
        echo -e "\t8: innobackupex --apply-log $backupDir/(daily/weekly/monthly)/BACKUP-DIR";
        echo -e "\t9: Follow the same steps as for a full backup restore now";
}

while getopts ":hv" opt; do
  case $opt in
        h)
                usage;
                exit 0
                ;;
        v)
                set -x;
                ;;
        \?)
                echo "Invalid option: -$OPTARG" >&2
                exit 1
                ;;
  esac
done

if [ -f /etc/default/autoxtrabackup ] ; then
        . /etc/default/autoxtrabackup
else
        echo -e " \"$0 -h\" for help on configuring"
        exit 1
fi

dailyDir=$backupDir/daily
weeklyDir=$backupDir/weekly
monthlyDir=$backupDir/monthly

# Check if innobackupex is installed (percona-xtrabackup)
if [[ -z "$(command -v innobackupex)" ]]; then
        echo "The innobackupex executable was not found, check if you have installed percona-xtrabackup."
        exit 1
fi

# Check if backup directory exists
if [ ! -d "$backupDir" ]; then
        echo "Backup directory does not exist. Check your config and create the backup directory"
        exit 1
fi
if [ ! -d "$dailyDir" ]; then
        mkdir $dailyDir
fi
if [ ! -d "$weeklyDir" ]; then
        mkdir $weeklyDir
fi
if [ ! -d "$monthlyDir" ]; then
        mkdir $monthlyDir
fi

# Check if mail is installed
if [[ $sendEmail == always ]] || [[ $sendEmail == onerror ]]; then
        if [[ -z "$(command -v mail)" ]]; then
                echo "You have enabled mail, but mail is not installed or not in PATH environment variable"
                exit 1
        fi
fi

# Check if you set a correct retention
if [ $(($keepDays * 24)) -le $hoursBeforeFull ]; then
        echo "ERROR: You have set hoursBeforeFull to $hoursBeforeFull and keepDays to $keepDays, this will delete all your backups... Change this"
        exit 1
fi

# If you enabled sendEmail, check if you also set a recipient
if [[ -z $emailAddress ]] && [[ $sendEmail == onerror ]]; then
        echo "Error, you have enabled sendEmail but you have not configured any recipient"
        exit 1
elif [[ -z $emailAddress ]] && [[ $sendEmail == always ]]; then
        echo "Error, you have enabled sendEmail but you have not configured any recipient"
        exit 1
fi

# If compression is enabled, pass it on to the backup command
if [[ $compression == true ]]; then
        compress="--compress"
        compressThreads="--compress-threads=$compressThreads"
else
        compress=
        compressThreads=
fi

dateNow=`date +%Y-%m-%d_%H-%M-%S`
dateNowUnix=`date +%s`
dateTomorrow=`date --date="tomorrow" +%d`
delDay=`date -d "-$keepDays days" +%Y-%m-%d`
delWeek=`date -d "-$keepWeeks weeks" +%Y-%m-%d`
delMonth=`date -d "-$keepMonths months" +%Y-%m-%d`
tomorrow=$(TZ=`date | awk '{print $5}'`-24 date +%d)
weekTomorrow=$((`date -d $dateTomorrow +%V`))
weekNext=$((`date +%V`+1))

# Check if last day of month
if [ $tomorrow == 1 ]; then
        # Do Monthly
        if [ -f "$monthlyDir"/last_monthly ]; then
                lastMonthly=`cat "$monthlyDir"/last_monthly`
        else
                lastMonthly=0
        fi

        # Calculate the time since the last full backup
        difference=$((($dateNowUnix - $lastMonthly) / 60 / 60))

        # Check if we must take a full or incremental backup
        if [ $difference -gt $hoursBeforeFull ]; then
                /usr/bin/innobackupex --user=$mysqlUser --password=$mysqlPwd --no-timestamp $compress $compressThreads --tmpdir=$tmpDir --rsync "$monthlyDir"/"$dateNow" > $backupLog 2>&1

                echo $dateNowUnix > "$monthlyDir"/last_monthly

                # Copy to Weekly
                if [ $weekTomorrow == $weekNext ]; then
                        cp -rfp $monthlyDir"/"$dateNow $weeklyDir
                        echo $dateNowUnix > "$weeklyDir"/last_weekly
                fi

                # Copy to daily
                cp -rfp $monthlyDir"/"$dateNow $dailyDir/$dateNow"_full"
                echo $dateNowUnix > "$dailyDir"/latest_full
        fi
elif [ $weekTomorrow == $weekNext ]; then
        # Do Weekly
        if [ -f "$weeklyDir"/last_weekly ]; then
                lastWeekly=`cat "$weeklyDir"/last_weekly`
        else
                lastWeekly=0
        fi

        # Calculate the time since the last full backup
        difference=$((($dateNowUnix - $lastWeekly) / 60 / 60))

        # Check if we must take a full or incremental backup
        if [ $difference -gt $hoursBeforeFull ]; then
                /usr/bin/innobackupex --user=$mysqlUser --password=$mysqlPwd --no-timestamp $compress $compressThreads --tmpdir=$tmpDir --rsync "$weeklyDir"/"$dateNow" > $backupLog 2>&1
                echo $dateNowUnix > "$weeklyDir"/last_weekly

                # Copy to daily
                cp -rfp $weeklyDir"/"$dateNow $dailyDir/$dateNow"_full"
                echo $dateNowUnix > "$dailyDir"/latest_full
        fi
else
        if [ -f "$dailyDir"/latest_full ]; then
                lastFull=`cat "$dailyDir"/latest_full`
        else
                lastFull=0
        fi
        if [ -f "$dailyDir"/latest_incremental ]; then
                lastInc=`cat "$dailyDir"/latest_incremental`
        else
                lastInc=0
        fi

        # Calculate the time since the last full backup
        differenceFull=$((($dateNowUnix - $lastFull) / 60 / 60))
        differenceInc=$((($dateNowUnix - $lastInc) / 60 / 60))

        # Check if we must take a full or incremental backup
        if [[ $differenceFull -lt $hoursBeforeFull ]] && [[ $differenceInc -gt $hoursBeforeInc ]]; then
                #echo "It's been $difference hours since last full, doing an incremental backup"
                lastFullDir=`date -d@"$lastFull" '+%Y-%m-%d_%H-%M-%S'`
                /usr/bin/innobackupex --user=$mysqlUser --password=$mysqlPwd --no-timestamp $compress $compressThreads --rsync --tmpdir=$tmpDir --incremental --incremental-basedir="$dailyDir"/"$lastFullDir"_full "$dailyDir"/"$dateNow"_incr > $backupLog 2>&1
                echo $dateNowUnix > "$dailyDir"/latest_incremental
        elif [ $differenceFull -gt $hoursBeforeFull ]; then
                #echo "It's been $difference hours since last full backup, time for a new full backup"
                echo $dateNowUnix > "$dailyDir"/latest_full
                /usr/bin/innobackupex --user=$mysqlUser --password=$mysqlPwd --no-timestamp $compress $compressThreads --tmpdir=$tmpDir --rsync "$dailyDir"/"$dateNow"_full > $backupLog 2>&1
        fi
fi


# Check if the backup succeeded or failed, and e-mail the logfile, if enabled
if grep -q "completed OK" $backupLog; then
        #echo "Backup completed OK"
        if [[ $sendEmail == always ]]; then
                cat $backupLog | mail -s "AutoXtraBackup log" $emailAddress
        fi
else
        #echo "Backup FAILED"
        if [[ $sendEmail == always ]] || [[ $sendEmail == onerror ]]; then
                cat $backupLog | mail -s "AutoXtraBackup log" $emailAddress
        fi
        exit 1
fi

# Delete backups older than retention date
rm -rf $dailyDir/$delDay*
rm -rf $weeklyDir/$delWeek*
rm -rf $monthlyDir/$delMonth*

# Delete incremental backups with full backup base directory that was deleted
for i in `find "$dailyDir"/*incr -type f -iname xtrabackup_info 2>/dev/null |  xargs grep $delDay | awk '{print $10}' | cut -d '=' -f2`; do rm -rf $i; done

exit 0
mysql_mariadb_galera/galera_autoxtrabackup.txt · Last modified: 2020/02/24 11:16 (external edit)