Бэкап ERPNext: bench backup, restore и проверка копии
АйТи Фреш
Linux, Docker и DevOps

Бэкапы ERPNext: bench backup, восстановление и проверка, что копия живая

Автор: , директор ООО «АйТи-Фреш» · · ~17 мин чтения
Две папки архива: полная с чертежами и пустая, лупа и штамп проверки — резервная копия ERPNext
Копия без вложений похожа на папку с пустыми обложками.

Бэкап ERPNext делает команда bench backup, но без флага --with-files она сохраняет только базу и site_config: вложения останутся без копии. Ниже: что копировать, как поставить расписание, где хранить, как восстановить через bench restore и как проверить, что копия живая.

Что нужно копировать в ERPNext: база, файлы и настройки сайта

Данные ERPNext живут в трёх местах, и копировать нужно все три. Первое — база данных сайта: документы, справочники, проводки, настройки. Второе — файлы: вложения, которые пользователи прикрепляют к документам, лежат не в базе, а на диске в каталогах public/files и private/files внутри папки сайта. Третье — конфигурация сайта site_config.json, где хранятся параметры подключения и ключ шифрования. Если вы разворачиваете систему по схеме ERPNext под ключ, я включаю проверку всех трёх составляющих в приёмку, а условия сопровождения смотрите на странице продукта.

Команда bench backup по умолчанию снимает только базу. Это видно в исходниках Frappe версии 15: функция backup в модуле commands/site.py передаёт в генератор копий параметр ignore_files=not with_files, то есть пока вы не указали --with-files, файлы игнорируются. На выходе получается набор файлов в каталоге sites/<сайт>/private/backups: архив базы ...-database.sql.gz и копия конфигурации ...-site_config_backup.json. С флагом --with-files добавляются два архива, ...-files.tar для публичных и ...-private-files.tar для приватных вложений, а с флагом --compress вместо tar получается tgz.

Приватные файлы — самая опасная часть. Счета, акты, сканы договоров и фотографии с объектов обычно прикрепляют к документам, и в базе остаётся лишь ссылка на файл. Если восстановить только дамп, документ откроется, а при попытке скачать вложение вы получите ошибку. Поэтому в моих проектах «копия» всегда означает комплект из четырёх файлов с одинаковой временной меткой в имени, а не один архив базы. Ниже таблица, которая помогает быстро понять, что вы получите при разном запуске.

| Команда | База | Публичные файлы | Приватные файлы | site_config | |---|---|---|---|---| | bench --site сайт backup | да | нет | нет | да | | bench --site сайт backup --with-files | да | да | да | да | | bench --site сайт backup --with-files --compress | да | да (tgz) | да (tgz) | да | Таблица составлена по параметрам команды в версии 15. В своей версии сверьте вывод bench backup --help: набор флагов с годами менялся.

Как сделать бэкап командой bench backup и поставить его на расписание

Разовая копия делается из каталога bench под тем пользователем Linux, которому принадлежит установка. Если запустить команду от root, файлы получат неправильного владельца, и потом обычный пользователь не сможет их прочитать или удалить. Рабочий вариант для установки без контейнеров такой:

cd ~/frappe-bench
bench --site erp.example.com backup --with-files --compress
ls -lh sites/erp.example.com/private/backups/

После команды в каталоге должны лежать четыре свежих файла. Размер архива базы сравните со вчерашним: если он резко уменьшился, что-то не так с самой копией. Флаг --backup-path позволяет сразу писать комплект в другой каталог, например на примонтированный диск, а --exclude исключает тяжёлые журнальные DocType из дампа.

Расписание в обычной установке ставит команда bench setup backups. Я прочитал её код в репозитории bench: она добавляет в crontab пользователя задачу «каждые 6 часов» с командой bench --verbose --site all backup. Обратите внимание: там нет --with-files. Значит, штатное расписание копирует только базу, а вложения остаются без защиты. Свою строку лучше написать самостоятельно:

# crontab -e под пользователем bench
0 2 * * * cd /home/frappe/frappe-bench && /usr/local/bin/bench --site all backup --with-files >> logs/backup.log 2>&1

Время 02:00 условное, выберите окно, когда никто не работает: на крупных базах дамп заметно грузит процессор, а tar по вложениям нагружает диск.

