Fork and upgrade script
This commit is contained in:
parent
6dcb64f6b2
commit
9b5db8f9d1
258
mysqlbackup.sh
258
mysqlbackup.sh
|
@ -1,126 +1,226 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
|
||||
## Initial author : omegazeng
|
||||
## Fork : https://github.com/omegazeng/run-mariabackup
|
||||
## author : Dryusdan
|
||||
## date : 15/02/2020
|
||||
## date : 03/10/2023
|
||||
## description : A MySQL dumper
|
||||
## usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST HEALTCHECK_UUID
|
||||
|
||||
## Bash strict mode ####################################
|
||||
### Bash strict mode ####################################
|
||||
set -o errexit # abort on nonzero exitstatus
|
||||
set -o pipefail # don't hide errors within pipes
|
||||
|
||||
#set -o nounset # abort on unbound variable
|
||||
|
||||
## Logs ################################################
|
||||
### Logs ################################################
|
||||
readonly SCRIPTNAME="$(basename "$0")"
|
||||
info() { echo -e "[INFO] $* " | logger --tag "${SCRIPTNAME}" ; }
|
||||
warning() { echo -e "[WARNING] $* " | logger --tag "${SCRIPTNAME}" ; }
|
||||
error() { echo -e "[ERROR] $* " | logger --tag "${SCRIPTNAME}" ; }
|
||||
fatal() { echo -e "[FATAL] $* " | logger --tag "${SCRIPTNAME}" ; exit 1 ; }
|
||||
########################################################
|
||||
|
||||
## Define variables ###################################
|
||||
info "Define variables"
|
||||
DATE=$(date '+%s')
|
||||
#########################################################
|
||||
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
fatal "No arguments supplied. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
fatal "No arguments supplied. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
fi
|
||||
|
||||
if [ -z "${1}" ];
|
||||
then
|
||||
fatal "Folder is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
fatal "Folder is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
else
|
||||
FOLDER="${1}/${DATE}"
|
||||
FOLDER_WITHOUT_DATE="${1}/"
|
||||
BACKDIR="${1}/"
|
||||
fi
|
||||
|
||||
if [ -z "${2}" ];
|
||||
then
|
||||
fatal "Retention is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
fatal "Username is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
else
|
||||
RETENTION="${2}"
|
||||
MYSQL_USER=${2}
|
||||
fi
|
||||
|
||||
if [ -z "${3}" ];
|
||||
then
|
||||
fatal "Username is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
fatal "Password is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
else
|
||||
USERNAME=${3}
|
||||
MYSQL_PASSWORD=${3}
|
||||
fi
|
||||
|
||||
if [ -z "${4}" ];
|
||||
then
|
||||
fatal "Password is not defined. Usage : ./mysqlbackup.sh /BASE/BACKUP/FOLDER RETENTION USERNAME PASSWORD HOST PORT"
|
||||
warning "Host not defined, use 127.0.0.1 by default"
|
||||
MYSQL_HOST="127.0.0.1"
|
||||
else
|
||||
PASSWORD=${4}
|
||||
MYSQL_HOST=${4}
|
||||
fi
|
||||
|
||||
if [ -z "${5}" ];
|
||||
then
|
||||
warning "Host not defined, use 127.0.0.1 by default"
|
||||
HOST="127.0.0.1"
|
||||
warning "Port not defined, use 3306 by default"
|
||||
MYSQL_PORT="3306"
|
||||
else
|
||||
HOST=${5}
|
||||
MYSQL_PORT=${5}
|
||||
fi
|
||||
|
||||
if [ -z "${6}" ];
|
||||
then
|
||||
warning "Port not defined, use 3306 by default"
|
||||
PORT="3306"
|
||||
else
|
||||
PORT=${6}
|
||||
fi
|
||||
|
||||
if [ -z "${7}" ];
|
||||
then
|
||||
HC=false
|
||||
else
|
||||
HC=true
|
||||
HC_UUID="${7}"
|
||||
fi
|
||||
|
||||
## Run dump ######################################
|
||||
info "Create backup folder"
|
||||
mkdir -p ${FOLDER}/{databases,schemas,datas,extras}/
|
||||
info "Dumping databases"
|
||||
curl --silent --retry 3 "https://cron.dryusdan.net/ping/${HC_UUID}/start" > /dev/null
|
||||
for dbname in $(/usr/bin/mysql --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} -N -e "show databases" | grep -v "information_schema" | grep -v "mysql" | grep -v "performance_schema")
|
||||
for bin in mariabackup gzip mbstream
|
||||
do
|
||||
info "Dumping ${dbname}"
|
||||
mkdir -p ${FOLDER}/{schemas,datas}/${dbname}
|
||||
info "Dumping ${dbname} schema"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-data ${dbname} | gzip > ${FOLDER}/databases/${dbname}.sql.gz
|
||||
info "Dumping events, routines, triggers of ${dbname}"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-data --no-create-info --routines --triggers --events ${dbname} | gzip > ${FOLDER}/extras/${dbname}.sql.gz
|
||||
info "Know engine of database"
|
||||
## If all tables use InnoDB engine, we use --single-transaction
|
||||
## Also, if one or more table use MyISAM engine, we need lock all tables
|
||||
if /usr/bin/mysql --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} -N -e "select engine from information_schema.tables where table_schema = '${dbname}'" | grep "MyISAM" -q;
|
||||
then
|
||||
warning "${dbname} have a table who using MyISAM engine."
|
||||
info "Dumping with lock"
|
||||
for tablename in $(/usr/bin/mysql --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} -N -e "show tables" ${dbname})
|
||||
do
|
||||
info "Dumping ${tablename}'s schema of ${dbname}"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-data --add-locks --add-drop-table ${dbname} ${tablename} | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' | gzip > ${FOLDER}/schemas/${dbname}/${tablename}.sql.gz
|
||||
info "Dumping ${tablename}'s data of ${dbname}"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-create-info --add-locks --extended-insert=FALSE ${dbname} ${tablename} | gzip > ${FOLDER}/datas/${dbname}/${tablename}.sql.gz
|
||||
done
|
||||
else
|
||||
info "Dumping with single transaction"
|
||||
for tablename in $(/usr/bin/mysql --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} -N -e "show tables" ${dbname})
|
||||
do
|
||||
info "Dumping ${tablename}'s schema of ${dbname}"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-data --skip-lock-tables --add-drop-table ${dbname} ${tablename} | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' | gzip > ${FOLDER}/schemas/${dbname}/${tablename}.sql.gz
|
||||
info "Dumping ${tablename}'s data of ${dbname}"
|
||||
/usr/bin/mysqldump --user=${USERNAME} --password=${PASSWORD} --host=${HOST} --port=${PORT} --no-create-info --skip-lock-tables --single-transaction --extended-insert=FALSE ${dbname} ${tablename} | gzip > ${FOLDER}/datas/${dbname}/${tablename}.sql.gz
|
||||
done
|
||||
fi
|
||||
if ! command -v ${bin} &> /dev/null
|
||||
then
|
||||
fatal "${bin} could not be found"
|
||||
fi
|
||||
done
|
||||
info "Backup is done"
|
||||
curl --silent --retry 3 "https://cron.dryusdan.net/ping/${HC_UUID}/" > /dev/null
|
||||
info "Removing old backup"
|
||||
find ${FOLDER_WITHOUT_DATE} -mtime +${RETENTION} -exec rm -rf {} \;
|
||||
info "Removing done"
|
||||
|
||||
# Create a backup user
|
||||
# CREATE USER 'backup'@'localhost' IDENTIFIED BY 'YourPassword';
|
||||
# MariaDB < 10.5:
|
||||
# GRANT RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backup'@'localhost';
|
||||
# MariaDB >= 10.5:
|
||||
# GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR ON *.* TO 'backup'@'localhost';
|
||||
# FLUSH PRIVILEGES;
|
||||
#
|
||||
# Usage:
|
||||
# MYSQL_PASSWORD=YourPassword bash run-mariabackup.sh
|
||||
|
||||
BACKCMD="$(command -v mariabackup)" # Galera Cluster uses mariabackup instead of xtrabackup.
|
||||
GZIPCMD="$(command -v gzip)" # pigz (a parallel implementation of gzip) could be used if available.
|
||||
STREAMCMD="$(command -v mbstream)" # sometimes named mbstream to avoid clash with Percona command
|
||||
FULLBACKUPCYCLE=604800 # Create a new full backup every X seconds (7 daysà
|
||||
KEEP=3 # Number of additional backups cycles a backup should be kept for.
|
||||
LOCKDIR=/tmp/mariabackup.lock
|
||||
|
||||
ReleaseLockAndExitWithCode () {
|
||||
if rmdir "${LOCKDIR}"
|
||||
then
|
||||
info "Lock directory removed"
|
||||
else
|
||||
info "Could not remove lock dir" >&2
|
||||
fi
|
||||
exit "${1}"
|
||||
}
|
||||
|
||||
GetLockOrDie () {
|
||||
if mkdir "${LOCKDIR}"
|
||||
then
|
||||
info "Lock directory created"
|
||||
else
|
||||
error "Could not create lock directory ${LOCKDIR}"
|
||||
fatal "Is another backup running?"
|
||||
fi
|
||||
}
|
||||
|
||||
USEROPTIONS="--user=${MYSQL_USER} --password=${MYSQL_PASSWORD} --host=${MYSQL_HOST} --port=${MYSQL_PORT}"
|
||||
# Arguments may include amongst others:
|
||||
# --parallel=2 => Number of threads to use for parallel datafiles transfer. Default value is 1.
|
||||
# --galera-info => Creates the xtrabackup_galera_info file which contains the local node state
|
||||
# at the time of the backup. Option should be used when performing the backup of MariaDB Galera Cluster.
|
||||
ARGS=""
|
||||
BASEBACKDIR="${BACKDIR}/base"
|
||||
INCRBACKDIR="${BACKDIR}/incr"
|
||||
START="$(date +%s)"
|
||||
|
||||
info "run-mariabackup.sh: MySQL backup script"
|
||||
info "started: $(date)"
|
||||
|
||||
if test ! -d "${BASEBACKDIR}"
|
||||
then
|
||||
mkdir -p "${BASEBACKDIR}"
|
||||
fi
|
||||
|
||||
# Check base dir exists and is writable
|
||||
if test ! -d "${BASEBACKDIR}" -o ! -w "${BASEBACKDIR}"
|
||||
then
|
||||
fatal "${BASEBACKDIR} does not exist or is not writable"
|
||||
fi
|
||||
|
||||
if test ! -d "${INCRBACKDIR}"
|
||||
then
|
||||
mkdir -p "${INCRBACKDIR}"
|
||||
fi
|
||||
|
||||
# check incr dir exists and is writable
|
||||
if test ! -d "${INCRBACKDIR}" -o ! -w "${INCRBACKDIR}"
|
||||
then
|
||||
fatal "${INCRBACKDIR} does not exist or is not writable"
|
||||
fi
|
||||
|
||||
if [ -z "$(mysqladmin ${USEROPTIONS} status | grep 'Uptime')" ]
|
||||
then
|
||||
fatal "HALTED: MySQL does not appear to be running."
|
||||
fi
|
||||
|
||||
if ! "$(echo 'exit' | /usr/bin/mysql -s "${USEROPTIONS}")"
|
||||
then
|
||||
fatal "HALTED: Supplied mysql username or password appears to be incorrect (not copied here for security, see script)"
|
||||
fi
|
||||
|
||||
GetLockOrDie
|
||||
|
||||
info "Check completed OK"
|
||||
|
||||
# Find latest backup directory
|
||||
LATEST="$(find "${BASEBACKDIR}" -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -nr | head -1)"
|
||||
|
||||
AGE="$(stat -c %Y "${BASEBACKDIR}/${LATEST}/backup.stream.gz")"
|
||||
|
||||
if [ "${LATEST}" -a $(expr ${AGE} + ${FULLBACKUPCYCLE} + 5) -ge ${START} ]
|
||||
then
|
||||
info 'New incremental backup'
|
||||
# Create an incremental backup
|
||||
|
||||
# Check incr sub dir exists
|
||||
# try to create if not
|
||||
if test ! -d "${INCRBACKDIR}/${LATEST}"
|
||||
then
|
||||
mkdir -p "${INCRBACKDIR}/${LATEST}"
|
||||
fi
|
||||
|
||||
# Check incr sub dir exists and is writable
|
||||
if test ! -d "${INCRBACKDIR}/${LATEST}" -o ! -w "${INCRBACKDIR}/${LATEST}"
|
||||
then
|
||||
error "${INCRBACKDIR}/${LATEST} does not exist or is not writable"
|
||||
ReleaseLockAndExitWithCode 1
|
||||
fi
|
||||
|
||||
LATESTINCR="$(find "${INCRBACKDIR}/${LATEST}" -mindepth 1 -maxdepth 1 -type d | sort -nr | head -1)"
|
||||
if [ ! "${LATESTINCR}" ]
|
||||
then
|
||||
# This is the first incremental backup
|
||||
INCRBASEDIR="${BASEBACKDIR}/${LATEST}"
|
||||
else
|
||||
# This is a 2+ incremental backup
|
||||
INCRBASEDIR="${LATESTINCR}"
|
||||
fi
|
||||
|
||||
TARGETDIR="${INCRBACKDIR}/${LATEST}/$(date +%F_%H-%M-%S)"
|
||||
mkdir -p "${TARGETDIR}"
|
||||
|
||||
# Create incremental Backup
|
||||
"${BACKCMD}" --backup "${USEROPTIONS}" "${ARGS}" --extra-lsndir="${TARGETDIR}" --incremental-basedir="${INCRBASEDIR}" --stream="${STREAMCMD}" | "${GZIPCMD}" > "${TARGETDIR}/backup.stream.gz"
|
||||
else
|
||||
info 'New full backup'
|
||||
|
||||
TARGETDIR="${BASEBACKDIR}/$(date +%F_%H-%M-%S)"
|
||||
mkdir -p "${TARGETDIR}"
|
||||
|
||||
# Create a new full backup
|
||||
"${BACKCMD}" --backup "${USEROPTIONS}" "${ARGS}" --extra-lsndir="${TARGETDIR}" --stream="${STREAMCMD}" | "${GZIPCMD}" > "${TARGETDIR}/backup.stream.gz"
|
||||
fi
|
||||
|
||||
MINS=$(($FULLBACKUPCYCLE * ($KEEP + 1 ) / 60))
|
||||
info "Cleaning up old backups (older than $MINS minutes) and temporary files"
|
||||
|
||||
# Delete old backups
|
||||
for DEL in $(find ${BASEBACKDIR} -mindepth 1 -maxdepth 1 -type d -mmin +${MINN} -printf "%P\n")
|
||||
do
|
||||
info "deleting ${DEL}"
|
||||
rm -rf "${BASEBACKDIR}/${DEL}"
|
||||
rm -rf "${INCRBACKDIR}/${DEL}"
|
||||
done
|
||||
|
||||
SPENT=$(($(date +%s) - ${START}))
|
||||
info "took ${SPENT} seconds"
|
||||
info "completed: $(date)"
|
||||
ReleaseLockAndExitWithCode 0
|
||||
|
|
Loading…
Reference in a new issue