Бэкап Zimbra есть, а восстановиться нельзя: четыре причины

Автосервису на Авиамоторной два года каждую ночь уезжал архив почтового сервера на сетевое хранилище. Когда в июле 2021-го умер системный диск, я взял свежую копию и уверенно пошёл разворачивать. Через четыре часа стало понятно, что развернуть её нельзя вообще — и не по одной причине, а сразу по четырём.

Вопрос, на который у большинства нет ответа

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

В девяти случаях из десяти ответа не будет. Будет что-то вроде «ну, бэкап-то идёт», «места хватает», «задание зелёное». Это разные утверждения. Первое означает, что файлы куда-то пишутся. Второе — что там есть место. Третье — что программа не сообщила об ошибке. Ни одно из них не отвечает на вопрос, сможете ли вы вернуть почту в понедельник, если сервер умрёт в воскресенье.

Неприятность в том, что разница между «копия есть» и «восстановиться можно» обнаруживается ровно один раз — в тот день, когда уже поздно. И обнаруживается не как «ой, не получилось», а как многочасовое выяснение, почему не получилось, пока пятнадцать человек сидят без работы, а директор стоит рядом.

Дальше — конкретная авария, четыре независимые причины отказа и проверка, которая ловит всё это за один вечер вместо одной катастрофы.

Июль 2021, Авиамоторная: умер системный диск

Автосервис на 15 рабочих мест, два поста кузовного ремонта и склад запчастей, всё в одном здании на Авиамоторной. Почта — Zimbra 8.8.15 Open Source Edition на CentOS 7, физический сервер 2015 года, ящики небольшие, store 96 ГБ.

В понедельник утром сервер не загрузился. Системный SSD ушёл в аппаратный отказ — не деградация, а мгновенная смерть, диск не определялся в контроллере вовсе. Данные почты лежали на отдельном массиве и физически уцелели, но операционная система, конфигурация и база находились на том же системном диске.

Меня вызвали в 10:40. К этому моменту сотрудники сидели без почты уже два с половиной часа, и это было ощутимо: приёмка машин у автосервиса живёт на переписке со страховыми компаниями, а согласования по деталям идут почтой с фотографиями.

Бэкап был. Настроил его в 2019 году предыдущий подрядчик: ночной архив каталога /opt/zimbra и заливка на сетевое хранилище, глубина хранения 14 копий. Задание отработало и в ночь на понедельник, архив весил 104 ГБ, дата свежая. Отчёт по почте приходил каждое утро со словом OK.

Я поставил новую машину, распаковал архив и запустил. Дальше был длинный день.

Ложная версия: битый архив

Первая мысль была самая простая — архив повредился при передаче или на хранилище. Сетевое хранилище стояло в той же серверной, дешёвое, без контроля целостности, и версия выглядела правдоподобной.

Проверил в лоб:

$ tar -tzf /mnt/nas/zimbra/2021-07-19/opt-zimbra.tar.gz > /tmp/list.txt; echo $?
0
$ wc -l /tmp/list.txt
1284617 /tmp/list.txt
$ md5sum /mnt/nas/zimbra/2021-07-19/opt-zimbra.tar.gz
a3c9...  opt-zimbra.tar.gz

Архив читался целиком, распаковывался без единой ошибки, содержал больше миллиона файлов. Контрольная сумма совпадала с той, что лежала рядом с прошлой ночи. С архивом было всё в порядке.

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

Разница принципиальная. Целый архив и пригодный архив — не одно и то же. Проверка контрольной суммы отвечает только на вопрос «доехали ли байты». На вопрос «можно ли из этого собрать работающий сервер» она не отвечает никак.

Причина первая: чужое имя хоста

Новый сервер я назвал по-новому — mail2.service-avia.ru, потому что старый ещё формально существовал в документации, а мне нужно было развести их в голове. Ошибка на ровном месте.

Распакованный каталог /opt/zimbra на машине с другим именем не оживает. Внутри конфигурации, в объектах каталога, в путях и в сертификатах прошито старое имя, и никакой первый запуск это не пересчитает. Получил я вот такое:

$ su - zimbra -c 'zmcontrol start'
Host mail2.service-avia.ru
        Starting ldap...FAILED
Unable to determine enabled services from ldap.
Unable to determine enabled services. Cache is out of date or doesn't exist.

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