Для Docker-установки принцип тот же, но команда выполняется внутри контейнера. В документации frappe_docker для этого приведён пример строки cron: docker compose -p erpnext exec backend bench --site all backup --with-files. А вот скрипт easy-install, которым многие ставят ERPNext и Frappe CRM за пять минут, подключает контейнер с планировщиком ofelia и расписанием @every 6h, и команда у него bench --site all backup без файлов. Проверьте, что получилось у вас: откройте файл .env проекта и посмотрите на переменную BACKUP_CRONSTRING, затем зайдите в private/backups и убедитесь, что рядом с базой лежат tar-архивы вложений. Если их нет, расписание нужно дополнить.

Отдельная мелочь: bench --site all backup снимает все сайты в bench, а --site имя только один. Для компании с одним сайтом разницы нет, но при нескольких тестовых сайтах рядом вы будете копировать лишнее и заполните диск. Я оставляю --site all только на выделенном сервере, где живёт один рабочий сайт.

Схема ночного копирования ERPNext: cron, bench backup с файлами, проверка, вынос на второй сервер
Каждый шаг цепочки должен оставлять проверяемый след.

Где хранить копии: retention, шифрование и резервный сервер

Копия, которая лежит рядом с базой на том же диске, не защищает от потери диска, ошибки администратора и шифровальщика. Правило простое: минимум одна копия вне сервера. Общий принцип хорошо описан в статье про правило бэкапа 3-2-1, а здесь я разберу то, что специфично для Frappe. Каталог sites/<сайт>/private/backups — это только место, куда команда складывает комплект. Выносить его на второй сервер нужно отдельной задачей: rsync по ключу, restic в объектное хранилище или копирование на NAS.

Теперь про глубину хранения. В главе нашей базы знаний было написано, что bench backup удаляет архивы старше суток. В исходниках версии 15 я такого правила для самой команды не нашёл. Нашёл другое: в System Settings есть поле «Number of Backups» (имя поля backup_limit, значение по умолчанию 3), а в расписании hourly_maintenance стоит задача delete_downloadable_backups. Она раз в час оставляет в private/backups только последние N комплектов и удаляет остальные. Следствие неочевидное: считается число комплектов, а не дни. Ночная копия с лимитом 3 даёт три ночи, а штатный запуск раз в 6 часов даёт всего 18 часов истории.

Из этого вытекает практический вывод: локальную глубину поднимайте осознанно, а долгую историю храните на втором сервере со своей политикой очистки. Для небольшой компании я обычно ставлю «Number of Backups» от 7 до 14, а на резервном сервере держу ежедневные копии за 30 дней и еженедельные за три месяца. Эти числа — мои рабочие ориентиры, а не правило Frappe: глубина зависит от того, как быстро вы замечаете ошибку в данных. Если бухгалтер обнаруживает расхождение раз в месяц, недельной глубины мало.

Шифрование включается в System Settings флажком «Encrypt Backups» (поле encrypt_backup). Тогда файлы получают в имени метку -enc, а при запуске команда напоминает записать ключ шифрования. Ключ лежит в site_config.json как encryption_key, и если потерять сервер вместе с ним, зашифрованная копия станет бесполезной. Храните ключ отдельно от копий, в менеджере паролей. При восстановлении ключ можно передать параметром --encryption-key. Помните и обратную сторону: копия site_config_backup.json содержит секреты, поэтому передавать её на сторонний сервер без шифрования нельзя.

Одна копия на том же диске — это не резервная копия, а удобство при откате. Держите хотя бы одну копию на другом сервере и сверяйте её дату раз в неделю.

Как восстановить сайт: bench restore, флаг --force и разбор типичных ошибок

Восстановление начинается с команды bench restore, и сразу предупреждаю: она перезаписывает базу сайта. В коде версии 15 видно, что восстановление идёт через пересоздание базы с параметром force, поэтому на живой сервер без предварительной копии текущего состояния запускать её нельзя. Сначала снимите свежий комплект того, что есть сейчас, даже если оно повреждено. Затем выполните:

cd ~/frappe-bench
bench --site erp.example.com restore \
  sites/erp.example.com/private/backups/20261001_020000-erp_example_com-database.sql.gz \
  --with-public-files sites/erp.example.com/private/backups/20261001_020000-erp_example_com-files.tar \
  --with-private-files sites/erp.example.com/private/backups/20261001_020000-erp_example_com-private-files.tar

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

На новом сервере порядок такой. Ставите ту же версию Frappe и ERPNext, что была на источнике, создаёте пустой сайт с тем же именем, копируете комплект и запускаете restore с путями ко всем трём файлам. Если версия новой установки ниже версии копии, команда предупредит о понижении и спросит подтверждение. Здесь и работает флаг --force: по описанию в коде он отключает проверки и предупреждения о downgrade. Это не лечебная кнопка. В форумных рецептах его советуют при ошибке Table 'tabDefaultValue' doesn't exist, но я проверил логику: перед загрузкой команда ищет в дампе таблицу __Auth, и --force лишь пропускает эту проверку, а повреждённый файл от этого не оживёт.

