У клиента-торговой компании в Балашихе почтовый сервер съел терабайт и начал отбивать входящие. Админ-консоль при этом честно показывала 430 ГБ писем. Разница почти в 500 ГБ пряталась в четырёх местах, и ни одно из них не видно из веб-интерфейса. Разбираю, как посчитать реальный объём и почему цифру для сметы переезда берут не из консоли.
Куда ушли 900 ГБ: считаем реальный объём Zimbra перед переездом
«Мы ничего не добавляли, а место кончилось»
Эта заявка приходит примерно так. Диск на почтовом сервере заполняется. Админ смотрит в админ-консоль Zimbra, складывает объёмы ящиков и получает цифру вдвое меньше того, что показывает df. Дальше начинается угадайка: чистят логи, сносят старые архивы, просят пользователей «удалить лишнее». Место возвращается на неделю и уходит снова.
Знакомо? Тогда сразу главное: сумма размеров ящиков в консоли — это не объём вашего почтового сервера. Это один из четырёх слоёв, и обычно даже не самый жирный. Три остальных из веб-интерфейса не видны вообще.
Отдельно обидно, когда этот же вопрос всплывает при подготовке к переезду. Подрядчик считает миграцию по цифре занятости диска, вы платите за перенос терабайта, а реально едет треть. Или наоборот: считаете по консоли, закладываете одно ночное окно, а упираетесь в четыре, потому что в store лежит вдвое больше, чем в учёте.
Ниже — сентябрьский случай 2023 года, разбор по слоям и порядок, по которому я теперь считаю объём перед любой миграцией. Ошибиться тут вдвое очень легко, и ошибаются почти все.
Сентябрь 2023, Балашиха: 42 менеджера, терабайт и 452-я ошибка
Торговая компания, склад и офис в одном здании на выезде из Балашихи, 42 рабочих места. Заказы от контрагентов приходят почтой — счета, спецификации, сканы доверенностей. Почта для них не удобство, а канал продаж.
Позвонили в среду утром: часть отправителей получает отбойники. В логе отправителя лежал текст, который я узнаю с полувзгляда:
452 4.3.1 Insufficient system storage
Это Postfix говорит, что ему некуда писать. Сервер жив, службы подняты, веб-почта открывается — а входящие часть контрагентов уже не может доставить. Первое, что я сделал, — посмотрел на разделы:
$ df -h /opt
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg0-opt 1007G 927G 80G 92% /opt
$ su - zimbra -c 'zmcontrol -v'
Release 8.8.15_GA_3869.RHEL7_64_20190917004220 RHEL7_64 FOSS edition, Patch 8.8.15_P34.
92 процента. При этом в админ-консоли на вкладке с ящиками суммарный объём был 430 ГБ. Директор по IT показал мне эту цифру и сказал: «У нас писем на четыреста гигабайт, откуда девятьсот с лишним?»
Честный ответ на тот момент: я не знал. Знал только, что искать надо не в ящиках.
Из чего на самом деле складывается объём
Zimbra хранит почту не одним куском. Слоёв четыре, и живут они в разных каталогах:
- store — сами письма. Каждое письмо лежит на диске отдельным blob-файлом. Это то, что примерно соответствует «объёму почты» в вашем понимании.
- index — полнотекстовый индекс, отдельный том. По нему работает поиск. Его размер зависит не от гигабайтов, а от количества писем и от того, сколько раз ящики переиндексировали.
- db — MariaDB с метаданными: кто, кому, когда, в какой папке, какие флаги, какие теги. Письма тут нет, а строк — десятки миллионов.
- всё остальное — логи, очередь Postfix, карантин антивируса, каталоги обновлений, чьи-то ручные архивы.
Три первые цифры снимаются одной командой:
$ du -sh /opt/zimbra/store /opt/zimbra/index /opt/zimbra/db
612G /opt/zimbra/store
84G /opt/zimbra/index
41G /opt/zimbra/db
Итого 737 ГБ на трёх слоях против 430 ГБ в консоли. Уже видно, что консоль — это про store, и то не про весь. Но df показывал 927 ГБ, то есть до баланса не хватало ещё 190 ГБ.
Сто двадцать девять из них нашлись сразу, в четвёртой категории: 67 ГБ ротированных логов и mailbox.log за три года, 37 ГБ старых установочных пакетов в /opt/zimbra/packages, 25 ГБ чьих-то ручных tar-архивов, положенных в /opt/zimbra/backup в 2020 году. Оставшийся 61 ГБ я в тот вечер не нашёл вообще — просто записал в блокнот «не сходится на 61» и пошёл дальше. Всплыли они через неделю, и про них будет отдельный раздел.
Лёгкую часть я вычистил за сорок минут: снёс пакеты, архивы и всё, что старше двух месяцев, из логов. Освободилось 117 ГБ, занятость упала с 92% до 80%, отбойники прекратились. И на этом я чуть не закрыл заявку.
Проверьте у себя за две минуты
Пять команд под пользователем zimbra, ничего не останавливая:
df -h /opt
du -sh /opt/zimbra/store /opt/zimbra/index /opt/zimbra/db /opt/zimbra/log
zmvolume -l
du -sh /opt/zimbra/store/* | sort -h | tail
zmprov -l gaa | while read u; do echo -n "$u "; zmmailbox -z -m "$u" gms; done | sort -k2 -h | tail -10
Таблица трактовки — по тому, какие цифры разошлись:
| Что увидели | Что это значит | Куда копать |
|---|---|---|
store заметно больше суммы ящиков из консоли | В store лежат письма, которых нет в учёте: dumpster, удалённые ящики, осиротевшие blob | Корзина уровня сервера и заброшенные учётки |
index больше 15% от store | Индекс раздут, обычно после серии переиндексаций или сбоев | Проверить mailbox.log на записи о повреждённом индексе |
db больше 10% от store | Метаданных непропорционально много — типично при мусоре в mboxgroup-таблицах | Сравнить число строк с числом писем |
zmvolume -l показывает больше двух томов | Есть тома, о которых никто не помнит; один из них может быть на том же диске | Посмотреть пути каждого тома |
/opt/zimbra/log больше 20 ГБ | Ротация не настроена или настроена на «вечно» | Чистка, потом logrotate |
| Один ящик больше 15% всего store | Ящик-гигант. Он определит график любого переезда | Отдельное окно работ под него |
Если df показывает больше 90% — не откладывайте. Zimbra при нехватке места останавливается сама, и остановится она не в удобный момент, а под пятничную выгрузку.
Моя ложная версия: удалили 118 ГБ, освободилось ноль
После лёгкой чистки я пошёл по ящикам и нашёл главного подозреваемого. Общий ящик zakaz@, куда шли все заявки с сайта и все ответы менеджеров, весил 118 ГБ. Триста двенадцать тысяч писем с 2016 года.
Версия была логичная: вот он, ваш терабайт. Договорились с директором, что переписку старше трёх лет выгружаем в архив и удаляем из ящика. Выгрузили штатно, в tgz, положили на NAS:
zmmailbox -z -m zakaz@example.ru -t 0 getRestURL \
"//?fmt=tgz&query=before:1/1/2021" > /mnt/arch/zakaz_do2021.tgz
Потом почистили ящик до 2021 года. Консоль показала: ящик стал 44 ГБ вместо 118. Я посмотрел на df. Занятость не изменилась ни на гигабайт.
Полез разбираться и вспомнил про dumpster — корзину уровня сервера. Когда пользователь удаляет письмо из «Корзины», письмо не исчезает: оно уходит в dumpster ящика и лежит там до истечения срока хранения, заданного в классе обслуживания. У этого клиента срок стоял 30 дней. То есть 74 ГБ, которые я «освободил», продолжали физически занимать store и должны были освободиться только через месяц.
Штатная чистка dumpster делается через zmmailbox, но тут есть подвох, на который я наступил в тот же вечер. Команда от имени администратора против чужого ящика отваливается с ошибкой прав, если не указать флаг -A:
# так — PERM_DENIED
zmmailbox -z -m zakaz@example.ru emptyDumpster
# так — работает
zmmailbox -z -A -m zakaz@example.ru emptyDumpster
Полчаса я убил, разглядывая права на каталог, прежде чем сообразил, что дело не в файловой системе, а в одном флаге. Обидная потеря времени, зато с тех пор помню.
Чистку dumpster я прогнал по всем ящикам, а не только по zakaz@: раз механизм есть, он копит у всех. df отдал 170 ГБ — из них 74 были вчерашним архивом заказов, а остальные 96 копились у остальных сорока с лишним ящиков годами.
И вот тут стало интересно, потому что арифметика опять не сошлась. df освободил 170 ГБ, а каталог /opt/zimbra/store по du похудел только на 143. Двадцать семь гигабайт удалённых писем лежали физически не там, где я думал. Как раз в ту сторону, где у меня в блокноте был записан несошедшийся 61 ГБ.
Второй том, о котором забыли, и мусор в mboxgroup
Следующая команда — список томов. Их оказалось три:
$ zmvolume -l
Volume id: 1
name: message1
type: primaryMessage
path: /opt/zimbra/store
compressed: false
current: true
Volume id: 2
name: index1
type: index
path: /opt/zimbra/index
Volume id: 3
name: message2
type: secondaryMessage
path: /opt/zimbra/store2
compressed: false
current: false
Третий том, store2, кто-то создал в 2021 году под политику переноса старых писем на медленное хранилище. Потом от идеи отказались, том попытались удалить из админ-консоли и получили отказ: том использовался. Так он и остался — не текущий, не используемый для новых писем, но с 61 ГБ старых blob внутри. Вот и весь мой несошедшийся остаток. И, что важно, лежал он на том же логическом томе /opt, то есть никуда данные не «переносил», а просто занимал место рядом.
Заодно объяснилось и расхождение с dumpster. Часть удалённых писем — те самые 27 ГБ — физически лежала на втором томе, поэтому df отдал больше, чем похудел каталог store. После чистки в store2 оставалось 34 ГБ живых блобов. Я перенёс их штатным способом на основной том и удалил том из конфигурации. Места это не освободило ни гигабайта — данные как лежали на /opt, так и остались, просто в другом каталоге. Смысл был в другом: сервер перестал держать письма там, куда не смотрел ни бэкап, ни любой подсчёт объёма.
Последний слой — база. У Zimbra метаданные разложены по группам таблиц mboxgroup, и после массовых удалений там остаётся неиспользуемое место внутри самих таблиц: строки удалены, а файлы данных не сжались. На форуме Zimbra это разбирают под названием «странное расхождение занятого места и объёма сообщений», и лечится оно скриптом optimizeMboxgroups.pl из поставки.
Тут я хочу быть честным про сложность. Скрипт работает, но во время работы он блокирует таблицы и заметно грузит дисковую подсистему. На живом сервере в рабочее время это означает подвисший веб-клиент и растущую очередь LMTP. Мы запускали его в ночь с субботы на воскресенье, предварительно сняв дамп базы, и он шёл 3 часа 40 минут на 41 ГБ базы. Вернул 12 ГБ. Не рекорд, но и не ноль.
И вещь, которую я тогда делать не стал по осторожности, а сегодня не делаю уже осознанно. У штатного инструмента проверки целостности хранилища есть флаг, удаляющий из базы записи о письмах, для которых не нашлось файла. Соблазн большой. За прошедшие годы всплыл случай, где этот инструмент неверно разбирал адреса блобов на вторичном томе одного из типов внешних хранилищ и репортил отсутствующими все подряд — с таким флагом он удалил бы из базы живые письма. Проверять целостность нужно. Удалять по её результатам автоматически — нет, только вручную и с ограничением по конкретному тому.
Что вернули и во что это встало
Итоговая арифметика по этому серверу за две недели работ:
| Источник | Освободилось | Как |
|---|---|---|
| Логи, старые пакеты, ручные архивы | 117 ГБ | Чистка, 40 минут, без простоя |
Dumpster по всем ящикам, включая вчерашний архив zakaz@ на 74 ГБ | 170 ГБ | Выгрузка в tgz на NAS 5 часов + emptyDumpster с флагом -A, 1,5 часа |
| Забытый вторичный том store2 | 0 ГБ | Перенос содержимого на основной том и удаление тома, 3 часа. Места не дал вообще — но объяснил расхождение и убрал каталог из-под учёта |
| Оптимизация mboxgroup-таблиц | 12 ГБ | Ночное окно, 3 ч 40 мин, с дампом базы заранее |
| Итого | 299 ГБ | Занятость 92% → 62% |
Работ вышло на 21 час, из них 6 — ночью. Теперь цена бездействия, потому что её тоже надо назвать.
Пока сервер отбивал входящие 452-й ошибкой, компания не получила часть заявок. Отбойник получает отправитель, а не получатель, — то есть у себя вы этого не видите вообще. По логам за те три часа мы насчитали 47 отклонённых входящих от 19 контрагентов. Часть из них дошла позже, когда место освободилось. Часть — нет: два контрагента к тому моменту уже позвонили конкурентам. Директор оценил потерю в один средний заказ, это около 245 тысяч рублей маржи. Плюс мои 21 час: по нашей ставке 3 500 ₽ в час — 73 500 ₽, и половина этих часов была аварийной, то есть в неудобное время и в чужом графике.
Для сравнения: та же чистка, сделанная планово, заняла бы те же 21 час и те же 73 500 ₽, но без потерянного заказа на 180 тысяч и без ночной аварии в среду. Разрыв — 180 тысяч рублей на ровном месте, и это только то, что директор согласился посчитать. Разница — целиком в том, смотрит кто-нибудь на df раз в месяц или нет.
Как брать цифру для сметы переезда
И главное, ради чего эта арифметика вообще нужна. Когда вы считаете переезд на другую платформу, вам нужна не занятость диска и не сумма из консоли. Вам нужен объём того, что реально поедет.
Правило, которым пользуюсь я:
- Берём
store, а неdf. Индекс, база и логи не переносятся — на новой площадке индекс строится заново, а метаданные приезжают вместе с письмами по протоколу. - Вычитаем dumpster. Его надо вычистить до переезда, иначе вы будете гонять по сети удалённые письма, а на приёмнике они всё равно не появятся.
- Вычитаем ящики уволенных, которые никто не открывал два года. Их правильнее выгрузить в архив, а не тащить в новую систему.
- Отдельной строкой выписываем ящики-гиганты. У этого клиента после чистки таких осталось два, по 44 и 51 ГБ, и они поехали в свои окна.
Арифметика по Балашихе получилась такая. Каталог store после всех работ — 469 ГБ, плюс 34 ГБ, приехавшие с удалённого store2, итого 503 ГБ реальных писем. Из них 56 ГБ — восемь ящиков уволенных, которые выгрузили в архив и в новую систему не потащили. Остаётся 447 ГБ, и вот это и есть цифра для сметы; два гиганта на 44 и 51 ГБ сидят внутри неё и едут отдельными окнами.
В Балашихе разница между «переезжаем 900 ГБ» и «переезжаем 447 ГБ» — это разница между четырьмя ночными окнами и двумя. По деньгам ровно вдвое. Именно поэтому я никогда не считаю миграцию по цифре из df: она завышена почти всегда, и завышена по-разному у разных клиентов.
Если хотите быструю оценку по своему серверу — пришлите мне вывод трёх вещей: df -h /opt, du -sh по store, index, db и log, и zmvolume -l. Этого достаточно, чтобы за день сказать, сколько у вас реальных писем, сколько мусора и в какой из четырёх слоёв ушло место. Ответ будет с цифрами, а не «надо смотреть».
Частые вопросы
Почему админ-консоль показывает меньше, чем занято на диске?
Консоль считает объём почты в ящиках — то, что видит пользователь. За её пределами остаются как минимум четыре вещи: письма в dumpster, то есть в корзине уровня сервера после окончательного удаления; полнотекстовый индекс, который живёт отдельным томом; база метаданных MariaDB; и всё техническое — логи, очередь, карантин, старые пакеты. У торговой компании в Балашихе разрыв составил 497 ГБ при 430 ГБ учтённой почты. Это не аномалия, а обычная картина сервера, который работает шестой год.
Что такое dumpster и почему письма не удаляются сразу?
Это корзина уровня сервера: когда пользователь очищает свою «Корзину», письма уходят туда и лежат ещё какое-то время — срок задан в классе обслуживания, часто это 30 дней. Механизм полезный, он не раз спасал людей, удаливших письмо по ошибке. Но при разборе места он путает карты: вы чистите ящик, консоль показывает уменьшение, а диск не освобождается. Принудительно очищается командой zmmailbox с подкомандой emptyDumpster, и обязательно с флагом -A — без него команда против чужого ящика падает с отказом в правах.
Можно ли просто добавить второй диск и не разбираться?
Можно, и иногда это правильный ход — например, когда до планового переезда осталось два месяца. Монтируете новый раздел, заводите его как дополнительный том хранения сообщений и делаете текущим. Но два предупреждения. Первое: том, который стал использоваться, потом не удаляется просто так — сервер ответит, что том занят, и вам придётся сначала переносить с него данные. Второе: добавленное место скрывает симптом, а не причину. У клиента из этой статьи забытый вторичный том лежал на том же логическом разделе и просто занимал 61 ГБ рядом, ничего не разгружая.
Сколько места нормально занимает индекс?
Ориентир, которым я пользуюсь: индекс редко бывает больше десяти-пятнадцати процентов от store. Если у вас 612 ГБ писем и 84 ГБ индекса — это верхняя граница нормы, как было в Балашихе. Если индекс подбирается к трети от store, почти всегда обнаруживается история с переиндексациями: ящик реиндексировали, старые файлы индекса при этом остались, повторили несколько раз. В логе mailbox.log в таких случаях находятся предупреждения о возможно повреждённом индексе. Это лечится точечно, по конкретным ящикам, а не оптовой переиндексацией всего сервера.
Насколько опасно запускать оптимизацию таблиц на рабочем сервере?
Опасность не в потере данных, а в простое. Скрипт оптимизации mboxgroup-таблиц блокирует таблицы на время работы и создаёт серьёзную нагрузку на диск: пока он идёт, веб-почта подтормаживает, а входящие копятся в очереди. На базе 41 ГБ у нас это заняло 3 часа 40 минут. Поэтому порядок такой: сначала снимаем дамп базы, потом выбираем ночное окно, потом предупреждаем людей, и только потом запускаем. Гонять его днём «просто посмотреть, сколько вернёт» — плохая идея, вернуться назад в середине процесса нельзя.
Оставить комментарий