Правильный путь — один из двух, и оба надо выбирать заранее, а не в аварию:

  • Поднять новый сервер под тем же именем, что и старый, развернуть архив, и только потом, если нужно, менять имя штатной процедурой.
  • Поставить на чистую машину Zimbra ровно той же версии и на ту же операционную систему, дать ей то же имя, и уже поверх этой инсталляции восстанавливать данные.

Второй пункт содержит требование, о которое спотыкаются чаще всего: та же версия и та же ОС. Не «девятка вместо восьмёрки», не «Ubuntu вместо CentOS». Архив, снятый с 8.8.15 на CentOS 7, разворачивается на 8.8.15 на CentOS 7 — и точка. В июле 2021 года это ещё было выполнимо. Через три года, когда CentOS 7 умерла, тот же архив стал почти неразворачиваемым, и это отдельный повод пересмотреть схему, если она у вас такая же.

Я переименовал машину, переустановил всё заново и потерял на этом 2 часа 20 минут.

Причина вторая: копия снималась на живом сервере

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

InnoDB: Database page corruption on disk or a failed file read of page ...
InnoDB: Error: page ... log sequence number ... is in the future!
[ERROR] Plugin 'InnoDB' init function returned error.

Полез разбираться, как именно снимался архив, и нашёл скрипт предыдущего подрядчика. В нём не было ни одной команды остановки служб. Просто tar по живому каталогу, каждую ночь, два года подряд.

Что при этом происходит. Архиватор идёт по каталогу последовательно, файл за файлом, и на 104 гигабайтах это занимает больше часа. За этот час сервер продолжает работать: принимает почту, пишет в базу, создаёт новые файлы писем, удаляет старые. В результате база попадает в архив в состоянии, зафиксированном в 02:41, а файлы писем — в состоянии между 02:15 и 03:50. Согласованности между ними нет и быть не может.

Для базы это означает недописанные транзакции — то, что вы и видите в сообщении выше. Для хранилища — ссылки на файлы, которых нет, и файлы, на которые никто не ссылается.

Поднять базу удалось только через принудительное восстановление с постепенным повышением уровня, дампом и заливкой в чистую базу. Уровень 3 не помог, уровень 4 дал дамп с потерей части последних записей.

# /opt/zimbra/conf/my.cnf, временно
innodb_force_recovery = 4

# всё из-под zimbra: своя сборка MariaDB, свой сокет, свой пароль
$ mysqldump --all-databases \
    -S /opt/zimbra/data/tmp/mysql/mysql.sock \
    -u root -p$(zmlocalconfig -m nokey -s mysql_root_password) > /tmp/dump.sql
$ /opt/zimbra/libexec/zmmyinit --sql_root_pw '...'
$ mysql -S /opt/zimbra/data/tmp/mysql/mysql.sock -u root -p'...' < /tmp/dump.sql

Это сработало, но с потерями. Дамп с четвёртым уровнем восстановления обрубил хвост записей, накопившихся перед фиксацией базы в 02:41, — примерно с девяти вечера воскресенья. Плюс промежуток от копии до отказа диска в начале девятого утра. Вместе это и дало те одиннадцать часов переписки, которые потеряли безвозвратно. Файлы этих писем физически лежали на уцелевшем массиве, но без записи в базе они бесполезны: сервер не знает, чьё это письмо, в какой оно папке и от кого. Вернуть письмо в ящик можно только вместе с метаданными.

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

Причина третья: каталога учётных записей в архиве не было

Вот здесь пригодился список файлов, который я получил, пока проверял ложную версию. Посмотрел, что вообще есть в архиве. Пути внутри архива относительные, вида opt/zimbra/store/..., поэтому имя интересующего каталога — третье поле, а не второе; на этом легко ошибиться и получить одно слово zimbra на миллион строк:

$ awk -F/ '{print $3}' /tmp/list.txt | sort | uniq -c | sort -rn | head
 1198334 store
   72140 db
    9822 conf
    3901 ssl
      12 log
$ grep -c '^opt/zimbra/data/ldap/' /tmp/list.txt
0

Ноль. Каталога /opt/zimbra/data/ldap в архиве не было вовсе. Нашлась и причина — в том же скрипте предыдущего подрядчика стояло исключение по маске --exclude='*/data/*'. Судя по всему, автор хотел выкинуть временные файлы, а выкинул вместе с ними всю базу учётных записей. Два года никто этого не замечал, потому что архив исправно создавался и весил правдоподобно.

