В этой статье я поделюсь скриптом для создания бэкапов БД PostgreSQL за определенный период (например: 1, 2, 3 дня, 1 неделя, 1 месяц, 6 месяцев, каждый год).
Объясню как запустить скрипт с помощью расписания crontab, покажу как настроить синхронизацию папки с бэкапами с облаком Yandex Disk.
TL;DR
Ссылка на репозиторий с python скриптом для создания бэкапов:
https://github.com/oktend/server-backup-exampleДобавление расписания в crontab:
sudo apt-get update
sudo apt-get install cron
crontab -e
0 3 * * * cd /home/user/server-backup-example; /home/user/.local/bin/poetry run python src/main.pyСинхронизация с Yandex Disk
echo "deb http://repo.yandex.ru/yandex-disk/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/yandex-disk.list > /dev/null && wget http://repo.yandex.ru/yandex-disk/YANDEX-DISK-KEY.GPG -O- | sudo apt-key add - && sudo apt-get update && sudo apt-get install -y yandex-disk
yandex-disk setupЗадача
Меня зовут Вадим Резвов, я начинающий системный администратор.
В мои обязанности входит: мониторинг состояния всех основных сервисов и систем проекта, создание бэкапов, поддержка ключевых компонентов сервера.
В одном из моих текущих проектов возникла задача - создавать регулярные бэкапы БД PostgreSQL за определенные отрезки времени (1, 2, 3 дня, 1 неделя, 1 месяц, 6 месяцев, каждый год - такое расписание я сформировал по запросу заказчика, чтобы была возможность откатиться на короткий, средний или длинный срок), также бэкап включает в себя папку с media и static контентом.
Регулярные бэкапы необходимы для того, чтобы была возможность восстановиться до последней работающей версии сервера.
В зависимости от того насколько давно возникла ошибка, можно откатиться на 1 день, месяц, год и т.д. - расписание можно поменять в server-backup-example/src/main.py, в 35-44 строке, все числа указаны в днях:
BACKUPS_TIMETABLE = [
0,
1,
2,
3,
7,
30,
180,
360,
]Бэкап попадающий в интервал между:
0 и 1, - бэкап за 1 день,
1 и 2, - за 2 день,
2 и 3, - за 3 день,
3 и 7, за 1 неделю,
7 и 30, за 1 месяц, и т.д.
В каждом интервале всегда находится только 1 бэкап попадающий в интервал, другой бэкап либо сдвигается в следующий интервал, либо удаляется.
Бэкапы в интервале после 360 - создаются за каждый год, и не удаляются.
Сообщения об ошибках и успешных бэкапах будут приходить в “Support” Telegram топик, - это сделано для того, чтобы постоянные сообщения о бэкапах не спамили в основной топик.
Решение задачи по отправке сообщений в определенный Telegram topic я описал в прошлой статье:
Python скрипт для создания бэкапов за определенные отрезки времени
Репозиторий с кодом для создания бэкапов:
https://github.com/oktend/server-backup-example
Рассмотрим основные моменты в коде:
Функция create_dump_postgres делает dump БД PostgreSQL:
def create_dump_postgres(now, dump_filename):
command = PG_DUMP_COMMAND_TEMPLATE.format(
password=os.getenv("DATABASE_PASSWORD"),
user=os.getenv("DATABASE_USER"),
database=os.getenv("DATABASE_NAME"),
port=os.getenv("DATABASE_PORT"),
filename=dump_filename,
)
result = os.system(command)
if result != 0:
log.error(f"pg_dump failed with code {result}, command = {command}")
sys.exit(10)
Функция create_backup_file архивирует dump БД (sql файл) и папку media в один файл формата tar.gz
def create_backup_file(now):
log.info("create_backup_file started")
archive_filename = BACKUPS_PATH / BACKUP_FILENAME_TEMPLATE.format(now=now)
with tempfile.TemporaryDirectory() as tmpdirname:
dump_filename = SQLDUMP_FILENAME_TEMPLATE.format(now=now)
dump_filepath = Path(tmpdirname) / dump_filename
create_dump_postgres(now, dump_filepath)
tar_command = TAR_COMMAND_TEMPLATE.format(
archive_filename=archive_filename,
dump_dirname=tmpdirname,
dump_filename=dump_filename,
media_dirpath=os.getenv("MEDIA_DIRPATH"),
media_dirname=os.getenv("MEDIA_DIRNAME"),
)
Список BACKUPS_TIMETABLE далее используется в функции rotate_backups, чтобы удалить лишние бэкапы в определенных интервалах:
def rotate_backups(now):
def add_backup(intervals, backup):
for interval in intervals:
if interval["start"] <= backup["timestamp"] and backup["timestamp"] < interval["end"]:
interval["backups"].append(backup)
return
log.warning(f"found a file that does not belong to any interval: {backup}")
def clear_extra_backups(intervals):
for interval in intervals:
backups = sorted(interval["backups"], key=lambda a: a["timestamp"], reverse=True)
for i in range(len(backups) - 1):
filename = backups[i]["filename"]
log.info(f"deleting extra backup: {filename}")
os.remove(filename)
backups[i]["status"] = "deleted"
log.info("rotate_backups started")
intervals = []
for i in range(len(BACKUPS_TIMETABLE) - 1):
end = BACKUPS_TIMETABLE[i]
start = BACKUPS_TIMETABLE[i + 1]
intervals.append({
"start": now - timedelta(days=start),
"end": now - timedelta(days=end),
"backups": [],
"days_end": BACKUPS_TIMETABLE[i],
"days_start": BACKUPS_TIMETABLE[i + 1],
})
По всему коду раскинуто логирование, например:
log.error(f"pg_dump failed with code {result}, command = {command}")
log.warning(f"found a file that does not belong to any interval: {backup}")
log.info("create_backup_file started")
log.debug(f"tar_command = {tar_command}")
В файле src/log.py в 33 строке, можно установить определенный уровень логирования, чтобы получать только важные сообщения (по уровню критичности логи имеют следущий порядок: DEBUG, INFO, WARNING, ERROR, CRITICAL), начиная с установленного уровня сообщения будут прилетать в Telegram топик:
telegram_handler.setLevel(logging.WARNING)В .env файле прописываете правильные значения переменных:
BACKUPS_DIR="/home/user/backups"
DATABASE_USER=user
DATABASE_PASSWORD=123
DATABASE_NAME=database
DATABASE_PORT=5432
MEDIA_DIRPATH=/home/user/your_project_repo/static_content/
MEDIA_DIRNAME=media
LOGFILE=logs/server_backup.log
TELEGRAM_NOTIFICATION_BOT_TOKEN="1234567890:asjWJHdejKJWKKklkli"
TELEGRAM_NOTIFICATION_CHAT_ID="-1234567890"
DUMP_MINIMAL_FILESIZE_MB="100"
TELEGRAM_MESSAGE_THREAD_ID="1234"
В данном примере полный путь до директории (/home/user/your_project_repo/static_content/media),
я разбил на две переменные MEDIA_DIRPATH=/home/user/your_project_repo/static_content/ и
MEDIA_DIRNAME=media, это нужно для корректной работы скрипта.
Если у вас нет media контента, в MEDIA_DIRPATH пропишите путь до любой фейковой директории, не включая саму директорию, а в MEDIA_DIRNAME укажите название самой директории.
В файле src/log.py функция init_logger берет из .env файла ранее указанные значения bot_token, chat_id, message_thread_id, на их основе сообщения отправляются в определенный Telegram топик:
def init_logger(filename, telegram_bot_token, telegram_chat_id, telegram_message_thread_id):Как узнать значения bot_token, chat_id, message_thread_id, я рассказывал в прошлой статье:
После окончания редактирования, в папке репозитория (server-backup-example) нужно выполнить следующие команды установки python, poetry, чтобы подготовить репозиторий к работе:
curl -sSL https://install.python-poetry.org | python3 -
poetry install
poetry shellCrontab
Чтобы бэкапы автоматически создавались по расписанию, будем использовать crontab, который будет выполнять по указанному расписанию определенную команду (запуск скрипта по созданию бэкапа).
Для начала скачиваем crontab:
sudo apt-get update
sudo apt-get install cronДалее нужно настроить расписание crontab, для этого прописываем команду:
crontab -eи добавляем последнюю строку, запускающую backup скрипт, весь остальной не используемый код должен быть закомментирован:
0 3 * * * cd /home/user/server-backup-example; /home/user/.local/bin/poetry run python src/main.py
В записи "0 3 * * *" 0 - минуты, 3 - часы, * - дни, * - месяцы, * - день недели(1-7), например 1 - это понедельник.
В данной записи бэкап создается каждый день в 3 часа ночи по серверному времени.
Далее в ручную запускаем команду из crontab, чтобы проверить создается ли бэкап:
cd /home/user/server-backup-example; /home/user/.local/bin/poetry run python src/main.pyСинхронизация с облачным хранилищем Yandex Disk
Чтобы при возникновении каких-либо проблем с хранилищем на сервере мы не потеряли ��айлы бэкапов, имеет смысл привязать папку с бэкапами на сервере к папке с бэкапами в облачном хранилище, например Yandex Disk.
Для синхронизации понадобится аккаунт Yandex Disk, если его нет, нужно зарегистрировать новый:
https://passport.yandex.ru/registration?mode=register
Для начала, нужно скачать yandex disk cli:
echo "deb http://repo.yandex.ru/yandex-disk/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/yandex-disk.list > /dev/null && wget http://repo.yandex.ru/yandex-disk/YANDEX-DISK-KEY.GPG -O- | sudo apt-key add - && sudo apt-get update && sudo apt-get install -y yandex-diskДалее запускаем настройку синхронизации:
yandex-disk setupКогда запросят, выбираем путь к папке с бэкапами (та же папка, которую указали в .env файле: /home/user/backups), которая будет синхронизироваться с yandex диском.
Теперь бэкапы должны создаваться по расписанию и синхронизироваться с облаком Yandex Disk.
Заключение
Надеюсь моя статья будет полезна тем, кто хочет настроить создание бэкапов для своего сервера.
Буду рад любой критике, комментариям, отзывам.