Юрфирма в Одинцово, 47 рабочих мест, Zimbra Network Edition. За лицензию там платили ровно за одну функцию — встроенный бэкап. В сентябре 2024 выяснилось, что экспорт работает, а восстановление — нет: процесс исправно стартовал и через пять часов падал с com.zextras.lib.Error.OperationBlockingError. Разбираю, что там было на самом деле и как мы вытащили 3,1 ТБ другим путём.
Коммерческий бэкап Zimbra тоже падает: OperationBlockingError
«У нас коммерческая лицензия, с бэкапом всё в порядке»
Эту фразу я слышу от каждого второго клиента на 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 ГБ в час.
Оставить комментарий