Cuma gecesi saat 23:40. Upstream'deki bir DNS sağlayıcısı MX çözümlemesinde takılıyor, sizin smtp transport'unuz komşu domainlere bağlanamıyor ve normalde 200 mesaj civarında gezinen deferred kuyruğu sessizce 8.000'e tırmanıyor. Kimsenin haberi yok, çünkü elinizdeki tek gözlem aracı ekranın köşesinde açık duran bir watch mailq. Sabah bounce raporları geldiğinde iş işten geçmiş, birkaç büyük sağlayıcı IP'nizi throttle etmeye başlamış oluyor.
Buradaki asıl sorun teknik değil, yapısal: bir mail sunucusunun sağlığını anlatan üç metrik üç ayrı yerde yaşıyor. Kuyruk derinliği showq soketinde, teslimat hızı teslimat loglarında, reddetme oranı cleanup loglarında. Hiçbiri tek ekranda değil, hiçbiri geçmişe dönük değil, hiçbiri eşik geçince sizi uyandırmıyor. mailq | grep -c ile yazılmış cron script'leri bu üçünden yalnızca birini, o da anlık ve tarihsiz şekilde görebiliyor.
Çözüm bu üç kaynağı tek bir /metrics endpoint'inde birleştirmek. kumina/postfix_exporter tam olarak bunu yapıyor: showq soketini ve Postfix loglarını okuyup Prometheus'un anlayacağı sayaç ve histogramlara çeviriyor. Üstüne Grafana ve Alertmanager koyduğunuzda, deferred kuyruğu 500'ü geçtiği anda — reputation sistemleri daha farkına varmadan — telefonunuz çalıyor.
Exporter neyi, nereden okur
postfix_exporter'ın iki tamamen ayrı veri kaynağı var ve aralarındaki farkı anlamak panonuzun doğru okunması için kritik:
- `/var/spool/postfix/public/showq` unix soketi — Postfix'in kuyruk yöneticisiyle aynı binary protokolü konuşur ve o an kuyrukta bekleyen her mesajın boyutunu ve yaşını verir. Bu anlık bir gauge: "şu anda deferred kuyruğunda 812 mesaj var".
mailq'nun kaynağıyla aynı veri, ama metin parse etmeden. - Postfix logları — journald (
--systemd.enable) ya da düz/var/log/mail.log. Exporter satırları izler ve olayları kümülatif sayaçlara çevirir: kaç mesaj teslim edildi, kaç tanesi reddedildi, kaç SASL auth denemesi başarısız oldu. Bunlar hep artan counter'lardır; anlamlı hale gelmeleri içinrate()ile türevini almanız gerekir.
mailq çıktısını neden parse etmiyoruz? Çünkü showq binary protokolü hem daha ucuz hem de kırılmaz — Postfix sürümü kuyruk çıktısının formatını değiştirdiğinde script'iniz ölmez. Ve loglar olmadan reddetme/teslimat olaylarını hiç göremezsiniz; bunlar kuyrukta durmaz, akıp geçer.
Kurulum: binary, systemd unit ve izinler
Exporter Go ile yazılı, tek bir statik binary. Release'ten indirin ya da go build ile derleyin, sonra yerine koyun:
sudo install -m 0755 postfix_exporter /usr/local/bin/postfix_exporterKritik nokta izinler. Exporter'ın hem showq soketini okuması hem de journald'a erişmesi gerekir. showq soketi postfix grubuna, journald ise systemd-journal grubuna aittir. En temiz çözüm exporter'ı doğrudan postfix kullanıcısıyla çalıştırıp systemd-journal'ı ek grup olarak vermektir; böylece ayrı bir servis kullanıcısı için ACL uğraşına girmezsiniz:
# /etc/systemd/system/postfix_exporter.service
[Unit]
Description=Prometheus Postfix Exporter
After=postfix.service
Wants=postfix.service
[Service]
User=postfix
Group=postfix
SupplementaryGroups=systemd-journal
ExecStart=/usr/local/bin/postfix_exporter \
--postfix.showq_path=/var/spool/postfix/public/showq \
--systemd.enable \
--systemd.unit=postfix.service \
--web.listen-address=127.0.0.1:9154 \
--web.telemetry-path=/metrics
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target--web.listen-address=127.0.0.1:9154 ile endpoint'i sadece localhost'a bağladık; Prometheus aynı makinede ya da tünel arkasında scrape etsin, dışarı asla açılmasın — metric'ler kuyruk içeriğine dair sinyaller ve SASL kullanıcı adları sızdırabilir. journald yerine düz log dosyası okuyacaksanız --systemd.enable'ı çıkarıp --postfix.logfile_path=/var/log/mail.log verin, ama o zaman exporter kullanıcısının o dosyayı okuyabildiğinden emin olun.
Servisi ayağa kaldırıp doğrulayın:
sudo systemctl daemon-reload
sudo systemctl enable --now postfix_exporter
curl -s localhost:9154/metrics | grep '^postfix_up'
# postfix_up 1postfix_up 1 görüyorsanız iş tamam. 0 görüyorsanız ya soket izni ya journald grup üyeliği eksiktir; journalctl -u postfix_exporter size hangisi olduğunu söyler. En sık takılınan yer showq soketinin postfix grubuna kısıtlı olması — exporter'ı postfix kullanıcısıyla çalıştırmanızın sebebi tam olarak bu.
Prometheus'a bağlama ve saklama
scrape config sade. Kuyruk metrikleri düşük kardinaliteli olduğundan 15 saniyelik aralık hem yeterince granüler hem de sunucuya yük bindirmiyor:
scrape_configs:
- job_name: 'postfix'
scrape_interval: 15s
static_configs:
- targets: ['127.0.0.1:9154']postfix_up metriğini kendi scrape sağlığınız için de kullanın: exporter çökerse up{job="postfix"} == 0 olur ve bunu ayrı bir "exporter down" alarmıyla yakalarsınız — panonuzun kör kalmasını izleyen bir bekçi. showq histogramları üzerinde sık sorgu atacaksanız, ağır histogram_quantile hesaplarını bir recording rule ile önceden hesaplayın ki Grafana her yenilemede TSDB'yi baştan taramasın:
groups:
- name: postfix_recording
rules:
- record: postfix:deferred_queue:count
expr: sum(postfix_showq_message_size_bytes_count{queue="deferred"})Okuman gereken metrikler
Exporter onlarca metrik üretir ama panonuz birkaç tanesinin etrafında döner. İşte gerçek adlarıyla önemli olanlar:
- `postfix_showq_message_size_bytes` — showq'dan gelen histogram,
queuelabel'ıyla (active,deferred,hold,incoming,maildrop). Kuyruktaki mesaj sayısı için histogram değerini değil_countson ekini kullanın:postfix_showq_message_size_bytes_count. Bu, en çok yapılan hatanın da cevabı — histogram'ın kendisi boyut dağılımıdır, sayım_count'tadır. - `postfix_qmgr_messages_removed_total` — kuyruk yöneticisinin mesajı kuyruktan kaldırması, yani başarılı teslimatın proxy'si. Teslimat hızınızın kalbi.
- `postfix_cleanup_messages_processed_total` ve `postfix_cleanup_messages_rejected_total` — girişte işlenen ve reddedilen mesajlar. Reddetme oranının payı ve paydası.
- `postfix_smtp_status_deferred`
Bu metriklerin üçü bir arada bir mail sunucusunun anlık sağlığını tam olarak tarif eder ve ben buna sağlık üçgeni diyorum: derinlik (deferred kuyruğu ne kadar dolu), hız (saniyede kaç mesaj çıkıyor) ve red oranı (girişte ne kadar reddediyoruz). Üçü aynı anda kötüyse sunucu ölmüş demektir; biri bozulup diğerleri iyiyse elinizde spesifik bir teşhis vardır. Aşağıdaki diyagram her kuyruk geçişini onu ölçen metriğe bağlıyor:
Grafana panelleri ve PromQL
Beş panel bir mail sunucusunun tüm operasyonel hikâyesini anlatır. Hepsi doğrudan kopyala-yapıştır:
# 1) Kuyruğa göre derinlik (time series, deferred/active stacked)
sum by (queue) (postfix_showq_message_size_bytes_count)
# 2) Teslimat hızı (mesaj/sn — time series)
sum(rate(postfix_qmgr_messages_removed_total[5m]))
# 3) Reddetme oranı (0-1 arası, stat panel, birim: percent 0.0-1.0)
sum(rate(postfix_cleanup_messages_rejected_total[5m]))
/ sum(rate(postfix_cleanup_messages_processed_total[5m]))
# 4) Deferral hızı (geçici hata/sn — time series)
sum(rate(postfix_smtp_status_deferred[5m]))
# 5) p95 teslimat gecikmesi (saniye — time series)
histogram_quantile(0.95,
sum(rate(postfix_smtp_delivery_delay_seconds_bucket[5m])) by (le))Derinlik panelini queue label'ına göre stacked area yapın; deferred'ın active'in üstünde birikmesini görsel olarak yakalamak, sayının kendisinden daha hızlı alarm verir. Teslimat hızı ile deferral hızını aynı grafiğe koyun — biri düşerken diğeri yükseliyorsa hikâye netleşir. Reddetme oranını ve p95 gecikmeyi ekranın üstünde birer stat panel olarak tutun; renk eşiği koyup 0.20 ve 300s üstünde kırmızıya dönmelerini sağlayın, böylece panoya bakar bakmaz durumu okursunuz.
Alarm kuralları
Pano güzeldir ama kimse 7/24 ona bakmaz. Asıl iş alarmlarda ve for: sürelerinde — bir eşiğin anlık ihlali gürültüdür, süregelen ihlali olaydır. for: süresi flapping'i keser:
groups:
- name: postfix_alerts
rules:
- alert: PostfixDeferredHigh
expr: postfix_showq_message_size_bytes_count{queue="deferred"} > 500
for: 10m
labels: {severity: warning}
annotations:
summary: "Deferred kuyruğu 10dk boyunca 500 üstünde"
- alert: PostfixDeferredCritical
expr: postfix_showq_message_size_bytes_count{queue="deferred"} > 2000
for: 10m
labels: {severity: critical}
- alert: PostfixRejectRatioHigh
expr: |
sum(rate(postfix_cleanup_messages_rejected_total[5m]))
/ sum(rate(postfix_cleanup_messages_processed_total[5m])) > 0.20
for: 15m
labels: {severity: warning}
- alert: PostfixQmgrStalled
expr: |
rate(postfix_qmgr_messages_removed_total[5m]) == 0
and postfix_showq_message_size_bytes_count{queue="deferred"} > 100
for: 5m
labels: {severity: critical}
annotations:
summary: "Kuyruk dolu ama qmgr hiç teslimat yapmıyor"
- alert: PostfixDeliveryLatencyHigh
expr: |
histogram_quantile(0.95,
sum(rate(postfix_smtp_delivery_delay_seconds_bucket[5m])) by (le)) > 300
for: 10m
labels: {severity: warning}En değerli alarm PostfixQmgrStalled. Kuyruk doluyken teslimat hızının sıfıra düşmesi neredeyse her zaman yapısal bir arızadır: transport tıkanmış, DNS çözülmüyor ya da qmgr'ın process limiti dolmuş. for: 5m ile geçici bir dalgalanmayı olaydan ayırır. Bu kuralları Alertmanager'a bağlayıp severity: critical olanları Slack yerine gerçekten uyandıran bir kanala (PagerDuty, telefon) yönlendirin — cuma gecesi senaryosunu tam olarak bu yakalar.
Devreye alma checklist'i
- [ ] showq soketi izni doğru: exporter
postfixkullanıcısıyla çalışıyor, soketpostfixgrubunda - [ ]
SupplementaryGroups=systemd-journalverildi vejournalctl -u postfix_exporterhata basmıyor - [ ]
curl -s localhost:9154/metrics | grep '^postfix_up'çıktısı1 - [ ] Endpoint sadece
127.0.0.1'e bağlı, dışarıya açık değil - [ ] Prometheus'ta target
UP,up{job="postfix"} == 1 - [ ] 5 panel ekranda: derinlik, teslimat hızı, reddetme oranı, deferral hızı, p95 gecikme
- [ ]
postfix:deferred_queue:count


