Коммерческий бэкап Zimbra тоже падает: OperationBlockingError

Юрфирма в Одинцово, 47 рабочих мест, Zimbra Network Edition. За лицензию там платили ровно за одну функцию — встроенный бэкап. В сентябре 2024 выяснилось, что экспорт работает, а восстановление — нет: процесс исправно стартовал и через пять часов падал с com.zextras.lib.Error.OperationBlockingError. Разбираю, что там было на самом деле и как мы вытащили 3,1 ТБ другим путём.

«У нас коммерческая лицензия, с бэкапом всё в порядке»

Эту фразу я слышу от каждого второго клиента на Network Edition. Логика понятная: за лицензию платят деньги, в лицензии есть модуль резервного копирования, модуль показывает зелёные галочки. Значит, вопрос закрыт.

Вопрос не закрыт. Резервное копирование и восстановление — две разные операции, и проверка первой ничего не говорит о второй. Экспорт пишет данные линейно, порциями, в удобном ему темпе. Восстановление разбирает эти порции обратно в базу, держит открытыми тысячи объектов и работает часами подряд без единой точки сохранения. Ломается оно в других местах и по другим причинам.

Если вы ни разу не запускали именно восстановление — у вас нет данных о том, работает ли оно. Не «скорее всего работает». Просто нет данных.

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

Дальше — как это выглядело у конкретного клиента.

Сентябрь 2024, юрфирма в Одинцово: 61 ящик и 3,1 ТБ

Юридическая фирма, офис в Одинцово, 47 рабочих мест. Почта на Zimbra 9 Network Edition, Ubuntu, виртуалка в арендованной стойке. Хозяйство приличное для такого размера конторы:

  • 61 ящик, из них 12 общих по направлениям практики;
  • store — 3,1 ТБ, самый большой ящик управляющего партнёра — 254 ГБ;
  • экспорт коммерческого модуля лежал на NFS-шаре сетевого хранилища, занимал 940 ГБ;
  • журнал заданий чистый, SmartScan — инкрементальное сканирование изменений у Zextras — отрабатывал каждую ночь.

Экономия по диску, кстати, честная. За счёт дедупликации и сжатия коммерческий модуль ужимает архив примерно на 70 процентов — здесь 3,1 ТБ превратились в 940 ГБ, и это ровно то, за что люди платят. Претензий к экспорту у меня нет ни одной.

Позвали нас не по аварии. Юристы переезжали на новое железо, и системный интегратор, который вёл сервер до нас, честно сказал: «переносите через восстановление из бэкапа, это штатный путь». Мы согласились. Штатный так штатный.

Первый прогон запустили в среду в 19:40.

Пять часов работы и одна строка в логе

Процесс стартовал нормально. Счётчик обработанных объектов рос, нагрузка ровная, диск не забивался. В 22:15 я ушёл спать, оставив инженера смотреть.

В 00:31 он написал: упало.

com.zextras.lib.Error.OperationBlockingError: Blocking operation error
        at com.zextras.lib.Error.OperationBlockingError.<init>
        ...
Restore operation terminated with errors

Четыре часа пятьдесят одна минута работы — и одна строка. Ни номера аккаунта, на котором встало, ни объекта, ни внятного кода. Просто «блокирующая ошибка операции».

Что особенно неприятно: восстановление не докладывает, докуда оно дошло. Нельзя продолжить с места обрыва. Целевой сервер оказался в состоянии «часть ящиков есть, часть неполные, какие именно — неизвестно». Пришлось сносить и начинать заново.

Второй прогон, в четверг вечером, упал через 5 часов 12 минут с той же ошибкой. Третий — через 4 часа 38 минут.

Ложный след: я сутки грешил на NFS

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

Мы проверили всё, до чего дотянулись:

  • dmesg и /var/log/syslog на предмет nfs: server not responding — чисто за весь период;
  • счётчики nfsstat -c до и после падения — ретрансмиссий почти нет;
  • перемонтировали шару с увеличенными таймаутами и hard,intr;
  • для чистоты эксперимента скопировали весь архив, все 940 ГБ, на локальный диск целевой машины и запустили восстановление оттуда.

