Появилась задача по созданию бэкапов на одной из своих VPS. Решил собрать решение на Borg Backup для хранения и на MariaDB-Backup для бэкапа БД. Borg выбран ввиду того, что он умеет делать инкрементальные бэкапы и дедупликацию, что в рамках ограниченного места на VPS очень актуально. А еще есть возможность создавать защищенные бэкапы.
По итогу, все это дело автоматизировано в два скрипта, службу и таймер systemd.
Устанавливаем необходимые пакеты:
1 |
apt install -y borgbackup mariadb-backup |
Если у вас не MariaDB, а MySQL, то можно использовать Percona-XtraBackup, фактически аналогична MariaDB-Backup, так как одно форк другого.
Создаем сервис:
1 2 3 4 5 6 7 8 |
cat /etc/systemd/system/borg-mariadb-backup.service [Unit] Description=Hot backup MariaDB and archive via Borg [Service] Type=oneshot Environment=HOME=/root ExecStart=/usr/local/bin/borg-backup.sh |
Тут стоит обратить внимание, что указание HOME необходимо для MariaDB-Backup для корректного поиска .my.cnf, но это не обязательно, в скрипте предусмотрены варианты указать путь дерктивой в команде или непосредственно в скрипте.
Теперь создаем таймер:
1 2 3 4 5 6 7 8 9 10 |
cat /etc/systemd/system/borg-mariadb-backup.timer [Unit] Description=Run MariaDB Borg backup daily [Timer] OnCalendar=daily Persistent=true [Install] WantedBy=timers.target |
Применяем изменения в сервисах systemd:
1 |
systemctl daemon-reload |
Проверить расписание запуска можно командой:
1 2 3 4 5 6 |
systemctl list-timers borg-mariadb-backup.timer NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2025-09-16 00:00:00 +07 22h left Mon 2025-09-15 00:00:01 +07 1h 1min ago borg-mariadb-backup.timer borg-mariadb-backup.service 1 timers listed. Pass --all to see loaded but inactive timers, too. |
Запуск будет производиться каждый день в 00:00.
Теперь сами скрипты. Скрипт бэкапа MariaDB:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
cat /usr/local/bin/mysql-backup.sh #!/bin/bash set -e # Настройки BACKUP_DIR="/backup/mariadb" # гуда делаем бэкап #MYSQL_USER="root" #MYSQL_PASSWORD="pass" # Если каталог существует — удаляем его полностью if [ -d "$BACKUP_DIR" ]; then echo "Директория для бэкапа существует, удаляем..." rm -rf "$BACKUP_DIR" fi # Создаём директорию mkdir -p "$BACKUP_DIR" chown mysql:mysql "$BACKUP_DIR" # Горячий бэкап MariaDB (InnoDB) # Логин пароль заданы в скрипте #mariadb-backup --backup --target-dir="$BACKUP_DIR" --user="$MYSQL_USER" --password="$MYSQL_PASSWORD" # Логин пароль указаны в .my.cnf #mariadb-backup --defaults-file=/root/.my.cnf --backup --target-dir="$BACKUP_DIR" # Поиск в окружении пользователя mariadb-backup --backup --target-dir="$BACKUP_DIR" # Применяем журнал, чтобы бэкап был готов к восстановлению mariadb-backup --prepare --target-dir="$BACKUP_DIR" echo "Бэкап MariaDB завершен: $BACKUP_DIR" |
Бэкап БД делается на горячую, без остановки.
Скрипт для бэкапа файлов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
cat /usr/local/bin/borg-backup.sh #!/bin/bash set -e # Перед использованием нужно создать репозиторий # borg init --encryption=keyfile "$REPO" # Для автоматизации создания архивов, пароль необходимо # поместить по пути "PASS_FILE", безопасно создать пароль # можно командой pwgen 12 1 > /root/borg-pass.txt # Настройки REPO="/backup/borg" # куда делается бэкап ARCHIVE_NAME="backup-$(date +%Y-%m-%d_%H:%M)" MARIABACKUP_DIR="/backup/mariadb" # каталог с бэкапом mysql MARIABACKUP_SCRIPT="/usr/local/bin/mysql-backup.sh" # скрипт для запуска бэкапа mariadb BACKUP_DIRS="/etc /root /home /var/www /backup/mariadb /usr/local/bin /usr/local/x-ui/" # собственно указываем что бэкапить PASS_FILE="/root/borg-pass.txt" # Проверка файла пароля if [ ! -s "$PASS_FILE" ]; then echo "Файла с паролем нет или он пуст. Создаем новый пароль:" # Генерируем 12-символьный пароль pwgen 12 1 > "$PASS_FILE" chmod 600 "$PASS_FILE" cat $PASS_FILE echo "Файл с паролем сохранен в $PASS_FILE" fi # Проверка репозитория, если он ещё не создан, создаем if [ ! -d "$REPO" ] || [ ! -f "$REPO/config" ]; then echo "Borg репозиторий не создан, инциализируем...." # Проверка файла пароля if [ -s "$PASS_FILE" ]; then echo "Файла с паролем для данного репозитория существует." echo "Делаем резервную копию..." cp $PASS_FILE $PASS_FILE.save-$(date +%Y%m%d-%H:%M) echo "Файл с паролем сохранен в $PASS_FILE.save-$(date +%Y%m%d-%H:%M)" fi # Генерируем пароль репозитория pwgen 12 1 > "$PASS_FILE" chmod 600 "$PASS_FILE" echo "borg init --encryption=repokey "$REPO"" echo "Репозиторий инициализирован, пароль репозитория: $(cat $PASS_FILE)" echo "Сохраните пароль репозитория!" BORG_NEW_PASSPHRASE="$(cat $PASS_FILE)" borg init --encryption=repokey $REPO fi export BORG_PASSPHRASE="$(cat $PASS_FILE)" # Создание дампа MariaDB if [ -x "$MARIABACKUP_SCRIPT" ]; then echo "Запуск бэкапа MariaDB ..." "$MARIABACKUP_SCRIPT" else echo "Скрипт бэкапа MariaDB не удается найти: $MARIABACKUP_SCRIPT" exit 1 fi # Для корректного бэкапа x-ui останавливаем, иначе будет битая sqlite база systemctl stop x-ui # Создание архива (инкрементальный по умолчанию) borg create --stats --progress "$REPO::$ARCHIVE_NAME" $BACKUP_DIRS # Запускаем после бэкапа x-ui systemctl start x-ui # Причесывание старых бэкапов borg prune -v --list "$REPO" --keep-daily=7 --keep-weekly=4 --keep-monthly=6 # Если каталог бэкапа mariadb существует — удаляем его полностью if [ -d "$MARIABACKUP_DIR" ]; then echo "Удаляем директорию бэкапа MariaDB..." rm -rf "$MARIABACKUP_DIR" fi echo "Бэкап Borg завершился: $ARCHIVE_NAME" |
Скрипт сам создаст и сгенерирует пароль если репозиторий не найден, если был аналогичный файл с паролем, сделается бэкап. В скрипте помимо запуска бэкапа MariaDB, еще делается бэкап x-ui, это частный случай, просто мне так надо было, если кому то не нужно, можно убрать команды остановки и запуска x-ui. Репозиторий создается с encryption repokey, так как это позволяет автоматизировать создание репозитория если его нет. Если вам вообще не нужно шифрования, можно использовать none.
Ротация бэкапов у нас следующая:
- сохраняется 7 ежедневных бэкапов
- сохраняется 4 недельных бэкапа
- сохраняется 6 месячных бэкапа
Скрипты можно запускать как в ручную, так и через systemd:
1 |
systemctl start borg-mariadb-backup |
Теперь самое главное, восстановление бэкапов. Для восстановление так же требуется установленный Borg, восстанавливается следующим образом:
1 2 3 |
export BORG_PASSPHRASE='ваш_пароль' borg list /path/to/repo borg extract /path/to/repo::backup-2025-09-15_00:00 |
Для репозиториев с encryption repokey, для восстановления нужен только ваш пароль. Для encryption none, пароли не требуются. Для encryption keyfile потребуется ключ репозитория. Ключ хранится вне репозитория, в файле key (обычно в $HOME/.config/borg/keys/). Для keyfile необходим предварительный бэкап ключа:
1 |
borg key export /path/to/repo /root/borg-repo-key |
Восстановление следующим образом:
1 2 3 4 |
export BORG_KEY_FILE=/root/borg-repo-key export BORG_PASSPHRASE='ваш_пароль' borg list /path/to/repo borg extract /path/to/repo::backup-2025-09-15_00:00 |
Дополнительные варианты восстановления.
Вариант восстановления в определенный каталог:
1 |
borg extract /path/to/repo::backup-2025-09-15_00:00 --target /restore/path |
Вариант частичного восстановления:
1 2 |
borg extract /path/to/repo::backup-2025-09-15_00:00 etc borg extract /path/to/repo::backup-2025-09-15_00:00 backup/mariadb |
Для восстановления бэкапа БД необходимо распаковать каталог БД, остановить сервер БД и переместить в /var/lib/mysql(если используется стандартное хранилище) бэкап, после запустить БД. Примерная последовательность команд:
1 2 3 4 5 6 7 8 9 10 |
systemctl stop mariadb mariadb-backup --copy-back --target-dir=/backup/mariadb # тут указываем путь к бэкапу mv /var/lib/mysql /var/lib/mysql-backup # если актуального ничего нет, то можно rm /var/lib/mysql/* -rf mkdir /var/lib/mysql chown -R mysql:mysql /var/lib/mysql # или путь к datadir chmod -R 700 /var/lib/mysql systemctl start mariadb |
mariadb-backup —copy-back — можно зменить обычным mv или cp. Так же стоит иметь ввиду, что версии sql сервера на источнике и таргете должны быть идентичными.
Данная статья носит характер заметки, если у кого то возникнут вопросы, задавайте в комментариях к статье.
Добавить комментарий