Что это означало практически: пятнадцати учётных записей, их паролей, алиасов, списков рассылки и настроек домена в бэкапе не существовало. Восстанавливать письма было некуда.

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

$ su - zimbra -c '/opt/zimbra/libexec/zmslapcat /mnt/nas/zimbra/ldap/'
$ su - zimbra -c '/opt/zimbra/libexec/zmslapcat -c /mnt/nas/zimbra/ldap/'
$ ls -lh /mnt/nas/zimbra/ldap/
-rw-r----- 1 zimbra zimbra 8,4M ldap.bak
-rw-r----- 1 zimbra zimbra  31K ldap-config.bak

Восемь мегабайт против ста четырёх гигабайт. Именно эти восемь мегабайт определяют, будет ли куда возвращать остальные сто четыре гигабайта.

Учётки в итоге пересоздавали руками. Список собрали из двух источников: из каталогов внутри store, где каждому ящику соответствует свой номер, и из адресной книги на компьютере бухгалтера. Пароли выдали новые, всем пятнадцати, с обходом по кабинетам.

Причина четвёртая: права на файлы и зелёный отчёт

Четвёртая причина мельче трёх предыдущих, но добавила часа три.

Распакованный архив принёс с собой числовые идентификаторы владельца со старого сервера. На новой машине пакет создал пользователя zimbra с другим номером — потому что порядок установки пакетов был иной. В итоге сотни тысяч файлов принадлежали никому:

$ ls -ln /opt/zimbra/store/0/2/msg/0/ | head -3
-rw-r----- 1 1001 1001  4821 июл 19 02:33 271-1234.msg
$ id zimbra
uid=996(zimbra) gid=994(zimbra)

Лечится штатно и быстро, но знать про это надо заранее:

# от root, после распаковки, до первого старта
/opt/zimbra/libexec/zmfixperms

И последнее, о чём хочу рассказать отдельно, потому что это самая тихая из всех бед. Отчёт о бэкапе приходил каждое утро со словом OK. Я поднял почтовый ящик, куда он падал, и просмотрел письма за полгода. В шестнадцати из них внизу, после сводки, была строка про один и тот же ящик, который не выгрузился. Один из пятнадцати. Сводка при этом считалась успешной, потому что скрипт смотрел на код возврата архиватора, а не на содержимое журнала.

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

Смотрите на журнал, а не на итоговую строку. И заведите правило: любое сообщение об ошибке внутри успешного задания разбирается в тот же день.

Проверьте у себя за две минуты

Эта проверка не отвечает на вопрос «есть ли бэкап» — она отвечает на вопрос «развернётся ли он». Выполняется на боевом сервере и на хранилище, ничего не меняет.

# 1. Есть ли в архиве каталог учётных записей
tar -tzf /mnt/nas/zimbra/latest/opt-zimbra.tar.gz \
  | grep -c '^opt/zimbra/data/ldap/'

# 2. Останавливаются ли службы при снятии копии
grep -nE 'zmcontrol|systemctl|snapshot|lvcreate' /путь/к/скрипту/бэкапа

# 3. Под каким номером живёт пользователь zimbra
id zimbra

# 4. Что за версия и ОС — их придётся повторить при восстановлении
su - zimbra -c 'zmcontrol -v'; cat /etc/redhat-release 2>/dev/null || lsb_release -d

# 5. Ошибки внутри «успешных» заданий
grep -iE 'error|failed|warn' /var/log/zimbra-backup.log | tail -20
Что увиделиЧто это значитНасколько срочно
Первая команда вернула 0Учётных записей в копии нет. Письма будет некуда возвращатьСегодня
В скрипте нет остановки служб и нет снимка томаКопия несогласованна: база и письма из разных моментовНа этой неделе
Номер пользователя zimbra нигде не записанПри восстановлении права на файлы поедутЗаписать сегодня, это одна строка
Версия ОС снята с поддержкиУстановить такую же для восстановления будет негдеПересматривать схему целиком
В журнале есть ошибки, а отчёт «успешно»Часть ящиков не копируется месяцамиРазобрать в тот же день
Никто ни разу не разворачивал копиюУ вас не бэкап, а предположение о бэкапеНазначить дату учения

Первая строка — самая частая находка. Из последних одиннадцати клиентов, у которых я это проверял, каталог учётных записей отсутствовал в копии у четверых.

Чем кончилось, сколько стоило и что делать вам

