Главная » Хабрахабр » Резервное копирование большого количества разнородных web-проектов

Резервное копирование большого количества разнородных web-проектов

Тем не менее, каждый раз, когда перед системным администратором web-проекта встает задача настроить бэкапы, для многих она повисает в воздухе большим вопросительным знаком. Казалось бы, тема избитая – про резервное копирование сказано и написано многое, поэтому нечего изобретать велосипед, просто бери и делай. Где хранить резервные копии? Как правильно собрать бэкап данных? Как унифицировать процесс резервного копирования для целого зоопарка различного ПО? Как обеспечить необходимый уровень ретроспективы хранения копий?

Тогда мы сели и написали свои скрипты резервного копирования. Для себя мы впервые решали эту задачу в 2011. Бэкапы хранились в нашем либо каком-то другом внешнем хранилище, с возможностью тюнинга под конкретный проект. На протяжении многих лет мы пользовались только ими, и они успешно обеспечивали надежный процесс сбора и синхронизации бэкапов web-проектов наших клиентов.

Но чем дальше мы росли, тем больше у нас появлялось разношерстных проектов с разным ПО и внешними хранилищами, которые наши скрипты не поддерживали. Надо сказать, эти скрипты отработали своё по полной. Процесс бэкапов не мониторился, были только email-алерты. Например, у нас не было поддержки Redis и “горячих” бэкапов MySQL и PostgreSQL, которые появились позже.

За многие годы наши, некогда компактные, скрипты разрослись и превратились в огромного несуразного монстра. Другой проблемой был процесс поддержки. И когда мы собирались с силами и выпускали новую версию, отдельных усилий стоило выкатить обновление для той части клиентов, которые использовали предыдущую версию с какой-то кастомизацией.

Поэтому сначала мы сели и выписали все хотелки к новому решению. В итоге в начале этого года мы приняли волевое решение: заменить наши старые скрипты бэкапов чем-то более современным. Получилось примерно следующее:

  • Бэкапить данные наиболее часто используемого в работе ПО:
    • Файлы (дискретное и инкрементное копирование)
    • MySQL (холодные/горячие бэкапы)
    • PostgreSQL (холодные/горячие бэкапы)
    • MongoDB
    • Redis
  • Хранить бэкапы в популярных хранилищах:
    • Local
    • FTP
    • SSH
    • SMB
    • NFS
    • WebDAV
    • S3
  • Получать алерты в случае возникновения каких-либо проблем в процессе резервного копирования
  • Иметь единый конфигурационный файл, который позволит управлять бэкапами централизованно
  • Добавлять поддержку нового ПО через подключение внешних модулей
  • Указывать extra опции для сбора дампов
  • Иметь возможность восстановить бэкапы штатными средствами
  • Простота начального конфигурирования

Мы посмотрели open-source решения, которые уже существуют:

  • Bacula и её форки, например, Bareos
  • Amanda
  • Borg
  • Duplicaty
  • Duplicity
  • Rsnapshot
  • Rdiff-backup

Например, Bacula перегружена ненужными нам функциями, начальное конфигурирование — достаточно трудоемкое занятие из-за большого количества ручной работы (например, для написания/поиска скриптов бэкапов БД), а для восстановления копий необходимо задействовать специальные утилиты и т.д. Но у каждого из них были свои недостатки.

В конце концов мы пришли к двум важным выводам:

  1. Ни одно из существующих решений в полной мере нам не подходило;
  2. Похоже, у нас у самих было достаточно опыта и безумия, чтобы взяться за написание своего решения.

Так мы и сделали.

Конфигурационные файлы было принято решение описывать в формате yaml. В качестве языка для реализации выбрали Python – он прост в написании и поддержке, гибок и удобен.

Для удобства поддержки и добавления бэкапов нового ПО была выбрана модульная архитектура, где процесс сбора бэкапов каждого конкретного ПО (например, MySQL) описывается в отдельном модуле.

Поддержка файлов, БД и удалённых хранилищ

На текущий момент реализована поддержка следующих типов бэкапов файлов, БД и удалённых хранилищ:

БД:

  • MySQL (горячие/холодные бэкапы)
  • PostgreSQL (горячие/холодные бэкапы)
  • Redis
  • MongoDB

Файлы:

  • Дискретное копирование
  • Инкрементное копирование

Удалённые хранилища:

  • Local
  • S3
  • SMB
  • NFS
  • FTP
  • SSH
  • WebDAV

Дискретное резервное копирование