С локального диска упало точно так же. Через 4 часа 44 минуты, та же строка.

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

Что там на самом деле: длинная операция съедает саму себя

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

Отсюда следует лечение, которое звучит унизительно просто: не запускать одну операцию на несколько часов. Дробить.

Мы разбили 61 ящик на семнадцать партий по три-четыре штуки, отсортировав по объёму так, чтобы гиганты не собирались в одной. Каждая партия — отдельный запуск, между ними перезапуск службы, чтобы счётчики обнулились.

# состояние архива и список того, что в нём есть
zxsuite backup getBackupInfo

# восстановление одного аккаунта партии в новый ящик
zxsuite backup doRestoreOnNewAccount \
    partner@example.ru partner_new@example.ru

# между партиями
su - zimbra -c 'zmmailboxdctl restart'

Если нужна не последняя копия, а состояние на дату, к команде добавляется атрибут восстановления на момент времени. Точный его формат я советую брать не из статей в интернете, включая эту, а из zxsuite backup help doRestoreOnNewAccount на своём сервере: между версиями модуля он менялся, и ошибка в нём молча даёт не ту дату.

Партия из трёх-четырёх ящиков — это 150–200 ГБ данных, и шла каждая по два — два с половиной часа. За ночь мы прогоняли по четыре-пять. Ни одна партия не упала: до потолка, на котором ломается длинная операция, они просто не доживали. Общее время — 38 часов чистой работы, растянутых на четыре ночи, потому что днём сервер-источник должен был обслуживать людей. Это около 80 ГБ в час — та самая реальная скорость, которую до всей истории никто не знал.

Никакой магии тут нет. Мы просто перестали давать процессу шанс дойти до состояния, в котором он ломается.

Ящик, который падал всегда

Один аккаунт не проходил ни в какой партии. Каждый раз одно и то же:

Full backup finishing with errors
java.lang.NoSuchFieldError: revision
        account: p.morozova@example.ru

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

Вытащили родным средством Zimbra, в обход коммерческого модуля целиком:

# на сервере-источнике
/opt/zimbra/bin/zmmailbox -z -m p.morozova@example.ru -t 0 \
    getRestURL "//?fmt=tgz" > /backup/p.morozova.tgz

# на целевом
/opt/zimbra/bin/zmmailbox -z -m p.morozova@example.ru -t 0 \
    postRestURL "//?fmt=tgz&resolve=reset" /backup/p.morozova.tgz

Ключ -t 0 здесь обязателен — это бесконечный таймаут. Без него экспорт большого ящика оборвётся на середине по умолчанию. А resolve=reset полностью очищает целевой ящик перед импортом, поэтому направлять такую команду в боевой ящик с живыми письмами нельзя ни при каких обстоятельствах. Мы лили в пустой, только что созданный.

Ящик на 34 ГБ уехал за 57 минут и открылся полностью.

И каталог упал с тем же классом ошибки

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

OperationBlockingError
  caused by: LDAPException: connection closed while waiting for a response
  LDAP backup Failed

Связь с каталогом закрывалась в процессе снятия резервной копии. Разбираться с причиной внутри коммерческого модуля я не стал — там нечего крутить. Вместо этого поставил рядом штатную выгрузку каталога средствами самой Zimbra, которая работает независимо от лицензии:

su - zimbra
/opt/zimbra/libexec/zmslapcat /backup/ldap/$(date +%F)
# на целевом сервере, если каталог придётся поднимать с нуля:
# /opt/zimbra/libexec/zmslapadd /backup/ldap/2024-09-14/ldap.bak

Файл вышел 27 МБ. Двадцать семь мегабайт, которые снимают риск потерять 61 учётку, домены, алиасы, классы обслуживания и списки рассылки. У клиента, который платит за коммерческий бэкап, этой выгрузки не было вообще — она же «не нужна, у нас лицензия».

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

Если у вас Network Edition и вы уверены в бэкапе — три команды и один вопрос.

