PromQL ile Metrik Sorgulama Rehberi

Prometheus’u kurdunuz, Grafana’yı ayarladınız, metrikler akıyor. Ama şimdi ne yapacaksınız? İşte tam bu noktada PromQL devreye giriyor. Prometheus Query Language, ilk bakışta biraz ürkütücü görünebilir ama bir kez alıştığınızda olmadan edemezsiniz. Bu yazıda gerçek dünya senaryoları üzerinden PromQL’i derinlemesine inceleyeceğiz.

PromQL’e Giriş: Temel Kavramlar

PromQL, Prometheus’un yerleşik sorgu dilidir. SQL’e benzer ama zaman serisi verileri için optimize edilmiştir. Her şeyden önce birkaç temel kavramı kafanıza oturtmanız gerekiyor.

Metrik türleri aslında her şeyin temelidir:

  • Counter: Sadece artan değerler. Örneğin toplam istek sayısı, hata sayısı. Asla azalmaz, sadece sıfırlanır.
  • Gauge: Artıp azalabilen anlık değerler. CPU kullanımı, bellek miktarı, bağlantı sayısı.
  • Histogram: Değerlerin dağılımını ölçer. HTTP yanıt süreleri, istek boyutları.
  • Summary: Histogram’a benzer ama quantile’ları client tarafında hesaplar.

Bu ayrımı bilmek önemli çünkü hangi tür metrikle çalıştığınıza göre farklı fonksiyonlar kullanmanız gerekiyor. Counter üzerinde rate() kullanırsınız, gauge üzerinde direkt matematiksel işlem yaparsınız.

İlk Sorgularınız

Prometheus UI’ında (genellikle http://sunucu-ip:9090) Expression Browser’ı açın. En basit sorgu sadece metrik adını yazmaktır:

node_cpu_seconds_total

Bu sorgu tüm CPU metriklerini getirir. Muhtemelen onlarca satır veri görürsünüz. Bunu anlamlı hale getirmek için label filtreleme kullanmanız gerekiyor.

node_cpu_seconds_total{mode="idle", instance="web-sunucu-01:9100"}

Süslü parantez içindeki ifadeler label selector olarak adlandırılır. Eşitlik operatörü = tam eşleşme, != eşit değil, =~ regex eşleşme, !~ regex eşleşmeme anlamına gelir.

# Prod ortamındaki tüm web sunucularının metrikleri
node_memory_MemAvailable_bytes{job="node", instance=~"web-.*"}

# Test ortamı hariç tüm sunucular
node_load1{env!="test"}

Rate ve Irate: Counter Metriklerde Oran Hesaplama

Sysadmin olarak en çok ihtiyaç duyacağınız fonksiyonların başında rate() geliyor. Counter metrikleri ham haliyle pek anlam ifade etmez. “Sunucu açılışından bu yana 2.3 milyon istek gelmiş” yerine “saniyede kaç istek geliyor” sorusunun cevabını istersiniz.

# Son 5 dakikadaki HTTP istek oranı (saniye başına)
rate(http_requests_total[5m])

# Node exporter üzerinden ağ trafiği (bytes/saniye)
rate(node_network_receive_bytes_total{device="eth0"}[5m])

# Disk I/O okuma hızı
rate(node_disk_read_bytes_total{device="sda"}[5m])

Köşeli parantez içindeki [5m] ifadesi range vector selector’dır. Prometheus’a “son 5 dakikalık veriyi al” diyorsunuz. Bu değeri neye göre belirleyeceksiniz? Genel kural şudur: Scrape interval’ınızın en az 4 katı olmalı. 15 saniyelik scrape interval için [1m] minimum, [5m] güvenli bir başlangıç noktasıdır.

irate() ise anlık oran hesaplar, son iki veri noktasını kullanır. Ani spike’ları yakalamak için idealdir ama gürültülü grafikler üretebilir:

# Ani CPU spike'larını yakalamak için
irate(node_cpu_seconds_total{mode="user"}[5m])

Pratik öneri: Uzun vadeli trendler ve dashboard’lar için rate(), alert kuralları için spike tespitinde irate() tercih edin.

Aggregation Operatörleri: Veriyi Anlamlandırmak

Gerçek ortamlarda onlarca sunucu izliyorsunuz. Her birini ayrı ayrı görmek yerine toplu bilgi istiyorsunuz. Aggregation operatörleri tam burada devreye giriyor.

# Tüm sunucuların toplam HTTP istek oranı
sum(rate(http_requests_total[5m]))

# Sunucu başına ortalama CPU kullanımı
avg by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m]))

