├── Dockerfile ├── Makefile ├── README.md ├── backup-galera-xtrabackup-v2 ├── backup-mysqldump ├── backup-run └── docker-entrypoint.sh /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hauptmedia/mariadb:10.0-galera 2 | MAINTAINER Julian Haupt 3 | 4 | # install required packages 5 | RUN apt-get update -qq && \ 6 | apt-get install -y --no-install-recommends bzip2 && \ 7 | apt-get clean autoclean && \ 8 | apt-get autoremove --yes && \ 9 | rm -rf /var/lib/{apt,dpkg,cache,log}/ 10 | 11 | ENV GO_CRON_VERSION v0.0.7 12 | 13 | RUN curl -L https://github.com/odise/go-cron/releases/download/${GO_CRON_VERSION}/go-cron-linux.gz \ 14 | | zcat > /usr/local/bin/go-cron \ 15 | && chmod u+x /usr/local/bin/go-cron 16 | 17 | # install s3cmd 18 | RUN apt-get update -qq && \ 19 | wget -O- -q http://s3tools.org/repo/deb-all/stable/s3tools.key | apt-key add - && \ 20 | wget -O/etc/apt/sources.list.d/s3tools.list http://s3tools.org/repo/deb-all/stable/s3tools.list && \ 21 | apt-get install -y --no-install-recommends s3cmd && \ 22 | apt-get clean autoclean && \ 23 | apt-get autoremove --yes && \ 24 | rm -rf /var/lib/{apt,dpkg,cache,log}/ 25 | 26 | # install backup scripts 27 | ADD backup-galera-xtrabackup-v2 /usr/local/bin/backup-galera-xtrabackup-v2 28 | ADD backup-mysqldump /usr/local/bin/backup-mysqldump 29 | ADD backup-run /usr/local/bin/backup-run 30 | 31 | #18080 http status port 32 | EXPOSE 18080 33 | 34 | ADD docker-entrypoint.sh /usr/local/sbin/docker-entrypoint.sh 35 | ENTRYPOINT ["/usr/local/sbin/docker-entrypoint.sh"] 36 | 37 | CMD ["go-cron"] 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build -t="hauptmedia/mariadb-backup" . 3 | 4 | .PHONY: build 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-mariadb-backup 2 | 3 | This container can be used to periodically backup MySQL, MariaDB, and MariaDB Galera cluster instances. 4 | 5 | Example usage which will backup the database every day at 03:00. You can check the last run with the integrated HTTP server on port 18080: 6 | 7 | ```bash 8 | docker run -d \ 9 | -v /var/backups:/var/backups \ 10 | -p 18080:18080 \ 11 | -e TIMEZONE="Europe/Berlin" \ 12 | -e SCHEDULE="0 0 3 * *" \ 13 | -e BACKUP_METHOD="mysqldump" \ 14 | -e BACKUP_OPTS="-u root -p test -h 172.17.0.68" \ 15 | hauptmedia/mariadb-backup 16 | ``` 17 | 18 | # Available backup methods 19 | 20 | ## mysqldump 21 | 22 | Backups a MySQL/MariaDB database via mysqldump. 23 | 24 | Example standalone run: 25 | 26 | ```bash 27 | docker run -i -t --rm \ 28 | -v /var/backups:/var/backups \ 29 | hauptmedia/mariadb-backup \ 30 | backup-mysqldump \ 31 | -u root -p test -h 172.17.0.19 32 | ``` 33 | 34 | ``` 35 | Usage: /usr/local/bin/backup-mysqldump -u mysqluser -p mysqlpassword -h mysqlhost 36 | 37 | -u Specifies the MySQL user (required) 38 | -p Specifies the MySQL password (required) 39 | -h Specifies the MySQL host (required) 40 | -P Specifies the MySQL port (optional) 41 | -d Specifies the backup file where to put the backup (default: /var/backups/CURRENT_DATETIME_MYSQLHOST_mysqldump) 42 | ``` 43 | 44 | ## galera-xtrabackup-v2 45 | 46 | Backups a galera cluster via a remote connection using xtrabackup-v2 sst method. 47 | 48 | It creates a listening socket for receiving the state snapshot transfer 49 | and launches a galera arbitrator which connects to the cluster, triggers 50 | a state snapshot transfer and disconnects from the cluster. 51 | 52 | *Please note: The donor cluster node must be able to connect to 53 | the listen address (which may be specified using the -l option)* 54 | 55 | Example standalone run: 56 | 57 | ```bash 58 | docker run -i -t --rm \ 59 | -v /tmp/mysqlbackup:/var/lib/mysql \ 60 | hauptmedia/mariadb-backup \ 61 | backup-galera-xtrabackup-v2 \ 62 | -a gcomm://172.17.0.19:4567 \ 63 | -g MyClusterName 64 | ``` 65 | 66 | ``` 67 | Usage: /usr/local/bin/backup-galera-xtrabackup-v2 -a gcomm://ip:4567,ip:4567 -g MyClusterName 68 | 69 | -a Specifies the galera cluster address (required) 70 | -g Specifies the galera cluster name (required) 71 | -l Specifies the ip and port where to listen for the state snapshot transfer (default: public-ip:4444) 72 | -d Specifies the data directory where to put the backup (default: /var/lib/mysql) 73 | -n Specifies the donor node which should be use (optional) 74 | ``` 75 | -------------------------------------------------------------------------------- /backup-galera-xtrabackup-v2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PID=$$ 4 | XTRABACKUP_LOG=/tmp/$$-xtrabackup 5 | DATA_DIR=/var/lib/mysql 6 | CLUSTER_ADDRESS= 7 | CLUSTER_NAME= 8 | CLUSTER_DONOR_NODE= 9 | LISTEN_ADDRESS=$(hostname --ip-addr):4444 10 | 11 | while getopts ":a:g:ldn" opt; do 12 | case $opt in 13 | n) 14 | CLUSTER_DONOR_NODE=$OPTARG 15 | ;; 16 | d) 17 | DATA_DIR=$OPTARG 18 | ;; 19 | l) 20 | LISTEN_ADDRESS=$OPTARG 21 | ;; 22 | a) 23 | CLUSTER_ADDRESS=$OPTARG 24 | ;; 25 | g) 26 | CLUSTER_NAME=$OPTARG 27 | ;; 28 | \?) 29 | echo "Invalid option: -$OPTARG" >&2 30 | exit 1 31 | ;; 32 | :) 33 | echo "Option -$OPTARG requires an argument." >&2 34 | exit 1 35 | ;; 36 | esac 37 | done 38 | 39 | if [ -z "$CLUSTER_ADDRESS" ] || [ -z "$CLUSTER_NAME" ]; then 40 | echo 41 | echo Usage: $0 -a gcomm://ip:4567,ip:4567 -g MyClusterName 42 | echo 43 | echo " -a Specifies the galera cluster address (required)" 44 | echo " -g Specifies the galera cluster name (required)" 45 | echo " -l Specifies the ip and port where to listen for the state snapshot transfer (default: public-ip:4444)" 46 | echo " -d Specifies the data directory where to put the backup (default: /var/lib/mysql)" 47 | echo " -n Specifies the donor node which should be use (optional)" 48 | echo 49 | exit 1 50 | fi 51 | 52 | echo Using the following configuration: 53 | echo 54 | echo " data_dir: ${DATA_DIR}" 55 | echo " cluster_name: ${CLUSTER_NAME}" 56 | echo " cluster_donor_node: ${CLUSTER_DONOR_NODE}" 57 | echo " cluster_address: ${CLUSTER_ADDRESS}" 58 | echo " listen_address: ${LISTEN_ADDRESS}" 59 | echo 60 | 61 | #--auth replication:test \ 62 | wsrep_sst_xtrabackup-v2 \ 63 | --role joiner \ 64 | --datadir ${DATA_DIR} \ 65 | --address ${LISTEN_ADDRESS} \ 66 | --defaults-file /etc/mysql/my.cnf \ 67 | --parent $PID >${XTRABACKUP_LOG} 2>&1 & 68 | 69 | COUNTER=0 70 | 71 | echo 72 | echo -n Waiting xtrabackup to become ready 73 | 74 | while [ -z "$XTRABACKUP_ADDRESS" ] && [ $COUNTER -lt 30 ]; do 75 | let COUNTER=COUNTER+1 76 | echo -n "." 77 | sleep 1 78 | XTRABACKUP_ADDRESS=$(cat $XTRABACKUP_LOG | egrep "^ready " | awk '{ print $2; }') 79 | done 80 | 81 | echo 82 | 83 | if [ -z "$XTRABACKUP_ADDRESS" ]; then 84 | echo Could not determine xtrabackup address, aborting! 85 | exit 1 86 | fi 87 | 88 | echo 89 | echo xtrabackup_address = ${XTRABACKUP_ADDRESS} 90 | 91 | GARBD_OPTS="-a ${CLUSTER_ADDRESS} -g ${CLUSTER_NAME} --sst xtrabackup-v2:${XTRABACKUP_ADDRESS}" 92 | if [ -n "$CLUSTER_DONOR_NODE" ]; then 93 | GARBD_OPTS="${GARBD_OPTS} --donor ${CLUSTER_DONOR_NODE}" 94 | fi 95 | 96 | garbd $GARBD_OPTS 97 | 98 | echo 99 | echo -n State Snapshot Transfer in progress 100 | 101 | while [ -f ${DATA_DIR}/sst_in_progress ]; do 102 | echo -n "." 103 | sleep 1 104 | done 105 | 106 | echo 107 | echo 108 | 109 | cat ${XTRABACKUP_LOG} 110 | 111 | echo 112 | 113 | if grep -q "\[ERROR\]" ${XTRABACKUP_LOG}; then 114 | echo Backup failed with errors! 115 | exit 1 116 | else 117 | echo Backup finished successfully. 118 | exit 0 119 | fi 120 | 121 | 122 | -------------------------------------------------------------------------------- /backup-mysqldump: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MYSQL_USER= 4 | MYSQL_PASSWORD= 5 | MYSQL_HOST= 6 | MYSQL_PORT=3306 7 | COMPRESSION_METHOD= 8 | RSH= 9 | SPLIT_FILES= 10 | 11 | # The leading ":" suppresses error messages from 12 | while getopts ":u:p:h:P:d:c:e:x" opt; do 13 | case $opt in 14 | d) 15 | BACKUP_FILE=$OPTARG 16 | ;; 17 | u) 18 | MYSQL_USER=$OPTARG 19 | ;; 20 | p) 21 | MYSQL_PASSWORD=$OPTARG 22 | ;; 23 | P) 24 | MYSQL_PORT=$OPTARG 25 | ;; 26 | h) 27 | MYSQL_HOST=$OPTARG 28 | ;; 29 | c) 30 | COMPRESSION_METHOD=$OPTARG 31 | ;; 32 | e) 33 | RSH=$OPTARG 34 | ;; 35 | x) 36 | SPLIT_FILES=1 37 | ;; 38 | \?) 39 | echo "Invalid option: -$OPTARG" >&2 40 | exit 1 41 | ;; 42 | :) 43 | echo "Option -$OPTARG requires an argument." >&2 44 | exit 1 45 | ;; 46 | esac 47 | done 48 | 49 | if [ -z "$BACKUP_FILE" ]; then 50 | BACKUP_FILE=/var/backups/$(date +"%Y-%m-%d-%H%M%S")_${MYSQL_HOST}_mysqldump 51 | fi 52 | 53 | if [ -z "$MYSQL_USER" ] || [ -z "$MYSQL_PASSWORD" ]; then 54 | echo 55 | echo Usage: $0 -u mysqluser -p mysqlpassword -h mysqlhost -c bzip2 56 | echo 57 | echo " -u Specifies the MySQL user (required)" 58 | echo " -p Specifies the MySQL password (required)" 59 | echo " -h Specifies the MySQL host (required)" 60 | echo " -P Specifies the MySQL port (optional)" 61 | echo " -d Specifies the backup file where to put the backup (default: /var/backups/CURRENT_DATETIME_MYSQLHOST_mysqldump)" 62 | echo " -c Specifies the compression method which should be used to compress the dump file (bzip2 or gzip)" 63 | echo " -e Specified the remote shell which should be used (e.g. \"ssh -C user@remotehost\")" 64 | echo " -x If specified the backup will create a seperate file for each database/table" 65 | echo 66 | exit 1 67 | fi 68 | 69 | echo Using the following configuration: 70 | echo 71 | echo " backup_file: ${BACKUP_FILE}" 72 | echo " mysql_user: ${MYSQL_USER}" 73 | echo " mysql_password: ****** (not shown)" 74 | echo " mysql_host: ${MYSQL_HOST}" 75 | echo " mysql_port: ${MYSQL_PORT}" 76 | echo " compression_method: ${COMPRESSION_METHOD}" 77 | echo " rsh: ${RSH}" 78 | echo 79 | 80 | MYSQL_OPTS="-u${MYSQL_USER} -p${MYSQL_PASSWORD} -h${MYSQL_HOST} -P${MYSQL_PORT}" 81 | MYSQLDUMP_OPTS="--single-transaction --routines --triggers" 82 | 83 | if [ -n "$SPLIT_FILES" ]; then 84 | for db in $(${RSH} mysql ${MYSQL_OPTS} -e show\ databases -s --skip-column-names | grep -v information_schema | grep -v performance_schema); do 85 | for table in $(${RSH} mysql ${MYSQL_OPTS} $db -e show\ tables -s --skip-column-names); do 86 | echo "Dumping ${db}.${table}" 87 | 88 | echo "SET autocommit=0;SET unique_checks=0;SET foreign_key_checks=0;" >${BACKUP_FILE}_${db}.${table} 89 | ${RSH} mysqldump ${MYSQL_OPTS} ${MYSQLDUMP_OPTS} ${db} ${table} >>${BACKUP_FILE}_${db}.${table} 90 | echo "SET autocommit=1;SET unique_checks=1;SET foreign_key_checks=1;commit;" >>${BACKUP_FILE}_${db}.${table} 91 | 92 | RETVAL=$? 93 | done 94 | done 95 | else 96 | ${RSH} mysqldump ${MYSQL_OPTS} ${MYSQLDUMP_OPTS} --all-databases >${BACKUP_FILE} 97 | RETVAL=$? 98 | fi 99 | 100 | # compression step 101 | if [ "$RETVAL" == 0 ]; then 102 | if [ "$COMPRESSION_METHOD" = "bzip2" ]; then 103 | echo Compressing backup using bzip2 compression method. 104 | bzip2 --best ${BACKUP_FILE}* 105 | RETVAL=$? 106 | fi 107 | 108 | if [ "$COMPRESSION_METHOD" = "gzip" ]; then 109 | echo Compressing backup using gzip compression method. 110 | gzip --best ${BACKUP_FILE}* 111 | RETVAL=$? 112 | fi 113 | fi 114 | 115 | if [ "$RETVAL" == 0 ]; then 116 | echo Backup finished successfully. 117 | exit 0 118 | else 119 | echo Backup failed with errors! 120 | exit 1 121 | fi 122 | -------------------------------------------------------------------------------- /backup-run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PIDFILE=/var/run/backup-run.pid 4 | 5 | if [ -f $PIDFILE ]; then 6 | echo $0 is already running with pid $(cat $PIDFILE), aborting! 7 | exit 1 8 | fi 9 | 10 | if [ ! -x /usr/local/bin/backup-${BACKUP_METHOD} ]; then 11 | echo Backup Method ${BACKUP_METHOD} does not exists! 12 | exit 1 13 | fi 14 | 15 | echo $$ >$PIDFILE 16 | 17 | /usr/local/bin/backup-${BACKUP_METHOD} ${BACKUP_OPTS} 18 | 19 | retval=$? 20 | 21 | rm $PIDFILE 22 | 23 | exit $retval 24 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ -n "$TIMEZONE" ]; then 5 | echo ${TIMEZONE} > /etc/timezone && \ 6 | dpkg-reconfigure -f noninteractive tzdata 7 | fi 8 | 9 | if [ $1 = "go-cron" ]; then 10 | 11 | if [ -z "$SCHEDULE" ]; then 12 | echo Missing SCHEDULE environment variable 2>&1 13 | echo Example -e SCHEDULE=\"\*/10 \* \* \* \* \*\" 2>&1 14 | exit 1 15 | fi 16 | 17 | if [ -z "$BACKUP_METHOD" ]; then 18 | echo Missing BACKUP_METHOD environment variable 2>&1 19 | echo Example -e BACKUP_METHOD=\"galera-xtrabackup-v2\" 2>&1 20 | exit 1 21 | fi 22 | 23 | exec go-cron -s "${SCHEDULE}" -- /usr/local/bin/backup-run 24 | fi 25 | 26 | exec "$@" 27 | --------------------------------------------------------------------------------