Kubernetes’te İzleme ve Metrik: Prometheus ve Grafana Entegrasyonu

Kubernetes cluster’ınız çalışıyor, pod’lar ayakta, servisler erişilebilir. Peki içeride ne oluyor? Hangi node yükleniyor, hangi pod bellek sıkıntısı yaşıyor, API yanıt süreleri ne durumda? Bu soruların cevabını almadan Kubernetes yönetmek, gözleri kapalı araba sürmek gibi. İşte tam bu noktada Prometheus ve Grafana ikilisi devreye giriyor.

Bu yazıda sıfırdan başlayarak production-ready bir izleme altyapısı kuracağız. Teorik anlatımdan çok elle tutulur, gerçek dünyada işe yarayan bir kurulum yapacağız.

Prometheus ve Grafana Neden Bu Kadar Popüler?

Prometheus, 2016’dan bu yana CNCF bünyesinde olan, pull-based çalışan bir metrik toplama sistemi. Kubernetes ekosistemiyle neredeyse doğuştan uyumlu çünkü her ikisi de Go ile yazılmış ve aynı tasarım felsefesini paylaşıyor. Grafana ise bu metrikleri görselleştiren, alert yönetimi yapan ve onlarca veri kaynağını destekleyen bir platform.

Bu ikilinin avantajları:

  • Kubernetes-native entegrasyon: Service discovery sayesinde yeni pod’ları otomatik keşfediyor
  • PromQL: Son derece güçlü sorgulama dili, karmaşık sorguları bile kısa yazabiliyorsunuz
  • Açık kaynak ve maliyet: Lisans maliyeti yok, community desteği güçlü
  • Ölçeklenebilirlik: Thanos veya Cortex ile büyük ölçekli cluster’larda çalışıyor

Ortam Gereksinimleri

Kuruluma geçmeden önce neye ihtiyacımız olduğunu netleştirelim:

  • Çalışan bir Kubernetes cluster’ı (minikube, k3s veya managed bir servis olabilir)
  • kubectl ve helm CLI araçları kurulu
  • Cluster’a admin yetkisi

Helm kurulu değilse hızlıca kuralım:

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

kube-prometheus-stack ile Hızlı Kurulum

Tek tek Prometheus, Alertmanager, node-exporter ve Grafana kurmak yerine kube-prometheus-stack Helm chart’ını kullanacağız. Bu chart tüm bileşenleri bir arada getiriyor ve Kubernetes için önceden hazırlanmış onlarca dashboard ve alert kuralı içeriyor.

Önce namespace oluşturalım ve Helm repo’yu ekleyelim:

kubectl create namespace monitoring

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

Şimdi özel bir values dosyası oluşturalım. Default değerlerle kurulum yapılabilir ama production ortamında storage, retention ve resource limitleri ayarlamak şart:

cat > prometheus-values.yaml << 'EOF'
prometheus:
  prometheusSpec:
    retention: 30d
    retentionSize: "50GB"
    storageSpec:
      volumeClaimTemplate:
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi
    resources:
      requests:
        memory: "2Gi"
        cpu: "500m"
      limits:
        memory: "4Gi"
        cpu: "2000m"

grafana:
  adminPassword: "GucluBirSifre123!"
  persistence:
    enabled: true
    size: 10Gi
  resources:
    requests:
      memory: "256Mi"
      cpu: "100m"
    limits:
      memory: "512Mi"
      cpu: "500m"

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 10Gi

nodeExporter:
  enabled: true

kubeStateMetrics:
  enabled: true
EOF

Kurulumu başlatalım:

helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack 
  --namespace monitoring 
  --values prometheus-values.yaml 
  --wait

Kurulum birkaç dakika sürecek. Tamamlandığında pod’ların durumunu kontrol edelim:

kubectl get pods -n monitoring

Tüm pod’lar Running durumuna geldiğinde Grafana’ya erişmek için port-forward kullanalım:

kubectl port-forward svc/kube-prometheus-stack-grafana 3000:80 -n monitoring

Tarayıcıdan http://localhost:3000 adresine gidip admin kullanıcısı ve belirlediğimiz şifre ile giriş yapabilirsiniz.

Prometheus’u Anlamak: Nasıl Çalışıyor?

Grafana güzel görüntüler sunuyor ama asıl işi Prometheus yapıyor. Birkaç temel kavramı anlamadan PromQL sorguları yazmak zorlaşıyor.