# En yüksek bellek kullanan 5 sunucu
topk(5, node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)

# Her job grubundaki maksimum yük
max by(job) (node_load1)

by ve without clause’ları önemli. by(instance) ile sadece instance label’ına göre gruplarsınız, diğer tüm label’lar düşer. without(cpu) ise cpu label’ı hariç tüm label’lara göre gruplar.

# CPU modlarını dışarıda bırakarak instance bazında toplam
sum without(cpu, mode) (node_cpu_seconds_total)

# Ortam (env) ve iş (job) bazında istek sayısı
sum by(env, job) (rate(http_requests_total[5m]))

CPU Kullanım Yüzdesi Hesaplama

Bu, en sık sorulan sorulardan biri. Doğrudan CPU yüzdesi veren bir metrik yok, onu hesaplamanız gerekiyor:

# Tek sunucu için CPU kullanım yüzdesi
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Tüm CPU core'larını hesaba katarak daha doğru hesaplama
(1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) * 100

İkinci sorgu daha temiz ve aynı sonucu veriyor. avg by(instance) tüm CPU core’larının ortalamasını alıyor.

Matematiksel Operasyonlar ve Dönüşümler

PromQL’de temel matematiksel operatörlerin tamamını kullanabilirsiniz. Asıl güç, farklı metrikleri birleştirebildiğinizde ortaya çıkıyor.

# Bellek kullanım yüzdesi
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100

# Disk doluluk yüzdesi
(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_free_bytes{mountpoint="/"}) 
/ node_filesystem_size_bytes{mountpoint="/"} * 100

# Swap kullanım yüzdesi
(node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes) / node_memory_SwapTotal_bytes * 100

Birden fazla metriği karşılaştırırken binary operator matching kurallarına dikkat etmeniz gerekiyor. Prometheus, iki metriği eşleştirirken label’lara bakıyor:

# Hata oranı hesaplama (hata istekleri / toplam istekler)
sum(rate(http_requests_total{status=~"5.."}[5m])) 
/ 
sum(rate(http_requests_total[5m]))

# Job bazında hata oranı - on() ile label eşleştirme
sum by(job) (rate(http_requests_total{status=~"5.."}[5m])) 
/ on(job) 
sum by(job) (rate(http_requests_total[5m]))

Zaman Fonksiyonları: Offset ve Subquery

Bazen geçmişteki değerlerle karşılaştırma yapmak istiyorsunuz. Örneğin “Bu saatin trafiği bir hafta önceki aynı saate göre nasıl?” sorusunu sormak isteyebilirsiniz.

# Bir saat önceki CPU değeri
node_load1 offset 1h

# Bir hafta öncesiyle karşılaştırma
rate(http_requests_total[5m]) / rate(http_requests_total[5m] offset 1w)

# Günlük karşılaştırma - artış yüzdesi
(rate(http_requests_total[5m]) - rate(http_requests_total[5m] offset 1d)) 
/ rate(http_requests_total[5m] offset 1d) * 100

Subquery ise bir range fonksiyonunu başka bir range fonksiyonuna beslemenize olanak tanıyor:

# Son 1 saatin her 5 dakikalık maximum yük ortalaması
max_over_time(rate(http_requests_total[5m])[1h:5m])

# Son 6 saatteki en yüksek disk kullanımı
max_over_time(
  (node_filesystem_size_bytes - node_filesystem_free_bytes)[6h:15m]
)

Gerçek Dünya Senaryoları

Şimdiye kadar teorik bilgi verdik. Şimdi gelin, gerçekten işinize yarayacak sorguları yazalım.

Senaryo 1: Web Sunucusu Performans İzleme

Nginx veya Apache için temel performans metrikleri:

# Saniyedeki istek sayısı - status code bazında
sum by(status) (rate(nginx_http_requests_total[5m]))

# Aktif bağlantı sayısı
nginx_connections_active

# P95 yanıt süresi - en kritik metriklerden biri
histogram_quantile(0.95, sum by(le) (rate(http_request_duration_seconds_bucket[5m])))

