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_prometheus plugin’i ve per-object metrics endpoint’ini dikkatli scrape interval’larla kullanın.
  • Kafka için kafka_exporter ile 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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir