Mesaj Kuyruğunda İzleme: Prometheus ve Grafana ile Monitoring
Mesaj kuyruklarının izlenmesi konusu, çoğu ekibin “çalışıyor mu, tamam o zaman” mantığıyla geçiştirdiği ama sonunda büyük bir olay yaşandığında “keşke daha önce bakseydik” dediği alanlardan biri. Ben de bu yazıyı, tam olarak böyle bir gecenin sabahında, bir RabbitMQ cluster’ının sessiz sedasız çökmesinin ardından yazdım. Prometheus ve Grafana ikilisini mesaj kuyruklarına nasıl entegre edeceğimizi, hangi metriklere bakacağımızı ve gerçek bir üretim ortamında neleri izlememiz gerektiğini ele alacağız.
Neden Mesaj Kuyruğu Monitoring’i Farklıdır?
Standart bir web sunucusunu izlemek nispeten basittir: CPU, bellek, istek sayısı, hata oranı. Ama mesaj kuyruklarında tablo çok daha karmaşık. Bir queue’nun dolması anlık bir problem olmayabilir, consumer’lar yavaş çalışıyor olabilir, message TTL süresi dolup mesajlar kaybolabilir, ya da publisher tarafında bir spike gelip consumer’lar yetişemiyor olabilir. Bunların her biri farklı bir alarm ve farklı bir müdahale gerektirir.
Ayrıca mesaj kuyruğu sistemleri genellikle birden fazla servis arasında köprü görevi görür. Kuyruk çöktüğünde ya da yavaşladığında etki alanı çok geniş olabilir. Bir e-ticaret sisteminde order processing queue’su tıkandığında, siparişler işlenemez, stok güncellenmez, fatura oluşmaz, bildirim gitmez. Domino etkisi korkunç olabilir.
Temel Kavramlar: Ne İzliyoruz?
Hangi mesaj kuyruğu sistemini kullanırsanız kullanın (RabbitMQ, Kafka, ActiveMQ, Redis Streams), izlenmesi gereken temel metrik kategorileri benzerdir:
Throughput Metrikleri
- Saniyede yayınlanan mesaj sayısı (publish rate)
- Saniyede tüketilen mesaj sayısı (consume rate / deliver rate)
- Acknowledge oranı
Backlog ve Derinlik Metrikleri
- Queue’daki bekleyen mesaj sayısı (queue depth)
- Unacknowledged mesaj sayısı
- Dead letter queue derinliği
Gecikme Metrikleri
- Consumer lag (özellikle Kafka’da kritik)
- End-to-end mesaj gecikmesi
- Processing süresi
Kaynak Metrikleri
- Broker bellek kullanımı
- Disk kullanımı
- Bağlantı sayısı
- Channel sayısı
RabbitMQ + Prometheus Entegrasyonu
RabbitMQ, 3.8 sürümünden itibaren built-in Prometheus desteği sunar. Eğer daha eski bir sürüm kullanıyorsanız rabbitmq_prometheus plugin’ini aktif etmeniz gerekiyor.
# Plugin'i aktif etme
rabbitmq-plugins enable rabbitmq_prometheus
# Kontrol
rabbitmq-plugins list | grep prometheus
# Prometheus endpoint'ini test et
curl -s http://localhost:15692/metrics | head -50
Eğer birden fazla node’dan oluşan bir cluster varsa, her node kendi /metrics endpoint’ini açar. Prometheus’un tüm node’ları scrape etmesi gerekir. prometheus.yml dosyanıza şu şekilde ekleyebilirsiniz:
scrape_configs:
- job_name: 'rabbitmq'
static_configs:
- targets:
- 'rabbitmq-node1:15692'
- 'rabbitmq-node2:15692'
- 'rabbitmq-node3:15692'
scrape_interval: 15s
metrics_path: '/metrics'
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '([^:]+)(?::d+)?'
replacement: '${1}'
RabbitMQ’nun /metrics/per-object endpoint’i de var. Bu endpoint queue, exchange ve connection bazında detaylı metrikler verir ancak büyük sistemlerde ciddi bir yük oluşturabilir. Yüzlerce queue’nuz varsa scrape_interval‘i dikkatli ayarlayın, yoksa Prometheus scraping’in kendisi broker’a load bindirmeye başlar.
# Per-object metrics için ayrı bir job tanımlamak mantıklı olabilir
# prometheus.yml içinde:
- job_name: 'rabbitmq-detailed'
static_configs:
- targets: ['rabbitmq-node1:15692']
scrape_interval: 60s
metrics_path: '/metrics/per-object'
Kafka İçin Prometheus Entegrasyonu
Kafka’da işler biraz daha karmaşık. Native Prometheus desteği yok, JMX Exporter kullanmanız gerekiyor. JMX Exporter’ı Java agent olarak Kafka broker’a bağlarsınız.
# JMX Exporter'ı indirin
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar
# kafka-server-start.sh içinde ya da systemd service dosyasında KAFKA_OPTS'a ekleyin:
export KAFKA_OPTS="-javaagent:/opt/kafka/jmx_exporter/jmx_prometheus_javaagent-0.19.0.jar=9404:/opt/kafka/jmx_exporter/kafka-broker.yml"
JMX konfigürasyonu için temel bir kafka-broker.yml:
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
- pattern: kafka.server<type=BrokerTopicMetrics, name=MessagesInPerSec><>OneMinuteRate
name: kafka_server_brokertopicmetrics_messagesin_total
type: COUNTER
- pattern: kafka.server<type=BrokerTopicMetrics, name=BytesInPerSec><>OneMinuteRate
name: kafka_server_brokertopicmetrics_bytesin_total
type: COUNTER
- pattern: kafka.consumer<type=consumer-fetch-manager-metrics, client-id=(.+)><>records-lag-max
name: kafka_consumer_records_lag_max
labels:
client_id: "$1"
- pattern: kafka.server<type=ReplicaManager, name=UnderReplicatedPartitions><>Value
name: kafka_server_replicamanager_underreplicatedpartitions
type: GAUGE
Kafka için ayrıca kafka_exporter projesini kullanmak da yaygın bir tercih. Bu exporter consumer group lag’ini çok daha temiz bir şekilde raporluyor:
# kafka_exporter çalıştırma
docker run -d
--name kafka-exporter
-p 9308:9308
danielqsj/kafka-exporter
--kafka.server=kafka-broker1:9092
--kafka.server=kafka-broker2:9092
--kafka.server=kafka-broker3:9092
--web.listen-address=":9308"
Grafana Dashboard Yapılandırması
Metrikler akıyor, şimdi görselleştirme zamanı. Grafana’da bir RabbitMQ dashboard’u sıfırdan yapmak yerine, önemli panelleri nasıl yapılandıracağımıza bakalım.
Queue derinliği için temel bir PromQL sorgusu:
# Belirli bir queue'nun mesaj sayısı
rabbitmq_queue_messages{queue="order-processing", vhost="/"}
# Tüm queue'lar için - vhost bazlı toplam
sum by (vhost) (rabbitmq_queue_messages)
# Consumer sayısının sıfır olduğu queue'lar (tehlikeli durum)
rabbitmq_queue_messages{queue!~".*dead.*"} > 0 and rabbitmq_queue_consumers == 0
Kafka consumer lag için:
# Topic bazında maksimum lag
max by (topic, consumergroup) (kafka_consumergroup_lag)
# Belirli bir consumer group'un toplam lag'i
sum by (consumergroup) (kafka_consumergroup_lag{consumergroup="order-service"})
# Lag artış hızı - bu önemli, mutlak değer değil trend
rate(kafka_consumergroup_lag{consumergroup="order-service"}[5m])
Grafana panel konfigürasyonunda dikkat ettiğim bazı noktalar var. Queue derinliği grafiklerinde threshold değerleri mutlaka ayarlayın. Grafana’nın renk geçişleri aslında çok işe yarıyor: yeşilden sarıya, sarıdan kırmızıya. Ekibinizdeki herkes dashboard’a bakıp sağlık durumunu anlayabilsin.
Öte yandan Grafana’da annotations kullanmayı ihmal etmeyin. Deployment zamanlarını, bakım pencerelerini ve önemli olayları grafiklere işaretlemek, ilerleyen dönemde “o spike ne zamandı, ne yapmıştık” sorusunun cevabını vermenizi kolaylaştırır.
Alerting: Hangi Durumlarda Alarm Verelim?
İşte kritik bölüme geldik. Alarm kurallarını çok geniş tutarsanız ekip alarm yorgunluğuna girer, çok dar tutarsanız gerçek sorunları kaçırırsınız. Şu yaklaşımı öneririm:
Prometheus’ta alert kuralları için bir rabbitmq_alerts.yml dosyası:
groups:
- name: rabbitmq_alerts
interval: 30s
rules:
- alert: RabbitMQQueueDepthHigh
expr: rabbitmq_queue_messages{queue!~".*dead.*"} > 10000
for: 5m
labels:
severity: warning
annotations:
summary: "Queue {{ $labels.queue }} derinliği yüksek"
description: "{{ $labels.queue }} kuyruğunda {{ $value }} mesaj bekliyor. Consumer'lar yetişemiyor olabilir."
- alert: RabbitMQNoConsumers
expr: rabbitmq_queue_messages > 100 and rabbitmq_queue_consumers == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.queue }} kuyruğunda consumer yok"
description: "Kuyruğa mesaj geliyor ama okuyacak consumer bulunamıyor."
- alert: RabbitMQDeadLetterQueueGrowing
expr: rate(rabbitmq_queue_messages{queue=~".*dead.*|.*dlq.*|.*dlx.*"}[10m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "Dead letter queue büyüyor"
description: "{{ $labels.queue }} kuyruğu büyümeye devam ediyor. İşlenemeyen mesajlar var."
- alert: RabbitMQMemoryHigh
expr: rabbitmq_process_resident_memory_bytes / rabbitmq_resident_memory_limit_bytes > 0.85
for: 5m
labels:
severity: critical
annotations:
summary: "RabbitMQ bellek kullanımı kritik seviyede"
description: "Node {{ $labels.instance }} bellek kullanımı %{{ $value | humanizePercentage }}."
Kafka için consumer lag alarmı genellikle en kritik olanı:
- alert: KafkaConsumerGroupLagHigh
expr: sum by (consumergroup, topic) (kafka_consumergroup_lag) > 50000
for: 10m
labels:
severity: warning
annotations:
summary: "Kafka consumer lag yüksek"
description: "{{ $labels.consumergroup }} grubu {{ $labels.topic }} topic'inde {{ $value }} mesaj geride."
- alert: KafkaConsumerGroupStopped
expr: kafka_consumergroup_lag > 0 and rate(kafka_consumergroup_lag[5m]) >= 0
and rate(kafka_consumergroup_members[5m]) == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Consumer group durmuş olabilir"
description: "{{ $labels.consumergroup }} lag artıyor ama grup üyesi sayısı değişmiyor."
Gerçek Dünya Senaryosu: Flash Sale Hazırlığı
Bir flash sale öncesinde monitoring setup’ı hazırlarken yaşadığım süreci anlatayım. Sistemde RabbitMQ üzerinde çalışan sipariş işleme ve bildirim servisleri vardı. Normal günlerde queue derinliği 100-200 mesajda seyrediyor, consumer’lar rahatça yetişiyordu.
Flash sale başladığında saniyede 3000 sipariş gelmeye başladı. Consumer’ların toplam kapasitesi saniyede 500 siparişti. Queue derinliği dakikalar içinde milyonlara ulaştı.
Bu durumu önceden tespit etmek için kurduğumuz load test sırasında şu Grafana sorgusunu kullandık:
# Consumer'ların ne kadar sürede queue'yu boşaltabileceği tahmini
(rabbitmq_queue_messages{queue="order-processing"})
/
(rate(rabbitmq_queue_messages_acked_total{queue="order-processing"}[5m]))
Bu sorgu, mevcut tüketim hızında queue’nun kaç saniyede biteceğini verir. Flash sale simülasyonunda bu değerin 3 saate ulaştığını görünce consumer sayısını artırma kararını zamanında aldık.
Grafana’da bu senaryoya özel bir “capacity planning” dashboard’u oluşturduk. Sadece gerçek zamanlı değil, 1 saatlik ve 24 saatlik trendleri de gösteren, consume rate ile publish rate arasındaki farkı ayrı bir panel olarak öne çıkaran bir yapı. Bu “delta” paneli, sistemin ne kadar hızlı birikme yaşadığını net bir şekilde gösteriyor.
Alertmanager ile Akıllı Bildirim
Tüm bu alarmları doğru kişiye, doğru zamanda iletmek için Alertmanager konfigürasyonu da önemli. Mesaj kuyrukları için bazı özel durumlar var:
route:
group_by: ['alertname', 'queue']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'default'
routes:
- match:
severity: critical
alertname: RabbitMQNoConsumers
receiver: 'oncall-pager'
repeat_interval: 30m
- match:
alertname: RabbitMQDeadLetterQueueGrowing
receiver: 'dev-team-slack'
repeat_interval: 1h
receivers:
- name: 'oncall-pager'
pagerduty_configs:
- service_key: 'YOUR_SERVICE_KEY'
- name: 'dev-team-slack'
slack_configs:
- api_url: 'YOUR_SLACK_WEBHOOK'
channel: '#queue-alerts'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
Dead letter queue alarmlarının dev ekibine gitmesi önemli çünkü bu alarmlar genellikle bir altyapı değil, uygulama sorunu işaretidir. Consumer kodunda hata var, mesaj formatı beklenenden farklı, veya bağlı bir serviste sorun var gibi durumlar DLQ’ya mesaj yağmasına neden olur.
Monitoring’in Kendisini İzlemek
Son olarak, göz ardı edilen ama kritik bir nokta: monitoring sisteminin kendisinin izlenmesi. Prometheus scraping’in başarıyla çalışıp çalışmadığını kontrol edin:
# Son 5 dakikada scrape başarısız olan hedefler
up{job="rabbitmq"} == 0
# Scrape süresi çok uzun olan hedefler (timeout riski)
scrape_duration_seconds{job="rabbitmq"} > 10
Grafana’da bir “Monitoring Health” satırı oluşturmanızı öneririm. Bu satırda Prometheus’un her bir target’ı kaç saniyede scrape ettiği, son başarılı scrape zamanı ve toplam aktif alert sayısı gibi meta bilgileri tutun. Monitoring sisteminiz çöktüğünde siz de kör kalırsınız, bu yüzden en azından bir üst seviye watchdog mekanizması olması gerekiyor.
Sonuç
Mesaj kuyruğu monitoring’i, “kurdum, çalışıyor” deyip geçilecek bir konu değil. Doğru metrikler, akıllı alarm eşikleri ve iyi tasarlanmış dashboard’larla bir mesaj kuyruğu sisteminin iç dünyasını neredeyse gerçek zamanlı olarak görebilirsiniz.
Özetlemek gerekirse:
- RabbitMQ için
rabbitmq_prometheusplugin’i ve per-object metrics endpoint’ini dikkatli scrape interval’larla kullanın. - Kafka için
kafka_exporterile consumer lag takibini merkeze alın, JMX Exporter ile broker metriklerini tamamlayın. - Alarm kurallarında mutlak değerler kadar değişim hızına da bakın. Ani artışlar, sabit yüksek değerlerden çok daha tehlikeli sinyaller verebilir.
- Dead letter queue büyümesini asla sessizce geçiştirmeyin. Her DLQ mesajı, kayıp olan bir iş sürecidir.
- Capacity planning için consume/publish rate farkını ayrı bir metrik olarak izleyin.
- Alertmanager routing’ini alarmın niteliğine göre yapılandırın; altyapı alarmlari ops ekibine, uygulama alarmları dev ekibine gitsin.
Bu yapıyı doğru kurduğunuzda, bir sonraki büyük olay yaşandığında “neden çöktü” sorusunun cevabını logları eşeleyerek değil, Grafana’da 5 dakika içinde bulabilirsiniz.