Metrik tipleri:

  • Counter: Sadece artan değerler. Örneğin toplam HTTP istek sayısı. http_requests_total
  • Gauge: Artıp azalabilen değerler. Örneğin mevcut CPU kullanımı, bellek
  • Histogram: Değerlerin dağılımını ölçer. Yanıt süreleri için idealdir
  • Summary: Histogram’a benzer, önceden hesaplanmış quantile değerleri sunar

Scrape ve Service Discovery:

Prometheus, hedeflerden metrik çekmek için HTTP endpoint’lere (genellikle /metrics) istekte bulunur. Kubernetes’te ServiceMonitor ve PodMonitor CRD’leri sayesinde hangi servislerin izleneceğini tanımlıyoruz.

ServiceMonitor ile Uygulama Metriklerini Toplamak

Diyelim ki bir Node.js veya Go uygulamanız var ve kendi metriklerini /metrics endpoint’inde yayınlıyor. Bu uygulamayı Prometheus’a tanıtmak için ServiceMonitor oluşturuyoruz.

Önce örnek bir uygulama deployment’ı:

cat > sample-app.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
  namespace: default
  labels:
    app: sample-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: prom/prometheus:latest
        ports:
        - containerPort: 9090
          name: metrics
---
apiVersion: v1
kind: Service
metadata:
  name: sample-app-svc
  namespace: default
  labels:
    app: sample-app
spec:
  selector:
    app: sample-app
  ports:
  - name: metrics
    port: 9090
    targetPort: metrics
EOF

kubectl apply -f sample-app.yaml

Şimdi bu servisi izlemek için ServiceMonitor:

cat > servicemonitor.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: sample-app-monitor
  namespace: monitoring
  labels:
    release: kube-prometheus-stack
spec:
  selector:
    matchLabels:
      app: sample-app
  namespaceSelector:
    matchNames:
    - default
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
    scrapeTimeout: 10s
EOF

kubectl apply -f servicemonitor.yaml

Buradaki kritik nokta release: kube-prometheus-stack label’ı. Prometheus Operator, hangi ServiceMonitor’ları alacağını bu label ile belirliyor. Eğer eklemeyi unutursanız metrikler gelmez ve saatlerce hata ayıklarsınız.

PromQL ile Gerçek Dünya Sorguları

Prometheus’u kurduk, metrikler akıyor. Şimdi bu verileri anlamlı hale getirelim. PromQL öğrenmenin en iyi yolu pratik sorular sormak:

CPU kullanımı yüksek node’ları bul:

100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Pod’ların bellek kullanımı (MB cinsinden):

container_memory_working_set_bytes{namespace="default", container!=""} / 1024 / 1024

Son 5 dakikada HTTP 5xx hata oranı:

rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100

OOMKilled olan pod’ları tespit et:

kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1

Node’larda disk doluluk yüzdesi:

100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100)

Bu sorguları Prometheus UI’ında (http://localhost:9090) deneyebilirsiniz. Grafana’ya geçmeden önce sorgunun doğru çalıştığını burada test etmek iyi bir alışkanlık.

Grafana Dashboard Oluşturma

kube-prometheus-stack kurulduğunda onlarca hazır dashboard geliyor. Bunlara erişmek için Grafana’da sol menüden “Dashboards” kısmına bakabilirsiniz. Ama kendi uygulamanıza özel dashboard oluşturmak çoğu zaman kaçınılmaz oluyor.

Grafana dashboard’larını JSON formatında export edip Git repo’nuza commit edebilirsiniz. Bu sayede dashboard’lar versiyon kontrolü altına girer. Daha da iyisi, ConfigMap kullanarak Grafana’ya otomatik yükleme yapabilirsiniz:

cat > grafana-dashboard-configmap.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-app-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"
data:
  custom-app.json: |
    {
      "title": "Uygulama Metrikleri",
      "panels": [
        {
          "title": "HTTP İstek Oranı",
          "type": "graph",
          "targets": [
            {
              "expr": "rate(http_requests_total[5m])",
              "legendFormat": "{{method}} {{status}}"
            }
          ]
        }
      ],
      "schemaVersion": 16
    }
EOF

kubectl apply -f grafana-dashboard-configmap.yaml

grafana_dashboard: "1" label’ı sayesinde Grafana bu ConfigMap’i otomatik olarak alıp dashboard olarak yüklüyor.

Alert Kuralları Tanımlamak

İzleme sadece görselleştirmeden ibaret değil. Bir şeyler ters gittiğinde haberdar olmak istiyoruz. PrometheusRule CRD’si ile alert kuralları tanımlayalım:

cat > alert-rules.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: uygulama-alerts
  namespace: monitoring
  labels:
    release: kube-prometheus-stack
spec:
  groups:
  - name: uygulama.kurallar
    interval: 30s
    rules:
    - alert: YuksekCPUKullanimi
      expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Node CPU kullanımı yüksek"
        description: "{{ $labels.instance }} node'unda CPU kullanımı {{ $value | printf "%.2f" }}% seviyesinde, 5 dakikadan uzun süredir."

    - alert: PodCrashlooBackoff
      expr: kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1
      for: 2m
      labels:
        severity: critical
      annotations:
        summary: "Pod CrashLoopBackOff durumunda"
        description: "{{ $labels.namespace }} namespace'inde {{ $labels.pod }} pod'u CrashLoopBackOff durumunda."

    - alert: DiskDoluluguYuksek
      expr: 100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100) > 80
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "Disk dolulugu kritik seviyede"
        description: "{{ $labels.instance }} sunucusunda disk kullanimi {{ $value | printf "%.0f" }}% seviyesinde."
