Skip to main content

Backup mariaDB using mariabackup and upload to S3 or BackBlaze using docker compose and restic. How to restore it

· 3 min read
Hreniuc Cristian-Alexandru

MariaDB container

I started the mariadb container like this:

version: "3"
name: "mariadb"
services:
mariadb:
image: mariadb:10.10.2
container_name: mariadb_prod_1
ports:
- "3306:3306"
volumes:
- ./fs/mariadb/volume:/var/lib/mysql:rw
- ./fs/mariadb/mysqld:/var/run/mysqld:rw
- ./fs/mariadb/log:/var/log/mysql:rw
- ./fs/mariadb/conf.d:/etc/mysql/conf.d:ro
environment:
MARIADB_MYSQL_LOCALHOST_USER: 1
MARIADB_MYSQL_LOCALHOST_GRANTS: "RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR"
MARIADB_ROOT_PASSWORD: password
restart: always

Those folders are empty at startup, I used them to make the server data persistent and also because some were required by mariabackup.

Afterwards I created a script which is ran by a cronjob, the script creates a full backup using mariabackup and uploads the backup to S3 or back blaze using restic(restic detects which files are already up and it won't upload them again).

Env Credentials

Before running the script, you need to create an env file next to it called .env with the following envs:

# Add here the envs for restic
export RESTIC_REPOSITORY="s3:https://s3.eu-central-003.backblazeb2.com/bucket/folder"
export AWS_ACCESS_KEY_ID="?"
export AWS_SECRET_ACCESS_KEY="?"
export RESTIC_PASSWORD="?"

If the restic repository is not initialized, you need to run this(anywhere):

# This initializes the repository in S3

unset HISTFILE
export RESTIC_REPOSITORY="s3:https://s3.eu-central-003.backblazeb2.com/bucket/folder"
export AWS_ACCESS_KEY_ID="key"
export AWS_SECRET_ACCESS_KEY="secret"
export RESTIC_PASSWORD="pass"

restic init

Backup Script

The script looks like this:

#!/bin/bash
SCRIPT_PATH='/opt/mariadb/backup/'

LOG_FOLDER="${SCRIPT_PATH}/fs/log"
BACKUP_FOLDER="${SCRIPT_PATH}/fs/backup"
# From the running mariadb container
MARIADB_VOLUME="/opt/mariadb/fs/mariadb/volume"
MARIADB_MYSQLD="/opt/mariadb/fs/mariadb/mysqld"
MARIADB_VERSION=10.10.2

declare -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
script_logging_level="INFO"

CURRENT_MONTH_LOG_FOLDER="${LOG_FOLDER}/$(date +'%m-%Y')"
mkdir -p ${CURRENT_MONTH_LOG_FOLDER}
LOG_FILE="${CURRENT_MONTH_LOG_FOLDER}/$(date +'%d-%m-%Y').log"

logThis() {
local log_priority=$1
local log_message=$2

# check if level exists
[[ ${levels[$log_priority]} ]] || return 1

# check if level is enough
(( ${levels[$log_priority]} < ${levels[$script_logging_level]} )) && return 2

# log here
# TODO: add colors: https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
echo "=====================================================================" >> ${LOG_FILE}
echo "[$(date)] ${log_priority} : ${log_message}" >> ${LOG_FILE}
}
logThis INFO "Performin Backup for MariaDB" &&

logThis INFO "Cleaning ${BACKUP_FOLDER} folder" &&
rm -rf ${BACKUP_FOLDER}/* &&
rm -rf ${BACKUP_FOLDER}/.gitkeep &&

BEGIN_BACKUP=${SECONDS} &&
# Full backup
logThis INFO "Performing full backup" &&
docker run --rm --user mysql -v ${MARIADB_MYSQLD}:/var/run/mysqld \
-v ${BACKUP_FOLDER}:/backup \
-v ${MARIADB_VOLUME}:/var/lib/mysql \
mariadb:${MARIADB_VERSION} mariabackup --backup \
--target-dir=/backup --user mysql &>> ${LOG_FILE} &&
BACKUP_DURATION=$((${SECONDS}-${BEGIN_BACKUP})) &&

logThis INFO "Finished performing a full backup, it took ${BACKUP_DURATION} seconds." &&

cd ${BACKUP_FOLDER} &&

# Load restic credentials envs
source ${SCRIPT_PATH}/env &&

logThis INFO "Running restic to upload the bacup to S3" &&
BEGIN_RESTIC=${SECONDS} &&
restic --verbose backup . &>> ${LOG_FILE} &&
RESTIC_DURATION=$((${SECONDS}-${BEGIN_RESTIC})) &&

logThis INFO "Finished uploading backup to S3, it took ${RESTIC_DURATION} seconds" &&
logThis INFO "Finished running script, ran for ${SECONDS} s, Backup took ${BACKUP_DURATION} s, Upload took ${RESTIC_DURATION} s" ||
logThis ERROR "Something went wrong.."

Cronjob

We need to add this to the cronjob to run as root:

# From root
sudo crontab -e

# Run every hour between 07 AM - 21 PM and then run another backup at 1 AM(The cron is in UTC, that's why it's 5-19 and 23)
0 5-19,23 * * * bash /opt/mariadb/backup/backup.sh &>> /opt/mariadb/backup/log/crontab.log

We need root, because we need to have read rights on the mysql files that were backed up and upload them in restic.

Restore Backup

RESTORED_BACKUP_PATH="/opt/mariadb/backup/fs/backup"
MARIADB_VOLUME="/opt/mariadb/fs/mariadb/volume"
MARIADB_COMPOSE="/opt/mariadb/prod_1/compose"

# Stop mariadb service
cd ${MARIADB_COMPOSE} && docker compose down

# Get backup from restic
unset HISTFILE
export RESTIC_REPOSITORY="s3:https://s3.eu-central-003.backblazeb2.com/bucket/folder"
export AWS_ACCESS_KEY_ID="key"
export AWS_SECRET_ACCESS_KEY="secret"
export RESTIC_PASSWORD="pass"

restic snapshots

restic restore ff7baae1 --target ${RESTORED_BACKUP_PATH}

# Prepare backup
docker run --user mysql --rm -v ${RESTORED_BACKUP_PATH}:/backup mariadb:10.10.2 mariabackup --prepare --target-dir=/backup

# Cleand MariaDB volume
cd ${MARIADB_VOLUME} && rm -rf *

# Restore the backup
docker run --user mysql --rm -v ${MARIADB_VOLUME}:/var/lib/mysql \
-v ${RESTORED_BACKUP_PATH}:/backup mariadb:10.10.2 mariabackup --copy-back --target-dir=/backup

# Start mariadb service or run pipeline
cd ${MARIADB_COMPOSE} && docker compose up -d