Мониторинг доставляемости в Grafana: blackbox-пробы SMTP и seed-письма с алертами на спам
TCP-чек порта 25 говорит, что «SMTP жив», и молчит, когда ваши письма тихо улетают в спам. Разбираем, как построить синтетический мониторинг доставляемости на blackbox exporter и самописной seed-пробе: реальное письмо через submission, подтверждение через IMAP, метрики в Prometheus, дашборд в Grafana и алерты в Alertmanager до жалоб клиентов.
EvilMail Team28 июля 2026 г.13 мин чтения
Про проблемы с доставляемостью команды узнают в двух местах, и оба — плохие. Первое: тикет в поддержке «мне не пришло письмо для сброса пароля». Второе: недельный отчёт Google Postmaster Tools, где репутация домена уже покраснела, а вы задним числом гадаете, какой релиз три дня назад её уронил. И то и другое — реакция постфактум. К моменту, когда сигнал доходит до человека, деградация идёт уже часами.
Между тем доставляемость — это ровно такая же метрика, как latency API или процент 5xx. Её можно мерить синтетически, в реальном времени, и стрелять алертом до того, как её заметит первый клиент. Ниже — как мы это делаем на инфраструктуре evilmail.pro: submission на 587/465, seed-ящики в Gmail, Outlook, Yandex и Mail.ru, blackbox exporter как дешёвая первая линия и самописная seed-проба как единственный правдивый сигнал.
Что именно мы измеряем и почему TCP-чек порта 25 вас обманывает
Сигнал о состоянии почтовой отправки распадается на три уровня, и путать их — главная ошибка мониторинга.
L1. Транспорт. Доступен ли порт, отвечает ли SMTP-баннер, валиден ли TLS-сертификат. Это про 25/465/587 и про «жив ли Postfix». Дёшево, но почти ничего не говорит о доставке.
L2. Приём своим MTA. Ваш submission принял письмо, подписал DKIM, поставил в очередь. Тоже зелёно — и тоже слепо к тому, что произошло дальше.
Мониторинг доставляемости в Grafana: blackbox SMTP + seed-пробы | evilmail.pro — EvilMail Blog
L3. End-to-end доставка. Письмо реально дошло до чужого ящика, легло в INBOX, а не в Junk, и Authentication-Results показывает dkim=pass, spf=pass, dmarc=pass.
Классический мониторинг живёт на L1–L2 и там же слепнет. Порт 25 отвечает 220 даже тогда, когда ваш IP уже в блоклисте Spamhaus, а Gmail молча складывает вашу почту в спам. Баннер не знает про репутацию. Приём своим Postfix не знает, что творится в чужой антиспам-логике. Единственный способ увидеть L3 — отправить настоящее письмо на настоящий ящик у провайдера и проверить, куда оно легло.
Отсюда — определение метрик, которые нам нужны: не «uptime порта», а процент доставленных, end-to-end latency до INBOX и доля попаданий в спам — по каждому провайдеру отдельно, потому что репутацию Gmail, Outlook и Yandex считают независимо.
Blackbox exporter: дешёвая первая линия на баннер и TLS
Blackbox exporter не решает задачу доставляемости, но бесплатно ловит грубые отказы транспорта: упавший Postfix, забитый listener, протухший сертификат, зарезанный аплинком исходящий 25. Держать его стоит уже за то, что он дёшев и мгновенно отличает «сервер лёг» от «письма тихо в спаме».
Модуль в blackbox.yml, который проверяет SMTP-диалог до STARTTLS:
Что реально забираем: probe_success (0/1 по всему диалогу) и probe_ssl_earliest_cert_expiry — unix-timestamp ближайшего к истечению сертификата. Дни до протухания считаются прямо в PromQL: (probe_ssl_earliest_cert_expiry - time()) / 86400. Это один из самых недооценённых алертов: половина инцидентов «внезапно всё сломалось на submission» — это молча истёкший TLS-сертификат.
Граница метода записана прямо в его определении: баннер 220 отвечает и тогда, когда вас уже занесли в блоклист. Blackbox — это L1. Дальше начинается настоящая работа.
Seed-проба: SMTP-инъекция плюс IMAP-подтверждение
Проба — небольшой демон (мы пишем на Go, Python тоже отлично подходит), который раз в 2–5 минут прогоняет полный цикл по реальной цепочке.
Цикл одной пробы:
1.Submit. Отправляем письмо через submission (587/STARTTLS или 465/implicit TLS — не через 25, 25-й порт только для MX-to-MX). В теме — метка времени, в заголовках — уникальный Message-ID: <[email protected]> и дублирующий X-Probe-Id с тем же UUID.
2.Poll. Каждые несколько секунд ходим по IMAP в seed-ящик получателя и ищем письмо: сначала в INBOX, потом в спам-папке.
3.Match и замер. Нашли по Message-ID — фиксируем latency от момента submit, определяем папку (папка, где нашлось, = placement), парсим Authentication-Results.
Ключевой момент — определение папки. У каждого провайдера спам-папка называется по-своему: Gmail — [Gmail]/Spam, Outlook — Junk Email, Yandex и Mail.ru — локализованный Спам. Хардкодить имена хрупко: надёжнее находить папку по IMAP-атрибуту SPECIAL-USE \Junk (RFC 6154), а строковое имя из примера ниже держать как fallback. Поиск идёт последовательно — сначала INBOX, потом Junk:
python
def find_placement(imap, message_id):
for folder, is_spam in [("INBOX", False), ("[Gmail]/Spam", True)]:
imap.select(folder, readonly=True)
typ, data = imap.search(None, 'HEADER', 'Message-ID', message_id)
if data[0].split():
return folder, is_spam
return None, None # ещё не дошло
Аутентификацию тянем из заголовка Authentication-Results регэкспом по dkim=(\w+), spf=(\w+), dmarc=(\w+). Если dkim=fail при живом submission — почти всегда сломанный селектор или неудачная ротация ключа, и это отдельный критический алерт.
Метрики проба отдаёт на своём /metrics (порт 9110), Prometheus скрейпит её как обычный target. Для ручного кросс-чека держим под рукой GlockApps и Mail-Tester, но это спот-проверки руками; постоянный сигнал даёт только своя проба. Google Postmaster Tools показывает репутацию домена и IP, но лагает на сутки — для алертов бесполезен, для трендов полезен.
Метрики Prometheus, которые стоит собирать
Набор компактный и осмысленный. Лейбл provider — да; лейбл per-message — никогда, иначе взорвёте кардинальность.
mail_probe_delivery_seconds — Histogram с бакетами [5, 15, 30, 60, 120, 300]. Основа для p95.
mail_probe_success{provider} — Gauge 0/1, доставлено ли вообще за окно.
Компоновка, которую не стыдно повесить на стену в дежурке:
Stat-ряд сверху: success rate и p95 latency по каждому провайдеру. Красный/зелёный по порогу SLA.
Heatmap распределения задержки — сразу видно, тянется ли хвост, ещё до того как p95 пробьёт порог.
Timeseries доли landed-in-spam по провайдерам через avg_over_time. Это график, за которым смотрят внимательнее всего.
Таблица последнего статуса per-provider: папка, dkim/spf/dmarc, latency последней пробы.
Single-stat дней до истечения TLS:(probe_ssl_earliest_cert_expiry - time()) / 86400.
Обязательно вешайте на графики аннотации деплоев и смен исходящего IP. Большинство просадок доставляемости коррелируют с конкретным изменением, и аннотация превращает получасовое расследование в один взгляд на таймлайн.
Алерты: на что будить дежурного, а на что нет
Разделение page vs warning здесь принципиально. Правила Alertmanager:
Три вещи отличают рабочую конфигурацию от игрушечной.
SpamPlacement — это page, а не warning. Попадание в спам не роняет ни один график доступности; всё «зелёное», клиенты просто перестают получать письма. Это самая дорогая и самая тихая деградация, и будить по ней надо всерьёз.
Ингибиция. Когда DeliveryFailing уже сработал, не нужно параллельно звенеть DeliveryLatencyHigh — задержка бесконечна, потому что письма нет вовсе. Настройте inhibit_rules, чтобы page по отказу глушил warning по latency того же провайдера.
Dead man's switch.absent(mail_probe_success) ловит смерть самой пробы. Мониторинг, который молча умер, страшнее отсутствия мониторинга — вы думаете, что всё хорошо. И отдельно учитесь читать разницу: если blackbox зелёный, submission принимает, но seed не долетает или долетает в спам — это блок на стороне провайдера, а не ваша поломка. Если же submission не принимает письмо — лёг ваш MTA.
Чек-лист внедрения
Seed-ящики заведены у 4 провайдеров (Gmail, Outlook, Yandex, Mail.ru), настоящие, не забитые тысячами старых проб.
Проба под systemd с Restart=always; интервал 2–5 минут — чаще нельзя, поймаете greylisting и rate-limit у приёмника.
Уникальный Message-ID на каждую пробу и регулярная чистка seed-ящиков (крон, удаляющий письма старше суток).
rDNS/PTR исходящего IP резолвится в HELO-имя: dig -x 203.0.113.10 +short должен вернуть ваш mail-хост.
Blackbox-проба без seed-цикла — это иллюзия мониторинга. Блокировку по IP на 25-м порту вы увидите, а тихий landing в спам — нет. Настоящий сигнал замыкается только через реальный чужой ящик, и стоит он одного маленького демона под systemd.