Куда ушли 900 ГБ: считаем реальный объём Zimbra перед переездом

У клиента-торговой компании в Балашихе почтовый сервер съел терабайт и начал отбивать входящие. Админ-консоль при этом честно показывала 430 ГБ писем. Разница почти в 500 ГБ пряталась в четырёх местах, и ни одно из них не видно из веб-интерфейса. Разбираю, как посчитать реальный объём и почему цифру для сметы переезда берут не из консоли.

«Мы ничего не добавляли, а место кончилось»

Эта заявка приходит примерно так. Диск на почтовом сервере заполняется. Админ смотрит в админ-консоль 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 часа
Забытый вторичный том store20 ГБПеренос содержимого на основной том и удаление тома, 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 минут. Поэтому порядок такой: сначала снимаем дамп базы, потом выбираем ночное окно, потом предупреждаем людей, и только потом запускаем. Гонять его днём «просто посмотреть, сколько вернёт» — плохая идея, вернуться назад в середине процесса нельзя.

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

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

📞 Связаться с нами
#Zimbra#хранилище#диагностика#тома#переезд
Комментарии 0

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

загрузка...

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

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

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

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