Можно задавать, какой способ использовать на уровне отдельных файлов и каталогов. Для разных задач подойдут либо дискретные, либо инкрементные бэкапы, поэтому реализовали оба типа.

Для дискретных копий (как файлов, так и БД) можно задавать ретроспективу в формате дни/недели/месяцы.

Инкрементное резервное копирование

Инкрементные копии файлов делаются по следующей схеме:

Далее в начале каждого месяца – инкрементная месячная копия относительно годичной. В начале года собирается полный бэкап. Внутри каждой декадной – инкрементные дневные относительно декадной. Внутри месячных – инкрементные декадные относительно месячной.

В таких случаях сбор копий значительно замедляется и может протекать более суток. Стоит оговориться, что пока наблюдаются некоторые проблемы при работе с директориями, которые содержат большое количество поддиректорий (десятки тысяч). Мы активно занимаемся устранением этого недочета.

Восстанавливаемся из инкрементных бэкапов

С инкрементными копиями немного сложнее. С восстановлением из дискретных бэкапов проблем нет – просто берём копию за нужную дату и разворачиваем обычным консольным tar. Чтобы восстановиться, например, на 24 июля 2018, нужно выполнить следующее:

  1. Развернуть годичный бэкап, пусть в нашем случае он отсчитывается от 1 января 2018 (на практике это может быть любая дата, в зависимости от того, когда было принято решение внедрить инкрементное резервное копирование)
  2. Накатить на него месячный бэкап за июль
  3. Накатить декадный бэкап за 21-ое июля
  4. Накатить дневной бэкап за 24 июля

Конечно, не самый быстрый процесс, но если учесть, что восстанавливаться из бэкапов приходиться не так уж часто и важна экономичность, такая схема получается вполне эффективной. При этом для выполнения 2-4 пунктов необходимо в команду tar добавить ключ -G, тем самым указав, что это инкрементный бэкап.

Исключения

Это можно сделать, указав соответствующие правила-исключения: Часто нужно исключить из бэкапов отдельные файлы или каталоги, например, каталоги с кешем.

пример конфигурационного файла

