К клиенту-производству в Химках я за девять месяцев приезжал трижды. Утром не открывалась почта, потом не синхронизировались телефоны, потом перестал работать поиск. Три разных симптома, три разных счёта. Причина была одна, и я нашёл её только на третий раз. После этого и появился регламент из девяти команд, который занимает полчаса раз в месяц.
Ежемесячный чек-ап Zimbra: девять команд и таблица трактовки
Три выезда, три счёта, одна причина
Есть клиенты, у которых почта ломается всегда по-разному. Каждый раз новый симптом, каждый раз «ну надо же, а раньше такого не было», каждый раз выезд. Со стороны это выглядит как невезение или как старое железо. Обычно это ни то ни другое.
Если вы вызываете подрядчика к почтовому серверу два-три раза в год и каждый раз с новой формулировкой — почти наверняка у вас одна причина, которая проявляется по-разному в зависимости от того, что в этот момент нагрузило сервер. Найти её по одному симптому нельзя: каждый симптом честно объясняется сам собой, лечится за несколько часов и уходит. По трём — можно, если сесть и разложить их рядом на одном листе.
Мешает этому простая вещь. Заявки разделены во времени, между ними месяцы, и к третьей никто уже не помнит подробностей первой. Помнит только сумму счетов.
Я разложил три заявки рядом в апреле 2026 года и потратил на разбор целый день без счёта клиенту. Потому что мне самому было неловко.
Химки, производство на 46 мест: как выглядели три заявки
Клиент — производственная компания, цех и офис на территории бывшего завода в Химках, 46 рабочих мест. Почта на Zimbra 9 с 2021 года, виртуалка на своём гипервизоре. Всё вело моё подразделение, так что чужого админа тут винить не в кого.
Июль 2025. Утро понедельника, веб-почта не открывается ни у кого. В логе:
2025-07-14 08:41:22,619 ERROR [qtp1740189450-231:https://mail.example.ru/service/soap/]
[] system - Fatal error occurred while handling exception
java.lang.OutOfMemoryError: Java heap space
Сам перезапуск mailboxd занял четыре минуты, всё поднялось. Три часа — это от звонка в 08:41 до момента, когда я до сервера добрался и понял, что перезапускать: два часа из них люди просто сидели без почты. Списали на «письмо с кривым вложением» — такое бывает, битое вложение с ложным заявленным размером действительно способно уронить mailboxd. Счёт: 3 часа.
Ноябрь 2025. У шести человек, у всех с айфонами, перестала синхронизироваться почта. В логе — другое:
2025-11-06 15:12:07,884 WARN [ImapServer-77] [] mailbox -
Failed to lock mailbox 341
com.zimbra.cs.mailbox.MailboxLock$LockFailedException: timeout
Тюнинговали таймауты блокировок и пул потоков IMAP, стало лучше. Счёт: 5 часов.
Март 2026. Поиск по почте не находит письма за последнюю неделю. В mailbox.log:
2026-03-19 11:48:33,102 WARN [Index-3] [] index -
Caught exception while indexing message id [489217] - indexing blocked. Possibly corrupt index?
Переиндексировали три ящика, поиск заработал. Счёт: 4 часа.
Двенадцать часов работ и три разных объяснения. Каждое по отдельности было технически верным. Все три вместе — неверными.
Ложная версия, которая стоила клиенту 60 тысяч
После второго выезда я построил версию, которая звучала убедительно: диск не тянет. Логика была такая — mailboxd и MariaDB конкурируют за один шпиндель, при пиковой нагрузке растёт IOwait, отсюда и блокировки, и подвисания. На форуме Zimbra такой сценарий описан не раз: показатель ожидания диска около десяти процентов, и тормозит буквально всё.
Померил. IOwait действительно держался на 8–11% в утренние часы. Версия подтвердилась, и мы перенесли виртуалку на NVMe-датастор. Аренда места на быстром хранилище обошлась клиенту примерно в 60 тысяч рублей за год плюс наши 4 часа на миграцию.
IOwait упал до 1,5%. Симптомы остались.
Это и есть ошибка, за которую мне было неловко: я нашёл подтверждение своей версии вместо того, чтобы искать её опровержение. Показатель был плохой, я его улучшил, а причина оказалась в другом слое. Быстрый диск, кстати, лишним не был — сервер стал приятнее. Но заявленную проблему он не решил.
Настоящая причина: память, которая не выросла вместе с сервером
В апреле я сел и выписал три инцидента в столбик. Нехватка кучи Java. Таймаут блокировки ящика при активных IMAP-клиентах. Прерванная индексация. И вдруг стало видно: это три разных проявления одного дефицита — mailboxd банально не хватает памяти под свои задачи.
Дальше я посмотрел, сколько ему выдано:
$ zmlocalconfig mailboxd_java_heap_size
mailboxd_java_heap_size = 4915
$ free -g
total used free shared buff/cache available
Mem: 47 19 2 0 25 27
4915 мегабайт кучи при 47 гигабайтах оперативной памяти на машине. Меньше десяти процентов.
Механика простая, и о неё спотыкаются многие. Инсталлятор при установке выделяет под кучу Java примерно 30% физической памяти на тот момент. Сервер ставили в 2021 году на виртуалку с 16 ГБ — отсюда и 4915 МБ. Потом компания росла, и памяти виртуалке добавили до 48 ГБ. Куча не изменилась ни на байт: она не пересчитывается сама, её поднимают руками. Три года сервер жил с настройкой трёхлетней давности и ломался в новом месте каждый раз, когда нагрузка подрастала.
Проверка гипотезы заняла двадцать минут — я посмотрел, как ведёт себя сборщик мусора:
$ tail -f ~zimbra/log/zmmailboxd.out
Ориентир, которым я пользуюсь: если сборка срабатывает раз в несколько секунд — это нормальная жизнь. Если каждую секунду, да ещё с паузами больше ста миллисекунд — куче тесно. У нас было именно второе, в утренний час особенно наглядно.
Лечение:
zmlocalconfig -e mailboxd_java_heap_size=8192
zmmailboxdctl restart
Восемь гигабайт. Для среды с большим числом IMAP-клиентов и крупными ящиками обычно советуют 6–8 ГБ, иногда до 10. С апреля выездов по этим симптомам не было.
Честно про ограничение: подъём кучи — не универсальное лекарство. Если память действительно течёт — из-за расширения, из-за индексации, из-за чего-то ещё — увеличенная куча просто отодвинет следующий сбой на месяц-другой. Отличить одно от другого можно только по поведению после изменения: если через три недели картина в логе сборщика вернулась к прежней, значит, дело не в размере, и надо снимать дамп кучи и разбираться, что её держит.
Проверьте у себя за две минуты
Начните с той самой пары цифр, которая три года пряталась у меня на виду:
su - zimbra
zmlocalconfig mailboxd_java_heap_size
free -m | awk '/Mem:/ {print "RAM всего, МБ:", $2}'
grep -c OutOfMemoryError ~zimbra/log/zmmailboxd.out
Трактовка:
| Что увидели | Что это значит | Когда действовать |
|---|---|---|
| Куча близка к 30% от RAM | Значение соответствует объёму памяти — вероятно, RAM после установки не меняли | Ничего не трогать |
| Куча заметно меньше 20% от RAM | Память серверу добавляли, а куча осталась от прежней конфигурации | В ближайшее окно |
| Куча больше трети RAM машины | Слишком большая куча даёт длинные паузы сборщика — веб-почта «залипает» на секунды, а остальным службам Zimbra памяти уже не остаётся | Пересмотреть значение |
OutOfMemoryError встречается хоть раз | Mailboxd уже падал по памяти, просто в тот раз поднялся сам | На этой неделе |
| Сборка мусора каждую секунду, паузы >100 мс | Куче тесно прямо сейчас | Сегодня |
Две минуты, две команды, и вы либо спокойны, либо знаете, куда смотреть дальше.
Регламент: девять команд раз в месяц
После апреля я собрал всё в один список. Проходится за 25–35 минут на сервере среднего размера, ничего не останавливает, ничего не меняет. Только смотрит.
# всё выполняется из-под пользователя zimbra: su - zimbra
# 1. версия и патч-уровень
zmcontrol -v
# 2. состояние служб
zmcontrol status
# 3. место
df -h /opt; du -sh /opt/zimbra/store /opt/zimbra/index /opt/zimbra/db /opt/zimbra/log
# 4. очередь
zmqstat; qshape deferred | head -20
# 5. память mailboxd
zmlocalconfig mailboxd_java_heap_size; grep -c OutOfMemoryError ~zimbra/log/zmmailboxd.out
# 6. целостность базы
/opt/zimbra/libexec/zmdbintegrityreport -v
# 7. хранилище и индекс
zgrep -B2 NO_SUCH_BLOB /opt/zimbra/log/mailbox.log*
grep -c 'Possibly corrupt index' /opt/zimbra/log/mailbox.log
# 8. сертификат
/opt/zimbra/bin/zmcertmgr viewdeployedcrt | grep -A2 Validity
# 9. антиспам и антивирус
zmlocalconfig | grep -E 'antispam_enable_(rule_updates|restarts|rule_compilation)'
ls -la /opt/zimbra/data/clamav/db/ | head
Что смотреть в выводе каждой команды
Что смотреть в каждом пункте:
| № | Тревожный признак | Что за ним стоит |
|---|---|---|
| 1 | Патч-уровень отстаёт больше чем на два выпуска | Открытые уязвимости, которые давно закрыты у всех остальных |
| 2 | Команда думает дольше минуты или врёт про службы | Внутри статусной обвязки зашит таймаут в 60 секунд; если сервер отвечает медленнее, вы получите неверную картину |
| 3 | Занятость /opt больше 85% | Дальше сервер начнёт отбивать входящие и остановится сам |
| 4 | В отложенных больше сотни писем к одному домену | Либо получатель нас не принимает, либо завис антивирусный слой |
| 5 | Куча меньше пятой части памяти машины | Ровно тот случай, из-за которого я трижды ездил в Химки |
| 6 | Отчёт нашёл повреждённые таблицы | Движок InnoDB штатной починке не поддаётся: только дамп, пересоздание и заливка обратно |
| 7 | Есть NO_SUCH_BLOB или упоминания повреждённого индекса | Метаданные и файлы писем разъехались, поиск начнёт врать |
| 8 | До конца срока меньше 30 дней | Просроченный сертификат роняет не только веб — по нему же ходит внутренняя связка со службой каталога |
| 9 | Все три флага в false, базы антивируса старше недели | Спам-правила не обновляются, устаревший антивирус способен положить весь поток почты в отложенные |
Пункт 6 стоит вынести в еженедельный крон, а не гонять руками — это штатная обёртка над проверкой таблиц, и она сама шлёт отчёт о найденных повреждениях. Пункт 9 удивляет почти всех: три ключа автообновления правил антиспама по умолчанию выключены, и на многих серверах они так и стоят годами. Правила лежат ровно те, что приехали с дистрибутивом.
Сколько стоит пропущенный чек-ап
Сравню два столбца по одному и тому же серверу.
| Как было | Как стало | |
|---|---|---|
| Проверки | Нет | 30 минут раз в месяц, 6 часов в год |
| Аварийные выезды | 3 за 9 месяцев | 0 с апреля — но честно: с фикса прошёл месяц, а не год |
| Часы по авариям | 12 ч, из них 7 в нерабочее время | — |
| Лишние решения | Перенос на быстрое хранилище, ~60 000 ₽/год | Пригодился, но проблему решал не он |
| Простой почты | Утро понедельника целиком плюс два частичных | — |
Утро понедельника на производстве — это отгрузки. Сорок шесть человек, из них двенадцать в отделе снабжения, которые с восьми утра согласовывают заявки поставщикам письмами. Три часа без почты в понедельник директор оценил в сдвиг двух отгрузок на день. Рубли посчитал я, и он не спорил: 46 человек по 3 часа при себестоимости часа 1 100 ₽ — это 151 800 ₽ впустую, и это только фонд оплаты, без штрафов за сдвиг отгрузки по договорам.
Мой счёт за три выезда — 12 часов, по ставке 3 500 ₽ в час это 42 000 ₽, из них 7 часов в нерабочее время. Регламент стоит 6 часов в год — 21 000 ₽. Даже если он поймает всего одну аварию из трёх, он окупается вдвое, а с учётом того, что за одну аварию простой стоил дороже годового регламента в семь раз, — считать тут вообще нечего.
И отдельно про то, чего регламент не делает. Он не чинит. Он показывает, что сломается, за недели до того, как это станет заявкой. Дальше всё равно нужен человек, который прочитает вывод и поймёт, что NO_SUCH_BLOB в трёх строках — это ерунда, а в трёхстах — повод останавливать сервер сегодня ночью. Разница между этими двумя случаями в цифрах не написана.
Как внедрить это у себя
Не надо строить систему мониторинга ради девяти команд. Начните проще.
- Заведите файл-журнал. Раз в месяц прогоняете список, вывод складываете в один текстовый файл с датой в имени. Ценность появляется на третьем месяце: вы начинаете видеть динамику, а не срез. Store прибавляет 8 ГБ в месяц — это норма. Прибавил 60 — идите смотреть, что случилось.
- Первые три пункта поставьте в крон с отправкой на почту администратора. Место, службы и версия — это то, что должно приходить само.
- Проверку целостности базы поставьте раз в неделю ночью. Она штатная и умеет отчитываться сама.
- Пункт про сертификат заведите с запасом в 30 дней. Этого хватает, чтобы спокойно перевыпустить, а не бегать в пятницу вечером.
Что здесь ломается при самостоятельном подходе — не команды, а трактовка. Команды простые, вывод их читается. Тяжело другое: отличить фоновый шум от начала аварии и не потратить, как я, четыре часа и шестьдесят тысяч на улучшение показателя, который не был причиной. Это приходит только с набором случаев на разных серверах, и никакая таблица тут полностью не заменяет опыт.
Хотите, чтобы я посмотрел на ваш срез — прогоните девять команд и пришлите вывод одним файлом. Отвечу за день: что у вас в норме, что требует внимания в ближайший месяц и что я бы делал сегодня ночью. Ничего устанавливать и никуда пускать для этого не нужно.
Частые вопросы
Почему куча Java не увеличивается сама, когда добавляют память серверу?
Потому что размер кучи фиксируется один раз — во время установки Zimbra, как процент от того объёма памяти, который был на машине в тот момент. Дальше это просто число в локальной конфигурации, и оно живёт своей жизнью. Виртуалке добавили памяти, ядро видит больше, а mailboxd продолжает работать со старым значением. У клиента в Химках было 4915 МБ кучи при 47 ГБ памяти — настройка трёхлетней давности на сервере, который за три года вырос вдвое. Проверяется одной командой, меняется двумя, а вылезает тремя разными авариями.
Можно ли просто поставить кучу побольше, с запасом?
Запас нужен, но не любой ценой. Слишком большая куча даёт длинные паузы сборщика мусора: раз в несколько минут веб-клиент замирает на секунду-две, и пользователи говорят «почта подвисает», что диагностировать сложнее, чем честное падение. Практический коридор для среды с активными IMAP-клиентами и крупными ящиками — 6–8 ГБ, иногда до 10. И ещё: если после подъёма кучи через три недели картина в логе сборщика вернулась к прежней — значит, память где-то течёт, и лечится это не размером, а поиском утечки по дампу кучи.
Три флага автообновления антиспама выключены. Это точно надо включать?
В большинстве случаев да, и обычно люди искренне удивляются, узнав, что они выключены по умолчанию. Речь про обновление правил, перезапуск службы после обновления и компиляцию правил, которая ускоряет проверку. Пока они в положении «выключено», ваш антиспам работает по правилам из дистрибутива — а дистрибутиву может быть пять лет. Включать надо все три и потом перезапустить антиспам-слой. Осторожность нужна на серверах с сильно кастомизированными правилами скоринга: после обновления веса могут поехать, поэтому первую неделю после включения стоит присматривать за карантином.
Что делать, если zmcontrol status показывает службу остановленной, а она работает?
Сначала перепроверить другим способом: посмотреть процессы и порты, а не только сводку. У статусной команды внутри зашит таймаут в шестьдесят секунд, и если сервер отвечает медленнее — а он отвечает медленнее, когда нагружен, — вы увидите картину, которой нет. Это известное поведение, обсуждается на официальном форуме. Второй частый источник вранья — осиротевшие PID-файлы после жёсткой перезагрузки: служба стартует, но обвязка ищет её по устаревшему идентификатору процесса. Так что расхождение статуса и реальности — само по себе диагностический признак.
У нас есть Zabbix. Зачем ещё какой-то ручной регламент?
Мониторинг ловит состояния, а регламент ловит тенденции. Zabbix прекрасно скажет вам, что диск заполнен на 90 процентов — то есть тогда, когда уже поздно. Он не скажет, что store прибавляет по 60 ГБ в месяц вместо обычных восьми, что индекс за квартал вырос вдвое, а куча Java три года не менялась при выросшем вдвое сервере. Для этого нужен человек, который раз в месяц кладёт срез рядом с предыдущим и смотрит на разницу. Идеально — когда есть оба: система шлёт алерты, человек раз в месяц смотрит динамику.
Оставить комментарий