su - zimbra -c 'zmcontrol -v'
su - zimbra -c 'zxsuite backup getBackupInfo'
grep -ril 'OperationBlockingError\|NoSuchFieldError' /opt/zimbra/log/ | head

Вопрос: какого числа вы последний раз восстанавливали из этого архива хотя бы один ящик — не проверяли отчёт, а именно восстанавливали?

Что получилосьЧто это значит
Отчёты зелёные, восстановление не запускалось ни разуПроверена только половина цепочки. Ровно как в Одинцово: экспорт был безупречен три года подряд.
grep нашёл OperationBlockingErrorОшибка уже случалась. Найдите, на какой операции — на бэкапе каталога или на восстановлении, это разные истории.
grep нашёл NoSuchFieldError: revisionЕсть аккаунт, который в полный бэкап не попадает. Отчёт при этом может выглядеть почти нормально — «finishing with errors».
Архив лежит на NFS-шареСамо по себе не приговор, у нас с локального диска падало так же. Но проверять восстановление придётся именно с той шары, где он лежит.
Отдельной выгрузки каталога в LDIF нетУчётки, домены и алиасы держатся на одном механизме, который падает по той же причине, что и всё остальное.

Чем кончилось и что я поменял в регламенте

Переезд занял 38 часов чистой работы за четыре ночи вместо запланированной одной. Два инженера, ночные смены, один ящик вытащен в обход коммерческого модуля. Смета была на 87 000 ₽ — одна ночь миграции по штатному пути. Счёт вышел на 347 000 ₽, ровно вчетверо. Я не считаю, что мы ошиблись в оценке: мы ошиблись в том, что поверили слову «штатный».

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

СтатьяЗначение
Простой персонала47 человек × 24 рабочих часа ≈ 1 130 человеко-часов
Час сотрудника с накладными (их же цифра)1 400 ₽
Неотработанное времяоколо 1,58 млн ₽
Работы по восстановлению по факту347 000 ₽ вместо 87 000 ₽
Пропущенный срок по одному делу в работе2,4 млн ₽ — цифру назвал управляющий партнёр

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

После истории в регламент клиента вошли четыре пункта:

  • восстановление гоняется партиями по три-четыре ящика, а не целиком, — даже когда «должно пройти»;
  • раз в квартал вслепую восстанавливается один случайный ящик в тестовую учётку, с замером времени;
  • каталог выгружается в LDIF ежесуточно, отдельно от коммерческого модуля;
  • для трёх самых больших ящиков дополнительно снимается tgz родными средствами.

Коммерческую лицензию мы не отменяли — она даёт удобство, дедупликацию и экономию 70 процентов диска. Просто перестали считать её единственной опорой.

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

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

Если восстановление падает, значит архив повреждён?

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

Почему нельзя продолжить восстановление с места обрыва?

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

Стоит ли вообще платить за Network Edition, если бэкап так себя ведёт?

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

Что делать с аккаунтом, который падает с NoSuchFieldError: revision?

Не пытаться его починить внутри модуля — предсказуемого инструмента для этого нет. Вытаскивайте такой ящик отдельно, родными средствами Zimbra: getRestURL с бесконечным таймаутом на источнике и postRestURL на приёмнике. У нас ящик на 34 ГБ уехал за 57 минут и открылся без потерь. Важная деталь: импортировать с resolve=reset можно только в пустой, специально созданный ящик — этот режим полностью очищает содержимое цели перед записью. В боевой ящик с живой перепиской такую команду направлять нельзя.

Как понять заранее, что восстановление не пройдёт, не тратя ночь?

Полной гарантии не даст ничего, но дешёвая проба есть. Возьмите средний по объёму ящик и восстановите его в новую учётку — это 30–60 минут. Затем возьмите три ящика разом и посмотрите, как ведут себя память и время. Если время растёт не линейно, а быстрее объёма — полный прогон почти наверняка упрётся в потолок, и его сразу надо планировать партиями. Заодно вы получите реальную цифру скорости на своём железе, из которой считается срок восстановления всего парка. У юрфирмы эта цифра была примерно 80 ГБ в час.

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

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

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

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

загрузка...

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

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

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

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