EOF

kubectl apply -f alert-rules.yaml

for: 5m parametresi önemli. Bu, koşulun sürekli 5 dakika boyunca sağlanması gerektiği anlamına geliyor. Bu sayede geçici spike’lar gereksiz alert üretmiyor.

Alertmanager Yapılandırması

Alert kuralları tanımlandı, şimdi bu alertlerin nereye gönderileceğini belirleyelim. Slack entegrasyonu yaygın kullanılan bir yöntem:

cat > alertmanager-config.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
  name: slack-alerts
  namespace: monitoring
spec:
  route:
    receiver: slack-notifications
    groupBy: ['alertname', 'namespace']
    groupWait: 30s
    groupInterval: 5m
    repeatInterval: 4h
  receivers:
  - name: slack-notifications
    slackConfigs:
    - apiURL:
        name: slack-webhook-secret
        key: webhook-url
      channel: '#infrastructure-alerts'
      sendResolved: true
      title: '{{ if eq .Status "firing" }}ALARM{{ else }}COZULDU{{ end }}: {{ .CommonLabels.alertname }}'
      text: |
        {{ range .Alerts }}
        *Detay:* {{ .Annotations.description }}
        *Severity:* {{ .Labels.severity }}
        *Zaman:* {{ .StartsAt.Format "2006-01-02 15:04:05" }}
        {{ end }}
EOF

Webhook URL’ini secret olarak saklamak önemli:

kubectl create secret generic slack-webhook-secret 
  --from-literal=webhook-url='https://hooks.slack.com/services/XXX/YYY/ZZZ' 
  -n monitoring

Gerçek Dünya Senaryosu: Memory Leak Tespiti

Bir production senaryosu düşünelim. Gece 03:00’te Slack’e alert geliyor: “PodMemoryYuksek – payments-service-7d9f8b454-xk2p9”. Sabah geldiğinizde ne yapıyorsunuz?

Önce Grafana’da ilgili pod’un bellek grafiğine bakıyorsunuz:

container_memory_working_set_bytes{pod=~"payments-service.*", container="payments-service"}

Grafik düzenli olarak artıyor ve pod restart edildiğinde sıfırlanıyor. Bu klasik memory leak belirtisi.

Daha detaylı analiz için heap dump almak isteyebilirsiniz. Ama önce ne zaman başladığını anlayalım:

increase(container_memory_working_set_bytes{pod=~"payments-service.*"}[1h])

Bu sorgu son 1 saatte ne kadar bellek arttığını gösteriyor. Eğer her saat 100-200MB artıyorsa ciddi bir leak var demektir.

Pod’un restart geçmişine bakalım:

increase(kube_pod_container_status_restarts_total{pod=~"payments-service.*"}[24h])

Bu bilgilerle geliştirme ekibine somut veri sunabiliyorsunuz: “Son 6 saatte bellek kullanımı 450MB’dan 2.1GB’a çıktı, 3 kez OOMKilled ile restart edildi.”

Prometheus Federasyonu ve Çok Cluster Yönetimi

Birden fazla Kubernetes cluster’ınız varsa merkezi bir Prometheus kurulumu işleri kolaylaştırıyor. Basit bir federe yapılandırma:

cat > federation-scrape.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1alpha1
kind: ScrapeConfig
metadata:
  name: federation-prod
  namespace: monitoring
spec:
  staticConfigs:
  - targets:
    - "prometheus-prod-cluster.internal:9090"
    - "prometheus-staging-cluster.internal:9090"
  metricsPath: /federate
  params:
    'match[]':
    - '{job="kubernetes-pods"}'
    - '{__name__=~"kube_pod_.*"}'
    - '{__name__=~"node_.*"}'
  honorLabels: true
EOF

