Моніторинг Postfix через Prometheus і Grafana: метрики черг, доставки та відмов
Більшість адмінів дізнаються про роздуту чергу і повернення 421-4.7.0 від Gmail тоді, коли клієнти вже скаржаться. Розбираємо, як за годину зібрати з Postfix телеметрію рівня хвилина-в-хвилину через postfix_exporter, побудувати дашборд у Grafana і налаштувати тривоги, що спрацьовують ДО інциденту.
EvilMail Team17 липня 2026 р.14 хв читання
Класичний інцидент з поштою розгортається так: о 09:40 маркетинг запустив розсилку, об 11:15 у підтримку прилітає перший тикет «лист не дійшов», об 11:50 ви логінитесь на сервер, робите mailq | tail і бачите 8 тисяч повідомлень у deferred. За цю годину з чвертю Gmail уже встиг занести ваш IP у сіру зону і сипле 421-4.7.0 Try again later. Проблема була видима на графіку ще о 09:52 — але графіка не було.
mailq показує зріз на одну секунду. pflogsumm дає вчорашній підсумок простирадлом тексту. Жодне з двох не показує тренд — а саме тренд глибини deferred і темпу відмов передбачає інцидент за 20–40 хвилин до того, як він стане болючим. Далі — як зняти цю телеметрію з Postfix без жодного рядка змін у його конфізі, вивести на дашборд Grafana і повісити тривоги, що будять вас раніше за клієнтів.
Що саме треба бачити (і чому SMTP-логи цього не дають)
CPU і RAM на поштовому сервері майже завжди нецікаві: Postfix надзвичайно економний, і машина під навантаженням розсилки рідко впирається в залізо. Ламається не залізо, а потік доставки
Моніторинг Postfix у Prometheus і Grafana через postfix_exporter — EvilMail Blog
. Ось чотири сигнали, які реально корелюють з інцидентом:
Глибина deferred-черги. Deferred — це листи, що отримали тимчасову відмову (4xx) і чекають повтору. Здорова черга дихає: піднялась на розсилці, за 10–15 хвилин розсмокталась. Deferred, що монотонно росте годину, — це або rate-limit від великого провайдера, або проблема з вашою репутацією.
Швидкість bounce (повідомлень/хв). Різкий спайк 5xx-відмов означає або биту базу адрес, або що вас почали відхиляти за політикою (5.7.1). Це прямий удар по reputation score.
Частка 4xx-tempfail. Той самий 421 від Gmail і від дрібного корпоративного сервера означає різні речі. Розбивка за статусом доставки дає ранню діагностику того, що потік почав в'язнути саме на повторах.
Затримка доставки (delivery latency). Postfix розкладає час доставки на стадії: до черги (прийом → queue manager) і після (з'єднання + власне передача на relay). Зростання latency на стадії передачі — це повільні relay або throttling на боці одержувача.
Жоден із цих чотирьох сигналів не видно з разового погляду в лог. Усі чотири елементарно знімаються метриками.
Архітектура збору
postfix_exporter (канонічний — kumina/postfix_exporter) не є плагіном Postfix і нічого в ньому не змінює. Він читає рівно два джерела:
Сокет `showq` (/var/spool/postfix/public/showq) — той самий інтерфейс, що використовує mailq. Дає точну поточну глибину кожної черги.
`mail.log` (regexp-парсинг) — дає темпи: скільки прийнято, доставлено, відкладено, відбито, і гістограми latency.
Обидва потрібні. Showq відповідає на питання «скільки зараз висить», лог — «як швидко рухається і з яким результатом». Exporter слухає на :9154 і віддає /metrics; Prometheus раз на 15 секунд робить pull. Pull-модель тут доречніша за push: Prometheus сам знає, живий exporter чи ні (up == 0 — це вже сигнал), і не треба тримати проміжний шлюз.
Розгортання postfix_exporter
Найпростіше — забрати статичний бінарник з релізів kumina/postfix_exporter і покласти під systemd. Ключове тут не команди, а права доступу, на яких спотикаються всі.
bash
# Бінарник
sudo install -m 0755 postfix_exporter /usr/local/bin/postfix_exporter
# Користувач без shell, у двох групах:
# postfix — щоб дотягтися до сокета showq
# adm — щоб читати /var/log/mail.log (Debian/Ubuntu)
sudo useradd --system --no-create-home --shell /usr/sbin/nologin postfix_exporter
sudo usermod -aG postfix,adm postfix_exporter
Сокет showq лежить у каталозі /var/spool/postfix/public/ з обмеженими правами на групу — тому exporter має бути членом групи, що володіє цим каталогом. На більшості дистрибутивів це postfix, але спершу перевірте: ls -ld /var/spool/postfix/public (подекуди це postdrop). Помилитесь із групою — замість глибини черги отримаєте permission denied і мовчазні нулі. Systemd-юніт:
Пастка з journald. Якщо у вас systemd пише пошту не у файл, а в журнал (типово на свіжих інсталяціях без rsyslog), --postfix.logfile_path не спрацює — файл або порожній, або його нема. Тоді знімайте лог із журналу:
Юзеру exporter доведеться додати доступ до журналу (група systemd-journal). У Docker-варіанті монтуйте /var/spool/postfix і /var/log read-only — і не забудьте, що всередині контейнера GID групи, яка володіє сокетом, має збігатися з хостовим, інакше showq знову недоступний.
Анатомія черги та де вимірюється latency
Щоб правильно читати метрики, тримайте в голові, як улаштована черга Postfix. Лист проходить incoming → active → deferred, і саме active обмежена зверху (qmgr_message_active_limit, типово 20000). Коли одержувачі віддають tempfail, повідомлення падає в deferred і повертається в active за таймером повтору. Deferred — це і є «зона тривоги»: поки вона в межах норми, все дихає, а її монотонний ріст = вхідний сигнал інциденту.
Гістограма postfix_smtp_delivery_delay_seconds розбита міткою stage на чотири складові: before_queue_manager, queue_manager, connection_setup, transmission. Розрив між прийомом і передачею діагностує, де саме сповільнення: у нашому qmgr чи вже на боці одержувача.
Ключові метрики та їхнє читання
Серії, на які варто дивитись у першу чергу:
`postfix_showq_message_size_bytes` — histogram з міткою queue (incoming, active, deferred, hold). Її _count{queue="deferred"} — точна кількість листів у deferred *прямо зараз*. Поряд є postfix_showq_message_age_seconds — та сама розбивка, але за віком повідомлень.
`postfix_smtp_delivery_delay_seconds` — histogram затримки доставки з міткою stage; з неї беруться квантилі p50/p95/p99.
`postfix_smtp_messages_processed_total` — лічильник результатів доставки з міткою status (sent, deferred, bounced). Це головна серія для bounce- і deferred-rate.
`postfix_smtpd_messages_processed_total` — скільки повідомлень прийняв smtpd на вході.
`postfix_qmgr_messages_inserted_size_bytes` (histogram) і `postfix_qmgr_messages_removed_total` — вставки в чергу і зняття з неї; _count першої = темп надходження в qmgr.
Ключова ідея: усі log-based серії — монотонні лічильники, тож дивитись на їхнє абсолютне значення марно, завжди беремо rate(...). І звертайте увагу на розрив між smtpd (скільки зайшло) і smtp (скільки вийшло): якщо вхід є, а вихід стоїть — queue manager застряг.
PromQL для дашборда
promql
# Глибина deferred-черги прямо зараз (повідомлень)
postfix_showq_message_size_bytes_count{queue="deferred"}
# Bounce rate, повідомлень за хвилину
rate(postfix_smtp_messages_processed_total{status="bounced"}[5m]) * 60
# Частка bounce від усіх спроб доставки
sum(rate(postfix_smtp_messages_processed_total{status="bounced"}[5m]))
/ sum(rate(postfix_smtp_messages_processed_total[5m]))
# p95 затримки доставки (по всіх стадіях)
histogram_quantile(0.95,
sum by (le) (rate(postfix_smtp_delivery_delay_seconds_bucket[5m])))
Вікно [5m] — компроміс: достатньо згладжує сплески, але лишається чутливим, щоб спайк bounce був видно за пару хвилин. Для повільних трендів репутації на окремій панелі беріть [1h].
Дашборд у Grafana: що варте екрана
Шість панелей закривають 95% операційних питань:
Глибина черг — time series з queue у legend (deferred виділити кольором) плюс stat-панель з поточним deferred і порогами (green < 200, yellow < 500, red ≥ 500).
Delivery latency p50/p95/p99 — три лінії на одному time series.
Результати доставки — stacked-графік sent/deferred/bounced у msg/хв із postfix_smtp_messages_processed_total.
Heatmap latency — по buckets delivery_delay, миттєво показує «хвіст» повільних доставок.
Вхід vs вихід — rate(postfix_smtpd_messages_processed_total) проти rate(postfix_smtp_messages_processed_total); розбіжність = qmgr буксує.
`up{job="postfix"}` — маленький stat, живий взагалі exporter чи ні.
Додайте змінну дашборда $instance, щоб перемикатись між серверами, і виведіть її у legend. Пороги на stat-панелях — не косметика: саме червоний квадрат у кутку монітора змушує подивитися, перш ніж прилетить тикет.
Робочі стартові пороги: deferred > 500 протягом 10 хвилин; bounce-rate > 5% від спроб доставки; вставки в чергу тривають, а доставок нема = мертвий qmgr; p95 latency > 30s. Директива for: тут критична — без неї тривоги флапатимуть на кожному нормальному сплеску розсилки. Маршрутизуйте через Alertmanager: warning — у Telegram-канал, critical — у PagerDuty з ескалацією.
Сліпі зони, про які треба знати чесно
postfix_exporter парсить лог регекспами. Це його сила (нуль втручання в Postfix) і його пастка одночасно:
Ротація лога. Якщо logrotate робить create+move, exporter може втратити хвіст рядків між ротаціями. Використовуйте copytruncate або переходьте на journald-режим, де цієї проблеми нема.
Нестандартні рядки. Кастомні smtpd-обмеження чи сторонні milter пишуть рядки, які регекспи не покривають — метрика їх просто не побачить. Це сліпа зона за визначенням.
Немає розбивки за доменом призначення. З коробки exporter не тримає next-hop як мітку — і в цьому його плюс: cardinality обмежена. Захочете «топ доменів» — доведеться болтити окремий log-конвеєр (grok_exporter, mtail, Loki), і саме там cardinality вибухає: тисячі доменів = мільйони серій. Обмежуйте topk і не кладіть домен міткою на кожну серію.
Розсинхрон часу. Latency-гістограми брешуть, якщо NTP не синхронізований. Перевіряйте timedatectl на всіх нодах.
Чек-лист впровадження
[ ] Юзер exporter у групі, що володіє /var/spool/postfix/public (перевірено через ls -ld), і в adm для лога.
[ ] Ротація лога не рве парсинг (copytruncate або journald).
[ ] Alert-rules завантажені (/rules у Prometheus UI показує їх).
[ ] Alertmanager route розводить warning/critical по каналах.
[ ] NTP синхронізований на всіх нодах.
[ ] Retention піднятий до 90d, якщо треба тренд репутації.
Різниця між «дізнався про роздуту чергу з дашборда о 09:52» і «дізнався з тикета об 11:15» — це година, за яку ваш IP не встиг просісти по репутації в Gmail. Година телеметрії проти доби відкопування репутації — обмін, який окупається з першого ж інциденту.