Итог. Полное восстановление заняло 31 час чистой работы за четверо суток: чистая установка 8.8.15 на CentOS 7 под старым именем, исправление прав, подъём базы принудительным восстановлением, ручное создание 15 учёток, заливка писем из уцелевшего хранилища.

Вернули 94% писем по объёму. Потеряли безвозвратно: письма за последние 11 часов, структуру папок у восьми ящиков из пятнадцати, пометки «прочитано», серверные фильтры, общие календари двух приёмщиков.

Цифры для владельца:

СтатьяЗначение
Полный простой почты1 день 6 часов
Работа в ограниченном режимеещё 3 дня
Простой персонала15 человек × 10 часов ≈ 150 человеко-часов
Оценка упущенной работыоколо 82 000 ₽
Восстановительные работы31 час
Два срыва сроков со страховойоценке не поддалось

Профилактика стоила бы вечер на проверку выше и четыре часа на переделку схемы. Один к двадцати.

Что поставили после аварии: холодная копия с простоем в пять минут, ежедневная выгрузка каталога учёток, записанные рядом с архивом версия, ОС и номер пользователя zimbra, и раз в квартал — разворачивание одного ящика на тестовой машине.

Последний пункт — единственный, который что-то доказывает. Проверить бэкап можно только восстановлением. Остальное — косвенные признаки.

И контр-совет к популярному «главное, чтобы копия лежала в другом месте». Разнесение важно, спорить не буду. Но копия, которая лежит в трёх городах и не разворачивается ни в одном, стоит столько же, сколько её отсутствие, только дороже в хранении. Сначала восстановимость, потом география.

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

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

Почему архив /opt/zimbra нельзя развернуть на сервере с другим именем?

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

Насколько важно совпадение UID пользователя zimbra?

Достаточно важно, чтобы записать его в файл рядом с архивом прямо сегодня — это одна строка вывода команды id zimbra. В распакованном архиве владелец файлов хранится числом, а на новой машине пакет может создать пользователя с другим номером: зависит от того, в каком порядке ставились пакеты. В результате сотни тысяч файлов почты оказываются принадлежащими несуществующему пользователю, и сервер их не читает. Лечится штатной утилитой /opt/zimbra/libexec/zmfixperms, запускать её надо от root после распаковки и до первого старта. У меня на этом ушло около трёх часов только потому, что я не подумал про права сразу.

Можно ли снимать копию без остановки служб, если сервер маленький?

Размер сервера тут ни при чём — важна длительность копирования. Архиватор обходит каталог за десятки минут, и всё это время сервер продолжает писать в базу и создавать файлы писем. База попадает в копию в одном моменте времени, письма — в диапазоне, и согласованности между ними нет. Иногда такая копия разворачивается нормально, иногда база требует принудительного восстановления с потерей последних записей — именно это и случилось у автосервиса. Правильный минимум: остановить службы на время создания снимка тома, а копировать уже со снимка. Простой при таком порядке — около пяти минут в сутки.

Отчёт о бэкапе приходит каждое утро со словом «успешно». Разве этого мало?

Мало, потому что «успешно» обычно означает лишь то, что архиватор вернул нулевой код. У клиента отчёт приходил два года, и в шестнадцати письмах за полгода внутри сводки была строка про ящик, который не выгружался, — но итоговая строка оставалась зелёной. То же бывает и со штатными средствами: задание стабильно падает на одном аккаунте с ошибкой вроде NoSuchFieldError: revision, остальные проходят, отчёт выглядит почти нормальным. Проблема в слове «почти»: обычно это самый большой и старый ящик, то есть директора или бухгалтера. Читайте журнал, а не итог, и разбирайте любую ошибку внутри успешного задания в тот же день.

Как проверить восстановимость, не устраивая полное учение?

Минимальная честная проверка занимает вечер. Берёте тестовую машину, ставите Zimbra той же версии и на ту же ОС под тем же именем, распаковываете туда архив, исправляете права и поднимаете службы. Дальше открываете один ящик и читаете письмо годичной давности. Если дошли до этого — восстановимость есть. Если нет, вы узнали это в спокойной обстановке, а не в аварию. Отдельно проверьте наличие в архиве каталога учётных записей одной командой tar -tzf ... | grep -c 'data/ldap/': ноль на выходе означает, что письма восстанавливать будет некуда, и это самая частая находка из всех.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#Zimbra#восстановление#бэкап#авария#LDAP
Комментарии 0

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

загрузка...

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.