Bu yapılandırma ile prod ve staging cluster’larından sadece ilgilendiğiniz metrikleri çekebiliyorsunuz.

Performans Optimizasyonu

Prometheus’un yavaşladığını ya da çok bellek yediğini fark ederseniz birkaç noktayı kontrol edin:

Gereksiz yüksek kardinaliteli label’lardan kaçının. Örneğin her HTTP isteğine kullanıcı ID’si label olarak eklemek binlerce unique label oluşturur:

# KOTU - yüksek kardinalite
http_requests_total{user_id="12345", url="/api/v1/users/12345/profile"}

# IYI - düşük kardinalite
http_requests_total{endpoint="/api/v1/users/{id}/profile", method="GET"}

Recording rules ile sık kullanılan sorguları önceden hesaplatın:

cat > recording-rules.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: recording-rules
  namespace: monitoring
  labels:
    release: kube-prometheus-stack
spec:
  groups:
  - name: kubernetes.recording
    interval: 5m
    rules:
    - record: job:node_cpu_usage:avg5m
      expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

    - record: job:node_memory_usage_percent:avg
      expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
EOF

kubectl apply -f recording-rules.yaml

Bu recording rules sayesinde dashboard’larınızdaki karmaşık sorgular yerine önceden hesaplanmış job:node_cpu_usage:avg5m metriğini kullanabilirsiniz. Dashboard yükleme süreleri ciddi oranda düşüyor.

Grafana’da SLO Takibi

Modern operasyonlarda SLO (Service Level Objective) takibi kritik önem taşıyor. Örneğin “API’mizin %99.9’u 200ms altında yanıt vermeli” gibi bir hedefimiz varsa bunu Grafana’da nasıl takip ederiz?

# Error budget hesaplama
# 30 günde toplam dakika: 43200
# %99.9 için izin verilen hata dakikası: 43.2 dakika

# Mevcut hata oranı
sum(rate(http_requests_total{status=~"5.."}[30d])) /
sum(rate(http_requests_total[30d])) * 100
# Yüzde 99. percentile yanıt süresi
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

Bu iki metriği Grafana’da yan yana koyduğunuzda güçlü bir SLO dashboard’u elde ediyorsunuz.

Yaygın Sorunlar ve Çözümleri

Metrikler gelmiyor:

# Prometheus hedeflerini kontrol et
kubectl port-forward svc/kube-prometheus-stack-prometheus 9090:9090 -n monitoring
# http://localhost:9090/targets adresine git, hedeflerin durumuna bak

ServiceMonitor tanınmıyor:

# Prometheus'un hangi ServiceMonitor label'larını aradığını kontrol et
kubectl get prometheus kube-prometheus-stack-prometheus -n monitoring -o jsonpath='{.spec.serviceMonitorSelector}'

Yüksek bellek kullanımı:

# En fazla serie üreten iş/etiket kombinasyonlarını bul
# Prometheus'un /api/v1/label/__name__/values endpoint'ini kullan
curl http://localhost:9090/api/v1/label/__name__/values | python3 -m json.tool | grep -c '"'

Grafana dashboard’larında “No Data”:

# Grafana'nın Prometheus data source'unu test et
kubectl logs deployment/kube-prometheus-stack-grafana -n monitoring | grep -i "error|warn"

Sonuç

Prometheus ve Grafana entegrasyonu ilk bakışta karmaşık görünebilir ama kube-prometheus-stack Helm chart’ı bu süreci ciddi ölçüde basitleştiriyor. Bugün anlattıklarımızı özetleyelim:

  • kube-prometheus-stack ile hızlıca production-ready bir izleme altyapısı kurduk
  • ServiceMonitor ile uygulama metriklerini Prometheus’a tanıttık
  • PromQL ile anlamlı sorgular yazmayı öğrendik
  • PrometheusRule ile alert kuralları tanımladık
  • Alertmanager ile Slack bildirimleri yapılandırdık
  • Recording rules ile performansı optimize ettik
  • Gerçek bir memory leak senaryosunu nasıl araştıracağımızı gördük

En önemli tavsiye: izleme altyapınızı cluster’dan önce kurun. “Önce uygulamayı deploy edelim, sonra izlemeyi ekleriz” yaklaşımı çoğu zaman “production’da kriz çıktığında karanlıkta el yordamıyla çözüm aramak” şeklinde sonuçlanıyor.

Bir sonraki adım olarak Thanos veya Grafana Mimir ile uzun süreli metrik saklama ve çok cluster yönetimini inceleyebilirsiniz. Cluster büyüdükçe tek Prometheus instance’ı yetmemeye başlıyor ve bu araçlar devreye giriyor.

Yorum yapın