- target: - /var/www/*/data/ excludes: - exclude1/exclude_file - exclude2 - /var/www/exclude_3

Ротация бэкапов

Это приводило к проблемам на проектах, где места под бэкапы в принципе выделено ровно под одну копию – свежая копия не могла там собраться по причине нехватки места. В наших старых скриптах ротация была реализована так, что старая копия удалялась только после того, как новая собиралась успешно.

А процесс сбора бэкапов поставить на мониторинг, чтобы узнавать о возникновении каких-либо проблем. В новой реализации мы решили поменять этот подход: сначала удалять старую и уже потом собирать новую копию.

В случае инкрементного резервного копирования бэкапы по умолчанию хранятся год, а удаление старых копий происходит в начале каждого месяца, при этом старыми резервными копиями считаются архивы за тот же месяц прошлого года. При дискретном резервном копировании старой копией считается архив, который выходит за рамки заданной схемы хранения в формате дни/недели/месяцы. Это позволяет оптимально использовать дисковое пространство. Например, перед сбором месячного бэкапа 1 августа 2018 система проверит, есть ли бэкапы за август 2017, и если да, то удалит их.

Логирование

Система ведёт лог своей работы и фиксирует результат каждого шага: запуск/остановка средств, начало/конец выполнения определенного задания, результат сбора копии во временном каталоге, результат копирования/перемещения копии из временного каталога в постоянное место дислокации, результат ротации бэкапов и т.д.. В любом процессе, а в бэкапах особенно, важно держать руку на пульсе и иметь возможность узнавать, если что-то пошло не так.

События делятся на 2 уровня:

  • Info: информационный уровень – полёт проходит нормально, очередной этап завершился успешно, в логе делается соответствующая запись информационного характера
  • Error: уровень ошибки – что-то пошло не так, очередной этап завершился аварийно, в логе делается соответствующая запись об ошибке

E-mail нотификации

По окончанию сбора резервной копии система может рассылать email-уведомления.

Поддерживаются 2 списка получателей:

  • Администраторы – те, кто обслуживают сервер. Они получают только нотификации об ошибках, нотификации об успешных операциях им не интересны
  • Бизнес-пользователи – в нашем случае это клиенты, которые иногда хотят получать уведомления, чтобы удостовериться, что с бэкапами у них всё хорошо. Или, наоборот, не очень. Они могут выбирать – получать полный лог либо только лог с ошибками.

Структура конфигурационных файлов

Структура конфигурационных файлов выглядит следующим образом:

пример структуры

/etc/nxs-backup
├── conf.d
│ ├── desc_files_local.conf
│ ├── external_clickhouse_local.conf
│ ├── inc_files_smb.conf
│ ├── mongodb_nfs.conf
│ ├── mysql_s3.conf
│ ├── mysql_xtradb_scp.conf
│ ├── postgresql_ftp.conf
│ ├── postgresql_hot_webdav.conf
│ └── redis_local_ftp.conf
└── nxs-backup.conf

Здесь /etc/nxs-backup/nxs-backup.conf – главный конфигурационный файл, в котором указываются глобальные настройки:

конфигурационный файл

main: server_name: SERVER_NAME admin_mail: project-tech@nixys.ru client_mail: - '' mail_from: backup@domain.ru level_message: error block_io_read: '' block_io_write: '' blkio_weight: '' general_path_to_all_tmp_dir: /var/nxs-backup cpu_shares: '' log_file_name: /var/log/nxs-backup/nxs-backup.log jobs: !include [conf.d/*.conf]

Как правило, они выносятся в отдельные файлы (один файл на один job), которые подключаются через include в главном конфигурационном файле. Массив заданий (jobs) содержит список задач (job), которые представляют собой описание того, что именно бэкапить, где хранить и в каком количестве.

Поэтому администратору не нужно тратить время на поиск шаблона конфига для какого-то сервиса, например, MySQL, а достаточно просто запустить команду: Также позаботились о том, чтобы максимально оптимизировать процесс подготовки этих файлов и написали простенький генератор.

nxs-backup generate --storage local scp --type mysql --path /etc/nxs-backup/conf.d/mysql_local_scp.conf

На выходе генерируется файл /etc/nxs-backup/conf.d/mysql_local_scp.conf:

Содержимое файла

- job: PROJECT-mysql type: mysql tmp_dir: /var/nxs-backup/databases/mysql/dump_tmp sources: - connect: db_host: '' db_port: '' socket: '' db_user: '' db_password: '' auth_file: '' target: - all excludes: - information_schema - performance_schema - mysql - sys gzip: no is_slave: no extra_keys: '--opt --add-drop-database --routines --comments --create-options --quote-names --order-by-primary --hex-blob' storages: - storage: local enable: yes backup_dir: /var/nxs-backup/databases/mysql/dump store: days: '' weeks: '' month: '' - storage: scp enable: yes backup_dir: /var/nxs-backup/databases/mysql/dump user: '' host: '' port: '' password: '' path_to_key: '' store: days: '' weeks: '' month: ''

В котором остаётся только подставить несколько нужных значений.

Пусть у нас на сервере в каталоге /var/www есть две площадки интернет-магазина на 1С-Битрикс (bitrix-1.ru, bitrix-2.ru), каждая из которых работает со своей БД в разных инстансах MySQL (3306 порт для bitrix_1_db и 3307 порт для bitrix_2_db). Разберём на примере.

Структура файлов типичного Битрикс-проекта примерно следующая:

├── ...
├── bitrix
│ ├── ..
│ ├── admin
│ ├── backup
│ ├── cache
│ ├── ..
│ ├── managed_cache
│ ├── ..
│ ├── stack_cache
│ └── ..
├── upload
└── ...

Все остальные каталоги — дискретно, за исключением каталогов с кешем и бэкапами, собираемых нативными средствами Bitrix. Как правило, каталог upload весит много, и со временем только растёт, поэтому его бэкапим инкрементно. Пусть схема хранения бэкапов для этих двух площадок должна быть одинакова, при этом копии файлов нужно хранить как локально, так и на удаленном ftp-хранилище, а БД — только на удаленном smb-хранилище.

Итоговые конфигурационные файлы для такого сетапа будут иметь следующий вид:

bitrix-desc-files.conf (конфигурационный файл с описанием job для дискретного резервного копирования)

- job: Bitrix-desc-files type: desc_files tmp_dir: /var/nxs-backup/files/desc/dump_tmp sources: - target: - /var/www/*/ excludes: - bitrix/backup - bitrix/cache - bitrix/managed_cache - bitrix/stack_cache - upload gzip: yes storages: - storage: local enable: yes backup_dir: /var/nxs-backup/files/desc/dump store: days: 6 weeks: 4 month: 6 - storage: ftp enable: yes backup_dir: /nxs-backup/databases/mysql/dump host: ftp_host user: ftp_usr password: ftp_usr_pass store: days: 6 weeks: 4 month: 6

