Fork and upgrade script

This commit is contained in:
Dryusdan 2023-10-03 11:42:07 +02:00
parent 6dcb64f6b2
commit 9b5db8f9d1
Signed by: Dryusdan
GPG key ID: EC1438DDE24E27D7

View file

@ -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