# P99 yanıt süresi
histogram_quantile(0.99, sum by(le, handler) (rate(http_request_duration_seconds_bucket[5m])))

# 5xx hata oranı yüzdesi
sum(rate(http_requests_total{status=~"5.."}[5m])) 
/ sum(rate(http_requests_total[5m])) * 100

histogram_quantile() fonksiyonu özellikle dikkat gerektiriyor. le (less than or equal) label’ı histogram bucket’larını temsil ediyor. Bu fonksiyon ile SLA metriklerinizi rahatlıkla izleyebilirsiniz.

Senaryo 2: Veritabanı İzleme (PostgreSQL/MySQL)

# PostgreSQL aktif bağlantı sayısı
pg_stat_activity_count{state="active"}

# Saniyede çalışan sorgu sayısı
rate(pg_stat_database_xact_commit_total[5m]) + rate(pg_stat_database_xact_rollback_total[5m])

# Cache hit oranı - %90 altına düşerse sorun var
pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read) * 100

# Replikasyon gecikmesi (byte cinsinden)
pg_replication_lag

Senaryo 3: Kubernetes Ortamı

# Pod restart sayısı - sürekli restart eden pod'lar
sum by(pod, namespace) (kube_pod_container_status_restarts_total) > 5

# CPU limit kullanım oranı
sum by(pod) (rate(container_cpu_usage_seconds_total[5m])) 
/ sum by(pod) (kube_pod_container_resource_limits{resource="cpu"}) * 100

# Bellek limit kullanımı
sum by(pod) (container_memory_working_set_bytes) 
/ sum by(pod) (kube_pod_container_resource_limits{resource="memory"}) * 100

Alerting Kuralları İçin PromQL

Sadece dashboard değil, alert kuralları da yazıyorsunuz. Alert PromQL’i biraz farklı düşünmenizi gerektiriyor. Sonucun boş set döndürmesi alert tetiklememesi, değer döndürmesi ise alert tetiklemesi anlamına geliyor.

# Disk doluluk uyarısı - %85 üzeri
(node_filesystem_size_bytes{fstype!~"tmpfs|fuse.lxcfs"} 
- node_filesystem_free_bytes{fstype!~"tmpfs|fuse.lxcfs"}) 
/ node_filesystem_size_bytes{fstype!~"tmpfs|fuse.lxcfs"} * 100 > 85

# Yüksek CPU kullanımı - 5 dakika boyunca %90 üzeri
(1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) * 100 > 90

# Sunucu erişilemiyor - son 2 dakikada scrape başarısız
up == 0

# Bellek kritik seviyesi
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10

Alert kurallarında for clause ile birlikte kullanıyorsunuz. Prometheus’un rules.yml dosyanızda bu sorgular şu şekilde görünüyor:

