Резервное копирование большого количества разнородных web-проектов
nxs-backup для централизованного резервного копирования web-проектов
Почему понадобилось новое решение
Резервное копирование — базовая задача для любого web-проекта. Однако при большом количестве систем и разнородного ПО быстро возникают практические вопросы: как правильно собирать резервные копии, где их хранить, как обеспечить необходимую глубину хранения и как унифицировать процесс для разных типов данных и инфраструктуры.
Впервые эту задачу мы решали в 2011 году. Тогда были разработаны собственные скрипты резервного копирования, которые на протяжении нескольких лет обеспечивали сбор и синхронизацию резервных копий web-проектов клиентов.
Резервные копии хранились как в собственной инфраструктуре, так и во внешних хранилищах. Для каждого проекта можно было настроить параметры резервного копирования в зависимости от его особенностей.
Со временем количество проектов и используемого ПО увеличивалось, а вместе с ним росло число сценариев, которые старые скрипты не поддерживали. Например, не было поддержки Redis, а также горячего резервного копирования MySQL и PostgreSQL. Сам процесс резервного копирования практически не мониторился — использовались только email-уведомления.
Отдельной проблемой стала поддержка самого решения. Со временем компактный набор скриптов превратился в сложную систему с большим количеством индивидуальных настроек. Выпуск новой версии и её обновление на проектах, где использовались собственные модификации, требовали значительных усилий.
Поэтому возникла необходимость заменить существующее решение более современной системой.
Требования к системе резервного копирования
Перед разработкой нового решения мы сформировали основные требования:
1. Поддержка наиболее распространённого ПО:
- файлы — дискретное и инкрементное копирование;
- MySQL — холодные и горячие бэкапы;
- PostgreSQL — холодные и горячие бэкапы;
- MongoDB;
- Redis;
2. Поддержка различных хранилищ:
- Local;
- FTP;
- SSH;
- SMB;
- NFS;
- WebDAV;
- S3;
3. Уведомления о проблемах в процессе резервного копирования;
4. Единый конфигурационный файл для централизованного управления;
5. Возможность добавлять поддержку нового ПО через внешние модули;
6. Возможность задавать дополнительные параметры создания дампов;
7. Восстановление резервных копий стандартными средствами;
8. Простая первоначальная настройка.
Анализ существующих решений
Мы рассмотрели несколько существующих open-source решений:
- Bacula и её форки, например Bareos;
- Amanda;
- Borg;
- Duplicaty;
- Duplicity;
- Rsnapshot;
- Rdiff-backup.
Однако ни одно из них полностью не соответствовало требованиям.
Например, Bacula содержит значительно больше функций, чем требовалось для наших сценариев. Первоначальная настройка также требует большого объёма ручной работы, в том числе подготовки или поиска отдельных скриптов для резервного копирования баз данных. Для восстановления резервных копий в некоторых решениях необходимо использовать специальные утилиты.
В результате мы пришли к двум выводам:
1. Ни одно из рассмотренных решений не закрывает все необходимые сценарии.
2. Имеющегося опыта достаточно, чтобы разработать собственный инструмент.
Так появился nxs-backup.
Архитектура nxs-backup
Для реализации был выбран Python: он позволяет относительно просто разрабатывать и поддерживать систему, а также удобно расширять её функциональность.
Конфигурация описывается в формате YAML.
Основой архитектуры стала модульность: процесс резервного копирования каждого типа ПО реализован в отдельном модуле. Это упрощает поддержку существующих интеграций и добавление новых.
Возможности резервного копирования
Поддерживаемые типы данных и хранилищ
nxs-backup поддерживает следующие варианты.
Базы данных:
- MySQL — горячие и холодные бэкапы;
- PostgreSQL — горячие и холодные бэкапы;
- Redis;
- MongoDB.
Файлы:
- дискретное копирование;
- инкрементное копирование.
Хранилища:
- Local;
- S3;
- SMB;
- NFS;
- FTP;
- SSH;
- WebDAV.
Дискретное резервное копирование
Для разных данных могут использоваться разные стратегии резервного копирования. Поэтому nxs-backup поддерживает как дискретные, так и инкрементные копии, причём способ можно задавать для отдельных файлов и каталогов.
Для дискретных копий файлов и баз данных можно настроить глубину хранения в формате:
- дни;
- недели;
- месяцы.
Инкрементное резервное копирование
Для файлов используется многоуровневая схема инкрементного резервного копирования.
В начале года создаётся полный бэкап. Далее:
- в начале каждого месяца создаётся месячная инкрементная копия относительно годичной;
- внутри месяца создаются декадные копии относительно месячной;
- внутри каждой декады создаются дневные копии относительно декадной.
У такого подхода есть особенность: при работе с каталогами, содержащими десятки тысяч подкаталогов, сбор копий может существенно замедляться и занимать более суток.
Восстановление из инкрементных копий
С дискретными копиями восстановление выполняется непосредственно из нужного архива с помощью стандартного 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
Ротация резервных копий
В предыдущей реализации старая копия удалялась только после успешного создания новой. При ограниченном объёме дискового пространства это могло приводить к проблемам: если хранилище было рассчитано ровно на одну копию, новая копия не могла быть создана до удаления старой.
В nxs-backup используется другой порядок:
1. Удаляется устаревшая копия.
2. Запускается сбор новой.
3. Процесс контролируется и журналируется.
Для дискретного резервного копирования устаревшей считается копия, которая вышла за пределы заданной схемы хранения в формате дней, недель и месяцев.
Для инкрементных копий по умолчанию используется годовая ретроспектива. Удаление старых копий выполняется в начале каждого месяца. Например, перед созданием месячного бэкапа 1 августа 2018 года система проверяет наличие копий за август 2017 года и удаляет их.
Такой подход позволяет контролировать объём занимаемого дискового пространства.
Логирование
Для контроля процесса резервного копирования система ведёт журнал и фиксирует результаты отдельных операций:
- запуск и остановку средств резервного копирования;
- начало и завершение задания;
- результат создания копии во временном каталоге;
- результат копирования или перемещения во внешнее хранилище;
- результат ротации;
- ошибки на отдельных этапах.
Используются два уровня событий:
- Info — информационные сообщения об успешном выполнении очередного этапа;
- Error — сообщения об ошибках и аварийном завершении операций.
Email-уведомления
После выполнения операций система может отправлять email-уведомления. Предусмотрено два списка получателей:
1. Администраторы получают уведомления об ошибках. Информация об успешном выполнении резервного копирования для них не обязательна.
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]
Массив jobs содержит список заданий. В каждом job описывается, что именно необходимо резервировать, где хранить копию и какую ретроспективу использовать.
Обычно каждое задание выносится в отдельный файл, который подключается через include в основном конфигурационном файле.
Генерация конфигурации
Для упрощения настройки предусмотрен генератор конфигурационных файлов.
Например, конфигурацию для MySQL с локальным и SCP-хранилищем можно создать командой:
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: ''
Пример: резервное копирование двух проектов 1С-Битрикс
Рассмотрим сервер, на котором в каталоге /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
└── ...
Каталог upload, как правило, занимает значительный объём и постоянно растёт, поэтому его имеет смысл резервировать инкрементно.
Остальные каталоги резервируются дискретно, за исключением каталогов с кэшем и резервными копиями, которые создаются штатными средствами Bitrix.
Предположим, что для обоих проектов используется одинаковая схема хранения:
- файловые копии хранятся локально и на удалённом FTP;
- резервные копии баз данных хранятся на удалённом SMB-хранилище.
Для этого создаются три конфигурационных файла:
- bitrix-desc-files.conf — дискретное резервное копирование файлов;
- bitrix-inc-files.conf — инкрементное резервное копирование файлов;
- bitrix-mysql.conf — резервное копирование MySQL.
bitrix-desc-files.conf
- 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: 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: 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
Запуск резервного копирования
После подготовки конфигурации запуск выполняется командой:
nxs-backup start $JOB_NAME -c $PATH_TO_MAIN_CONFIG
Предусмотрены специальные имена job:
- files — запуск всех заданий типов desc_files и inc_files;
- databases — запуск заданий типов mysql, mysql_xtradb, postgresql, postgresql_hot, mongodb, redis;
- external — запуск заданий типа external;
- all — последовательный запуск files, databases и external. Это значение используется по умолчанию.
Если требуется получить резервные копии файлов и баз данных с максимально близкими временными отметками, рекомендуется запускать nxs-backup с job all.
Задания выполняются последовательно, а не параллельно. При каждом запуске программа также проверяет, не запущен ли уже другой процесс nxs-backup. Если такой процесс обнаружен, новый запуск завершается с соответствующей записью в журнале.
Последовательное выполнение снижает нагрузку на сервер. Обратная сторона — резервные копии разных элементов создаются с некоторой разницей во времени. На практике такая разница не всегда критична.
Внешние модули
Модульная архитектура позволяет расширять возможности nxs-backup с помощью пользовательских скриптов. Это позволяет добавлять резервное копирование ПО, для которого нет встроенного модуля.
Для внешнего задания используется тип external:
- job: TEST-external
type: external
dump_cmd: ''
storages:
….
Ключ dump_cmd содержит полную команду запуска внешнего скрипта.
После выполнения скрипт должен:
1. Cоздать готовый архив.
2. Вывести в stdout JSON следующего формата:
{
"full_path": "ABS_PATH_TO_ARCHIVE",
"basename": "BASENAME_ARCHIVE",
"extension": "EXTERNSION_OF_ARCHIVE",
"gzip": true/false
}
Параметры basename, extension и gzip используются для формирования конечного имени резервной копии.
Код возврата 0 означает успешное выполнение, а любое другое значение говорит об ошибке.
Пример внешнего модуля для etcd
Допустим, используется скрипт /etc/nxs-backup-ext/etcd.py, который создаёт snapshot etcd:
#! /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;
- при успешном выполнении использует тот же механизм обработки, что и для встроенных модулей, подставляя значение full_path в качестве tmp_path;
- при ошибке завершает выполнение задания и фиксирует результат в журнале.
Разработка и обновление
Разработка и сопровождение nxs-backup построены с использованием CI/CD. Изменения не вносятся непосредственно в скрипты на рабочих серверах. Код хранится в центральном GitLab-репозитории.
Pipeline выполняет сборку новых версий deb/rpm-пакетов, после чего они загружаются в соответствующие репозитории. На конечные серверы обновления доставляются через менеджер пакетов.
Такой процесс позволяет централизованно управлять версиями и обновлениями системы резервного копирования.
nxs-backup как open-source проект
nxs-backup распространяется как open-source проект и может использоваться для организации резервного копирования собственных проектов, а также дорабатываться под конкретные задачи.
Исходный код, документация по установке и настройке доступны в GitHub-репозитории nxs-backup.
Также доступен Docker-образ nxs-backup.
Оставьте заявку, чтобы обсудить проект
Спасибо за обращение
Мы свяжемся с вами в ближайшее время.