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)
kubectlvehelmCLI 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.