Поэтому при ошибках я иду по порядку, а не включаю force. Сначала проверяю целостность архива командой gzip -t файл.sql.gz. Потом смотрю, что запустил restore тот пользователь, у которого есть доступ к MariaDB, и что передан пароль root базы, если нужно создавать базу заново (параметры --db-root-username и --db-root-password). Затем сверяю версии: восстановление копии версии 15 на сервер с версией 14 невозможно без миграции. И только если проверка файла не прошла, а нужные данные все равно есть, я иду на осознанный риск с --force и затем внимательно проверяю результат.

Что видноВероятная причинаЧто делать
Table 'tabDefaultValue' doesn't existБитый или неполный дамп, не те права на базуgzip -t, проверить права MariaDB, взять другой комплект
Документы есть, вложения не открываютсяВосстановили только базуПовторить restore с --with-public-files и --with-private-files
Запрос подтверждения про downgradeВерсия копии новее установленнойПоставить ту же версию, а не обходить через --force
Нет стилей, 404 на CSS и JSПо сообщениям на форуме, не собраны ассеты после переносаВыполнить bench build, перезапустить процессы, проверить в своей версии
Дерево решений при ошибках restore в ERPNext: симптом, причина, действие
Сначала диагностика, и только потом флаг --force.

Как убедиться, что копия живая: пробное восстановление на стенде

Копия, которую ни разу не разворачивали, остаётся предположением. Единственная проверка, которой я верю, — пробное восстановление на отдельном стенде. Для малой компании хватает недорогой виртуальной машины с той же версией ERPNext. Порядок действий короткий: берёте последний комплект с резервного сервера, а не с рабочего, разворачиваете его на стенде и проверяете три вещи. Открывается ли список документов за последний день. Скачивается ли вложение из недавней заявки. Совпадает ли число записей в нескольких ключевых DocType с рабочей системой. Если одна из проверок не прошла, копия не считается рабочей, и я разбираюсь с причиной сразу.

Есть и сценарий проверки без отдельного стенда, он описан в нашей главе: удалить тестовый документ, выполнить restore и убедиться, что документ вернулся. Я использую его только на тестовом сайте, потому что на рабочем это та же перезапись базы. Подробнее про принципы проверки копий я писал в статье про тест восстановления бэкапа, а здесь важен конкретный набор проверок именно для ERPNext: открыть вложение, проверить печатную форму, зайти под обычной ролью и убедиться, что список проектов не пуст.

Регламент проверок я записываю в одну таблицу и вешаю на мониторинг. Ежедневно скрипт сверяет, что свежий комплект появился и его размер не упал против вчерашнего более чем на заданную долю. Раз в неделю проверяется, что копия дошла на второй сервер. Раз в месяц делается пробное восстановление с замером времени: это время и есть ваш реальный срок возврата к работе. Перед каждым обновлением версии снимается внеплановый полный комплект с вложениями. Команда bench update сама снимает копию сайтов перед обновлением, а флаг --no-backup, который это отключает, в описании прямо помечен как не рекомендуемый для production. Я его на рабочих серверах не использую.

Не забудьте про Docker. Если сайт работает в контейнерах, то команда docker compose down -v удалит именованные тома вместе с базой и файлами, это прямо описано в документации Docker к ключу -v. Перед любой работой с контейнерами убедитесь, что свежий комплект уже лежит вне тома. Я добавляю в регламент отдельную строку: «ключ -v не используем никогда на рабочем проекте».

Таймлайн регламента проверки резервных копий ERPNext: ежедневно, еженедельно, ежемесячно
Регламент работает, когда у каждой проверки есть периодичность и ответственный.

Как «Электромонтаж Ритм» на 15 рабочих мест нашёл дыру в бэкапе до аварии

Разберу условный пример: подрядчик по электромонтажу «Электромонтаж Ритм», 15 рабочих мест, ERPNext в Docker на одном виртуальном сервере. Система стояла по схеме быстрой установки с готовым расписанием копий каждые шесть часов. Администратор был уверен, что копии есть: каталог private/backups пополнялся, размеры файлов выглядели правдоподобно. Числа ниже условные, они нужны для иллюстрации хода рассуждений, а не как данные реального проекта.