bitrix-inc-files.conf (конфигурационный файл с описанием job для инкрементного резервного копирования)

- job: Bitrix-inc-files type: inc_files sources: - target: - /var/www/*/upload/ gzip: yes storages: - storage: ftp enable: yes backup_dir: /nxs-backup/files/inc host: ftp_host user: ftp_usr password: ftp_usr_pass - storage: local enable: yes backup_dir: /var/nxs-backup/files/inc

bitrix-mysql.conf (конфигурационный файл с описанием job для бэкапов MySQL)

- job: Bitrix-mysql type: mysql tmp_dir: /var/nxs-backup/databases/mysql/dump_tmp sources: - connect: db_host: localhost db_port: 3306 db_user: bitrux_usr_1 db_password: password_1 target: - bitrix_1_db excludes: - information_schema - performance_schema - mysql - sys gzip: no is_slave: no extra_keys: '--opt --add-drop-database --routines --comments --create-options --quote-names --order-by-primary --hex-blob' - connect: db_host: localhost db_port: 3307 db_user: bitrix_usr_2 db_password: password_2 target: - bitrix_2_db excludes: - information_schema - performance_schema - mysql - sys gzip: yes is_slave: no extra_keys: '--opt --add-drop-database --routines --comments --create-options --quote-names --order-by-primary --hex-blob' storages: - storage: smb enable: yes backup_dir: /nxs-backup/databases/mysql/dump host: smb_host port: smb_port share: smb_share_name user: smb_usr password: smb_usr_pass store: days: 6 weeks: 4 month: 6

Параметры для запуска сбора бэкапов

В предыдущем примере мы подготовили конфигурационные файлы job для сбора бэкапов сразу всех элементов: файлов (дискретно и инкрементно), двух БД и их хранения на локальном и внешних (ftp, smb) хранилищах.

Запуск производится командой: Осталось всё это дело запустить.

nxs-backup start $JOB_NAME -c $PATH_TO_MAIN_CONFIG

При этом есть несколько зарезервированных имен job:

  • files — выполнение в произвольном порядке всех job с типами desc_files, inc_files (то есть, по сути забэкапить только файлы)
  • databases — выполнение в произвольном порядке всех job с типами mysql, mysql_xtradb, postgresql, postgresql_hot, mongodb, redis (то есть, забэкапить только БД)
  • external — выполнение в произвольном порядке всех job с типом external (запуск только дополнительных пользовательских скриптов, подробнее об этом ниже)
  • all — имитация поочередного запуска команды с job files, databases, external (значение по умолчанию)

Поскольку нам необходимо на выходе получить бэкапы данных как файлов, так и БД по состоянию на одно и тоже время (или с минимальной разницей), то рекомендуется осуществлять запуск nxs-backup с job all, что обеспечит последовательное выполнение описанных job (Bitrix-desc-files, Bitrix-inc_files, Bitrix-mysql).

Более того — само ПО при очередном запуске проверяет наличие уже запущенного процесса в системе и в случае его обнаружения автоматически завершит свою работу с соответствующей пометкой в журнале. То есть, важный момент – бэкапы будут собираться не параллельно, а последовательно, один за другим, с минимальной разницей во времени. Минус – бэкапы отдельных элементов собираются не одномоментно, а с некоторой разницей во времени. Такой подход значительно снижает нагрузку на систему. Но пока наша практика показывает, что это не критично.

Внешние модули

Цель – иметь в будущем возможность добавлять поддержку бэкапов нового ПО без необходимости переписывать nxs-backup. Как было сказано выше, благодаря модульной архитектуре, возможности системы можно расширять с помощью дополнительных пользовательских модулей, которые взаимодействуют с системой через специальный интерфейс.

пример конфигурационного файла

- job: TEST-external type: external dump_cmd: '' storages: ….

При этом по завершению выполнения данной команды ожидается, что: Особое внимание необходимо уделить ключу dump_cmd, где в качестве значения указывается полная команда для запуска внешнего скрипта.

  • Будет собран готовый архив данных ПО
  • В stdout будут отправлены данные в формате json, вида:

    { "full_path": "ABS_PATH_TO_ARCHIVE", "basename": "BASENAME_ARCHIVE", "extension": "EXTERNSION_OF_ARCHIVE", "gzip": true/false
    }

    • При этом ключи basename, extension, gzip необходимы исключительно для формирования конечного имени бэкапа.
  • В случае успешного завершения работы скрипта код возврата должен быть 0 и любой другой в случае возникновения каких-либо проблем.

Например, пусть у нас есть скрипт для создания snapshot etcd /etc/nxs-backup-ext/etcd.py:

код скрипта

#! /usr/bin/env python3
# -*- coding: utf-8 -*- import json
import os
import subprocess
import sys
import tarfile def archive(snapshot_path): abs_tmp_path = '%s.tar' %(snapshot_path) with tarfile.open(abs_tmp_path, 'w:') as tar: tar.add(snapshot_path) os.unlink(snapshot_path) return abs_tmp_path def exec_cmd(cmdline): data_dict = current_process = subprocess.Popen([cmdline], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable='/bin/bash') data = current_process.communicate() data_dict['stdout'] = data[0][0:-1].decode('utf-8') data_dict['stderr'] = data[1][0:-1].decode('utf-8') data_dict['code'] = current_process.returncode return data_dict def main(): snapshot_path = "/var/backups/snapshot.db" dump_cmd = "ETCDCTL_API=3 etcdctl --cacert=/etc/ssl/etcd/ssl/ca.pem --cert=/etc/ssl/etcd/ssl/member-node1.pem"+\ " --key=/etc/ssl/etcd/ssl/member-node1-key.pem --endpoints 'https://127.0.0.1:2379' snapshot save %s" %snapshot_path command = exec_cmd(dump_cmd) result_code = command['code'] if result_code: sys.stderr.write(command['stderr']) else: try: new_path = archive(snapshot_path) except tarfile.TarError as e: sys.exit(1) else: result_dict = { "full_path": new_path, "basename": "etcd", "extension": "tar", "gzip": False } print(json.dumps(result_dict)) sys.exit(result_code) if __name__ == '__main__': main()

Конфиг для запуска этого скрипта выглядит следующим образом:

конфигурационный файл

- job: etcd-external type: external dump_cmd: '/etc/nxs-backup-ext/etcd.py' storages: - storage: local enable: yes backup_dir: /var/nxs-backup/external/dump store: days: 6 weeks: 4 month: 6

При этом программа при запуске job etcd-external:

  • Запустит на выполнение скрипт /etc/nxs-backup-ext/etcd.py без параметров
  • После завершения работы скрипта проверит код завершения и наличие необходимых данных в stdout
  • Если все проверки прошли успешно, дальше задействуется тот же механизм, что и при работе уже встроенных модулей, где в качестве tmp_path выступает значение ключа full_path. Если нет — завершит выполнение данного задания с соответствующей отметкой в журнале.

Больше никаких обновлений и правок скриптов на боевых серверах. Процесс разработки и поддержки новой системы бэкапов реализован у нас по всем канонам CI/CD. И уже после этого через менеджер пакетов доставляются на конечные сервера клиентов. Все изменения проходят через наш центральный git-репозиторий в Gitlab, где в pipeline прописана сборка новых версий deb/rpm-пакетов, которые затем загружаются в наши deb/rpm репозитории.

Любой желающий может скачать и пользоваться им для организации процесса бекапа в своих проектах, а также дорабатывать под свои нужды, писать внешние модули. Мы сделали nxs-backup open-source проектом.

Там же находится инструкция по установке и настройке. Исходный код nxs-backup можно скачать с Github-репозитория по этой ссылке.

Также мы подготовили Docker-образ и выложили его на DockerHub.

Мы поможем разобраться и доработаем инструкцию. Если в процессе настройки или использования возникнут вопросы, напишите нам.

В ближайших планах у нас реализовать следующий функционал:

  • Интеграцию с мониторингом
  • Шифрование резервных копий
  • Web-интерфейс для управления настройками бэкапов
  • Разворачивание бэкапов средствами nxs-backup
  • И многое другое

Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Маленькие создания, большие деяния: роль листорезов в парниковом эффекте неотропики

Если вы подумали про пчел, то вы правы, но сегодня не о них. Какое существо частенько ассоциируется в культуре человека с трудолюбием, выносливостью, коллективностью и даже ответственностью? С чего это мы вдруг решили поговорить о каких-то насекомых? Сегодня мы поговорим ...

[Перевод] JavaScript: вопросы и ответы

Недавно в компании SmartSpate решили собрать вопросы, касающиеся JavaScript, и на них ответить. В материале, перевод которого мы публикуем, приведены чуть больше двух десятков вопросов о JavaScript и ответов на них. Спектр затрагиваемых здесь тем достаточно широк. В частности — ...