# prometheus/rules/node_alerts.yml örneği
groups:
  - name: node.rules
    rules:
      - alert: HighCPUUsage
        expr: (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Yüksek CPU kullanımı: {{ $labels.instance }}"
          description: "CPU kullanımı %{{ $value | humanize }} seviyesinde"

Predict ve Trend Analizi

PromQL’in az bilinen ama güçlü özelliklerinden biri tahminleme yapabilmesidir.

# Mevcut büyüme hızıyla diskin kaç saat sonra dolacağını hesapla
predict_linear(node_filesystem_free_bytes{mountpoint="/"}[6h], 4 * 3600)

# Disk 24 saat içinde dolacak mı?
predict_linear(node_filesystem_free_bytes[6h], 24 * 3600) < 0

# Haftalık büyüme trendine göre bellek tahminlemesi
predict_linear(node_memory_MemAvailable_bytes[7d], 7 * 24 * 3600)

predict_linear() fonksiyonu basit lineer regresyon kullanıyor. İkinci parametre saniye cinsinden ne kadar ileriye bakacağınızı belirliyor. Bu sorguyu alert kuralına çevirirseniz disk dolmadan önce uyarı alırsınız:

# 4 saat içinde disk dolacaksa uyar
predict_linear(node_filesystem_free_bytes{mountpoint="/"}[6h], 4 * 3600) < 0

Grafana’da PromQL Kullanımı

Grafana’da PromQL yazarken birkaç önemli nokta var. Grafana bazı özel değişkenler sunuyor:

  • $__interval: Panel’in zaman aralığına göre otomatik interval
  • $__range: Seçili zaman aralığı
  • $instance: Dashboard değişkeni olarak tanımladığınız instance
# $__interval değişkeni ile dinamik rate hesabı
rate(http_requests_total[$__interval])

# $__rate_interval daha güvenli bir alternatif (Grafana 7.2+)
rate(http_requests_total[$__rate_interval])

# Dashboard değişkeni ile filtreleme
node_cpu_seconds_total{instance=~"$instance"}

Grafana’da Legend formatı da önemli. {{instance}} ile label değerlerini gösterge isimlerine ekleyebilirsiniz. {{instance}} - CPU şeklinde bir format kullanabilirsiniz.

Performans İpuçları

Sorgularınızın Prometheus’u gereğinden fazla zorlamasını engellemek için birkaç kural:

  • Label filtreleri eklendiğinde taranan veri miktarı azalır, daima mümkün olan en spesifik filtreleri kullanın
  • sum() içindeki metriklerde önceden by() clause eklemek hesaplamayı hızlandırır
  • Recording rules ile sık kullanılan karmaşık sorguları önceden hesaplatın
# Recording rule örneği - prometheus/rules/recording.yml
groups:
  - name: recording.rules
    interval: 30s
    rules:
      - record: job:http_requests:rate5m
        expr: sum by(job) (rate(http_requests_total[5m]))
      
      - record: instance:node_cpu_utilisation:rate5m
        expr: (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))

Recording rule’lar tanımlandıktan sonra direkt bu isimle sorgulayabilirsiniz:

# Önceden hesaplanmış metriği sorgula - çok daha hızlı
job:http_requests:rate5m{job="web"}

# Dashboard'da kullan
instance:node_cpu_utilisation:rate5m * 100

Debugging ve Troubleshooting

Sorgularınız beklendiği gibi çalışmıyorsa sistematik yaklaşın.

# Önce metriğin var olup olmadığını kontrol et
up{job="node"}

# Hangi label'ların mevcut olduğunu gör
count by(__name__) ({job="node"})

# Belirli bir metriğin tüm label kombinasyonları
group by(instance, job, mode) (node_cpu_seconds_total)

# Scrape hedeflerinin durumu
prometheus_target_scrape_duration_seconds
prometheus_target_scrape_sample_count

absent() fonksiyonu da debugging için çok işe yarıyor. Bir metrik beklendiği halde gelmiyorsa alert üretir:

# node_exporter çalışmıyorsa veya metrik eksikse uyar
absent(up{job="node", instance="web-sunucu-01:9100"})

# 10 dakikadır metrik gelmiyor mu?
absent_over_time(up{job="node"}[10m])

Sonuç

PromQL öğrenmek bir maraton, sprint değil. Önce basit label filtreleme ile başlayın, sonra rate() ve sum() kombinasyonlarına geçin, zamanla histogram_quantile() ve predict_linear() gibi gelişmiş fonksiyonlara ilerleyin.

En çok değer alacağınız alan kesinlikle alert kuralları. Dashboard güzel ama siz uyurken de sistemi izleyen, anlamlı alertler üreten sorgular yazmak asıl hedefiniz olmalı. predict_linear() ile proaktif disk alertleri, HTTP error rate ile servis sağlığı izleme, histogram_quantile() ile SLA takibi. Bunlar günlük hayatınızı ciddi anlamda kolaylaştıracak pratikler.

Recording rules’u da ihmal etmeyin. Özellikle büyük ortamlarda karmaşık sorguların tekrar tekrar hesaplanması Prometheus’a gereksiz yük bindiriyor. Sık kullandığınız metrikleri recording rule olarak tanımlamak hem performans kazandırıyor hem de sorguları okunabilir hale getiriyor.

Son olarak, Prometheus’un kendi /graph arayüzünü sadece test ortamı olarak değil, gerçekten kullanın. Grafana’ya geçmeden önce sorgularınızı burada test etmek, hataları erken yakalamanızı sağlar. Özellikle alert expression’larını Grafana’ya eklemeden önce Prometheus UI’da doğrulamak iyi bir alışkanlık.

Benzer Konular

Bir yanıt yazın

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