При первой плановой проверке копию развернули на тестовой машине. Документы открылись, проводки совпали, но в заявках на материалы не открывались сканы счетов и фотографии актов: ссылка есть, файла нет. Причина обнаружилась быстро: расписание запускало bench --site all backup без --with-files, поэтому за все месяцы работы вложения не копировались ни разу. На рабочем сервере к тому моменту накопилось около 9 ГБ вложений, и вся эта история существовала в единственном экземпляре. Дополнительно выяснилось, что поле «Number of Backups» оставалось равным 3, и глубина локальной истории составляла 18 часов.

Починили в три шага. Расписание заменили на ночное с --with-files, а лимит в System Settings подняли до 10. Задачей rsync комплект стал уезжать на второй сервер в другой площадке. Раз в месяц администратор запускает пробное восстановление и записывает время: на условном стенде оно заняло около 40 минут вместе с вложениями. Отдельным пунктом договорились, что перед каждым обновлением ERPNext снимается внеплановый полный комплект, а восстановление проверяет не тот, кто настраивал расписание, а второй сотрудник, чтобы ошибка не повторилась из-за привычки.

Чего этот регламент не закрывает

Любой логический дамп через bench backup сохраняет состояние на момент снятия, но не журнал изменений между копиями. Если копия ночная, а ошибку в данных сделали в 16:00, при возврате вы потеряете работу всего дня. Для малой компании это обычно приемлемо, но решение должно быть принятым осознанно: допустимую потерю нужно проговорить с директором до аварии, а не после. Если потеря дня недопустима, нужна другая схема: репликация MariaDB или копирование бинарных журналов, а это отдельный проект с отдельным администрированием.

Второе ограничение — размер. Для крупных баз дамп грузит сервер, а в нашей главе упомянуто, что на больших системах рекомендуют физические копии через Mariabackup. Я встречал на форумах рецепты, где для баз больше гигабайта восстанавливают дамп напрямую через клиент mysql, а не через bench restore. Эти советы относятся к старым версиям. Прежде чем на них опираться, проверьте их на своей версии и на тестовой копии. Для компании на 10–30 пользователей такой размер базы встречается редко, и обычный bench backup справляется.

Третье: копия сервера и копия данных — разные вещи. Комплект bench backup не восстановит операционную систему, настройки nginx, обратного прокси и сертификаты. Эти файлы нужно хранить отдельно, в репозитории конфигураций. Подробнее про общую стратегию копирования Linux-серверов — в материале про бэкап MySQL и MariaDB. И наконец, ответственность за проверку лежит на человеке. Ни одна команда не заменит живого сотрудника, который раз в месяц разворачивает копию и открывает вложение.

Частые вопросы

Какая команда делает резервную копию ERPNext?

Штатная команда: bench --site имя_сайта backup. По умолчанию она сохраняет только базу и копию site_config в каталог sites/имя_сайта/private/backups. Чтобы в копию попали вложения, добавьте флаг --with-files. Запускайте её от пользователя Linux, которому принадлежит каталог bench, иначе созданные файлы получат неправильного владельца и потом не читаются.

Как восстановить ERPNext из бэкапа?

Командой bench --site имя_сайта restore путь_к_sql.gz, дополнительно указав --with-public-files и --with-private-files с путями к tar-архивам. Команда перезаписывает базу сайта, поэтому перед запуском снимите копию текущего состояния. На новом сервере сначала поставьте ту же версию Frappe и ERPNext и создайте пустой сайт с тем же именем.

Сколько хранить бэкапы ERPNext и как часто делать?

Локальную глубину задаёт поле Number of Backups в System Settings, по умолчанию 3 комплекта, и считаются именно комплекты, а не дни. Для небольшой компании обычно хватает ночной копии, а вторую копию с историей 30 дней держат на другом сервере. Допустимую потерю данных согласуйте с руководителем заранее.

Почему после восстановления ERPNext пропали вложения?

Чаще всего сохранили только дамп базы. Вложения лежат в каталогах public/files и private/files, а не в базе, и попадают в копию только с флагом --with-files. Проверьте, есть ли рядом с архивом базы файлы files.tar и private-files.tar, и повторите восстановление с обоими параметрами.

Можно ли бэкапить ERPNext в Docker так же, как обычную установку?

Команды те же, но запускаются внутри контейнера backend, например docker compose -p erpnext exec backend bench --site all backup --with-files. Копии лежат в томе sites, поэтому их нужно выносить на другой сервер. Команда docker compose down -v удаляет тома с базой и файлами, не используйте её на рабочем проекте.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи