Exploring Infinite Possibilities on FrontEnd 🚀
Burak Sağlık
Search...

Sun Sep 13 2026

KEDA ile Kubernetes Event-Driven Autoscaling: HPA Sınırlarını Aşın

KEDA ile Kubernetes Event-Driven Autoscaling: HPA Sınırlarını Aşın

🎯 Kubernetes HPA Neden Yetersiz Kalıyor?

Selam! ☕
Hazırsan biraz sohbet edelim — senin de bildiğin o "HPA yeterli sanıyordum ama işler karıştı" anları üzerine.

Kubernetes ile çalışıyorsan Horizontal Pod Autoscaler (HPA) senin ilk dostun. cpu ve memory metriklerine bakıp pod sayısını artırır/azaltır. Kafası rahat, kurulumu basit. Ama... 🤔

Günlük hayatta şu "şüpheli" anlar başınıza gelmedi mi?

  • 📈 Aniden trafik patlaması oldu, HPA tetiklendi ama 3–5 dakika sonra yeni podlar Ready oldu. O arada kullanıcılar hata aldı, sen de loglarda 504 Gateway Timeout gördün.
  • 🧮 CPU %70 diye alarm çaldı, ama aslında işlem kuyruğunda 10.000 mesaj bekliyordu. CPU zaten düşük çünkü worker'lar bekliyor — HPA bunu göremez.
  • 🌐 HTTP istek sayısı arttı ama her istek çok hafif. CPU hareket etmedi, HPA "hepsi normal" dedi. Sen ise latency grafiğini izlerken terledin.
  • 📨 Event-driven iş yüklerin var (Kafka, RabbitMQ, SQS...). Kuyruk uzunluğu uzadı, ama HPA'nın haberi yok — o sadece metrics-server’dan CPU/memory okur.

Ne oluyor burada?
HPA resource-based bir scaler. "Sistem ne kadar güçlü çalışıyor?" sorusuna cevap verir.
Oysa senin asıl sorun: "Ne kadar iş bekliyor?" 🎯

İşte bu noktada KEDA (Kubernetes Event-driven Autoscaling) devreye girer.
Kuyruk uzunluğunu, HTTP RPS’yi, özel metrikleri dinleyip saniyeler içinde scale eder. HPA’nın göremediği "iş yükü" sinyalini algılar.

Bu yazıda:

  • HPA’nın sınırlarını netleştireceğiz 🔍
  • KEDA’nın nasıl kurtarıcı olduğunu pratik örneklerle göreceğiz 🛠
  • Senin de "evet, ben de yaşadım" diye nodullayacağın senaryoları paylaşacağım 😉

Hadi başlayalım — sırtını sırtına koyup bu sorunu çözelim! 🚀


❗️ HPA'nın Sıkıntılı Olduğu Senaryolar: Gerçek Hayattan Kesitler

HPA (Horizontal Pod Autoscaler) kagit üzerinde harika görünse de, prod'da başınızı ağrıtan üç klasik tuzak var. Her birini küçük bir "hikaye" ile anlatalım — mutlaka "evet, bana da oldu" diyeceksiniz 😅


1️⃣ Sadece CPU/Memory'e Bakıp Uygulama Mantığını Görmemek

Senaryo: E-ticket servisiniz var. Kullanıcı bilet alırken RabbitMQ kuyruğuna mesaj atıyor. Yoğun saatlerde kuyruk binlerce mesaj birikiyor ama pod'ların CPU'su %15, memory'si %30. HPA "haफफ, kaynak bol" diyip scale etmiyor. Sonuç? Kuyrukta 10 dakika bekleme, müşteri şikayetleri, oncall alarmı 🚨

Ne oluyor burada?
HPA default'ta sadece cpu ve memory metric'lerine bakar. Ama sizin bottleneckınız CPU değil, kuyruk derinliği (queue depth).

# HPA tanımında custom metric eklemeden bu durum kurtulamaz
kubectl get hpa ticket-service -n prod
# NAME            REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# ticket-service  Deployment/ticket-service  15%/80%   3         20        3          4h

Bu sayede ne oluyor?
Pod'lar "rahat" görünüyor ama işlem kuyrukta takılı kalıyor. Kullanıcı "sistem yanıt vermiyor" diyor, siz de "CPU düşük, neden scale etmiyor?" diye kafayı yiyorsunuz.

İpucu: prometheus-adapter ile rabbitmq_queue_messages_ready metric'ini HPA'ya besleyin. Ama o da ayrı bir dert (3. senaryoya bakın) 😬


2️⃣ Metrik Toplama ↔ Ölçeklendirme Kararı Arasındaki Gecikme

Senaryo: Flash sale başlıyor. Trafik 30 saniyede 10x artıyor. HPA tetikleniyor... ama 2 dakika sonra. O 2 dakikada pod'lar OOM kill oluyor, yeni pod'lar CrashLoopBackOff ye giriyor, kullanıcılar "503 Service Unavailable" alıyor.

Neden?
İki ana gecikme kaynağı var:

Kaynak Default Süre Ne Yapıyor?
Metrics Server collection interval ~60s Pod metric'lerini toplar
HPA stabilization window (scale-up) 0s (default) ama metric buffer var Ölçeklenme kararı için "güvenli" veri bekler
HPA stabilization window (scale-down) 300s Gereksiz scale-down'u engeller

Gerçek log örneği (zaman damgalı):

2024-11-15T10:00:00Z  [METRICS]  cpu=12%  memory=28%  queue_depth=45
2024-11-15T10:00:30Z  [TRAFFIC]  RPS: 200 → 2,500  (flash sale başladı)
2024-11-15T10:01:00Z  [METRICS]  cpu=78%  memory=85%  queue_depth=1,200
2024-11-15T10:01:15Z  [HPA]      Evaluation: target 80% cpu, current 78% → **no action yet**
2024-11-15T10:02:00Z  [METRICS]  cpu=95%  memory=98%  queue_depth=4,800
2024-11-15T10:02:10Z  [HPA]      Scale-up decision: 3 → 8 replicas
2024-11-15T10:02:45Z  [KUBELET]  New pods: Pending → ContainerCreating
2024-11-15T10:03:30Z  [KUBELET]  Pods Ready: 8/8

Gördünüz mü? Trafik patlaması 10:00:30'da, HPA kararı 10:02:10'da, pod'lar ready 10:03:30'da. 3 dakika kaybettik. O arada queue 45 → 4,800'e çıktı.

Bunu kubectl get hpa -w ile canlı izlerseniz:

kubectl get hpa ticket-service -n prod -w
NAME            REFERENCE               TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
ticket-service  Deployment/ticket-service  12%/80%    3         20        3          4h
ticket-service  Deployment/ticket-service  78%/80%    3         20        3          4h
ticket-service  Deployment/ticket-service  95%/80%    3         20        3          4h
ticket-service  Deployment/ticket-service  95%/80%    3         20        8          4h   ← scale-up oldu (gecikmeli)

Ne yapabilirsiniz?

  • --horizontal-pod-autoscaler-sync-period (default 15s) kısaltılabilir (controller-manager flag)
  • behavior.scaleUp.stabilizationWindowSeconds: 0 (default zaten 0 ama metric buffer yine de var)
  • En etkili: Predictive scaling (KEDA, custom controller) veya pre-warm stratejisi (schedule ile önceden scale-up)

3️⃣ Çoklu Metrik / Özel Metrik Desteğinin Zayıflığı ve Prometheus Adapter Zahmeti

Senaryo: Hem CPU hem de RabbitMQ kuyruk derinliği hem de HTTP response time (p99) bazında scale etmek istiyorsunuz. HPA v2 API metrics: array'i destekliyor ama:

  1. Her metric için Prometheus Adapter kurup, rules: yazıp, discovery: yapıp, resourceOverride: falan eklemeniz gerekiyor
  2. Adapter deploy ettiniz, custom.metrics.k8s.io API'si gelmedi → kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1"404
  3. RBAC hatası, servis account eksik, namespace mismatch... 3 saat geçti, hala "no metrics found" alıyorsunuz 😭

Gerçek bir Prometheus Adapter values.yaml parçası (ve ağrısı):

rules:
  - seriesQuery: 'rabbitmq_queue_messages_ready{namespace!="",queue!=""}'
    resources:
      overrides:
        namespace:
          resource: namespace
        queue:
          resource: queue
    name:
      matches: "^(.*)$"
      as: "queue_messages_ready"
    metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'

Sonra HPA'da bunu nasıl referans alıyorsunuz?

metrics:
  - type: Pods
    pods:
      metric:
        name: queue_messages_ready
      target:
        type: AverageValue
        averageValue: "100"
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Ve tabii ki işe yaramıyor. Neden? Çünkü:

  • Adapter'ın discovery yapabilmesi için Prometheus'un kube-pod job'ının pod label'larını düzgün scrape etmesi lazım
  • Metric adı queue_messages_ready ama Prometheus'ta rabbitmq_queue_messages_readynaming mismatch
  • Namespace selector HPA'da namespace: yok → adapter hangi namespace'i sorgulayacağını bilmiyor

Bu sayede ne oluyor?
Siz "custom metric HPA" yazıyorsunuz, prod'da kubectl describe hpaUnable to get metrics for resource ...: unable to fetch metrics from custom metrics API hatası alıyorsunuz. Oncall'e "HPA çalışmıyor" diye mesaj atıyorsunuz, aslında adapter config'i bozuk 🤦‍♂️

Gerçekçi tavsiye:

  • Basit ihtiyaçlar için KEDA kullanın (RabbitMQ, Kafka, HTTP, Cron scaler hazır gelir, adapter uğraşası yok)
  • İleri seviye için: Prometheus Adapter yerine VictoriaMetrics / Thanos + custom metrics server stack'i değerlendirin
  • Veya Karpenter + KEDA kombinasyonu ile node + pod level scaling'i bir arada yönetin

🎯 Özet: Bu Üç Tuzağı Nasıl Kaçarız?

Tuzaç Hızlı Çözüm Kalıcı Çözüm
Sadece CPU/memory kubectl patch hpa --patch '{"spec":{"metrics":[{"type":"Pods",...}]}}' KEDA / Custom Metrics API
Gecikme (lag) --sync-period=10s, behavior.scaleUp.stabilizationWindowSeconds=0 Predictive scaling, pre-warm
Prometheus Adapter zahmeti KEDA kurun, bitirin Metrics pipeline yeniden tasarımı

Benim prod notum: HPA "basit işler için" harika. Ama event-driven, latency-sensitive, multi-metric workload'larda KEDA + Karpenter ikilisine geçince hayatımız kurtuldu. Siz de deneyin, oncall gece uyanmaz 🌙✨


Hazırsanız bir sonraki bölümde KEDA ile "5 dakikada RabbitMQ tabanlı autoscaling" kurulumuna geçelim 🚀


🎯 KEDA Nedir ve Neden Farklı? Event-Driven Ölçeklendirmenin Gücü

Hazırsan KEDA'yla tanışalım 🤝 KEDA (Kubernetes Event-driven Autoscaling) — CNCF graduated projesi, yani toplulukta olgunlaşmış, production-ready bir standard haline gelmiş.

Özetle: HPA'nın (Horizontal Pod Autoscaler) üzerine kurulu, ama HPA'nın yapabildiğinden çok daha esnek bir yapı sunuyor.

🤔 HPA Neden Yetersiz Kalıyor?

Kubernetes'in built-in HPA'sı harika bir araç, ama temel bir kısıtı var: pull model ile çalışıyor. Yani:

  • Metrikleri periyodik olarak çeker (varsayılan 15-30 saniyede bir)
  • Sadece CPU, Memory ya da Custom Metrics API üzerinden expose edilen metrikleri okuyabilir
  • Event kaynakları (Kafka, RabbitMQ, Azure Queue, vs.) doğal bir şekilde desteklemez

Soru şu: Benim mikroservisim bir kuyruk dinliyorsa, neden CPU'yu izlesin? 🤷‍♂️

Kuyrukta 10.000 mesaj birikmiş ama CPU %5'de kalabilir — consumer'lar bekliyor olabilir, ya da işlem I/O bound olabilir. HPA bunu görmez, scale etmez. İşte tam bu noktada KEDA devreye giriyor.


⚡ KEDA'nın Farkı: Scaler Kavramı

KEDA, ScaledObject (veya ScaledJob) CRD'leri aracılığıyla herhangi bir event kaynağından metrik almanı sağlıyor. Bunu Scaler denilen bileşenlerle yapıyor:

Event Kaynağı Scaler Adı Ne İşe Yarar?
📨 Kafka kafka Consumer group lag'ini izler
🐰 RabbitMQ rabbitmq Kuyruk uzunluğunu (message count) okur
☁️ Azure Queue / Service Bus azure-queue / azure-servicebus Cloud queue metrikleri
📊 Prometheus prometheus Herhangi bir PromQL sorgusu
🌐 HTTP http Custom endpoint'ten JSON metrik çeker
Cron cron Zaman tabanlı scale (örn. gece yoğunluğu)
...ve 60+ scaler daha!

Push/Event modeli: Scaler'lar event kaynağından anlık metrik alır (pull değil, event-driven). Kuyrukta mesaj birikince saniyeler içinde pod sayısı artar. ⚡


✅ HPA ile Uyumlu — Değil, HPA Üretir!

Bu kısmı çok net anlatayım: KEDA, HPA'yla çakışmaz, HPA yaratır. 🎯

Sen bir ScaledObject yazıyorsun, KEDA arka planda sizin için bir HorizontalPodAutoscaler resource'u oluşturuyor. Yani:

  • Mevcut HPA davranışını (min/max replicas, behavior, stabilization window) koruyorsun
  • kubectl get hpa dediğinde KEDA'nın yarattığı HPA'ları görüyorsun
  • Rollback, debugging, mevcut tooling (ArgoCD, Flux, vs.) sorunsuz çalışıyor
# Örnek: KEDA ScaledObject — Kafka consumer lag'ine göre scale
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaledobject
  namespace: production
spec:
  scaleTargetRef:
    name: order-processor       # Deployment adı
  pollingInterval: 5            # Her 5 saniyede bir metrik kontrol et
  cooldownPeriod: 300           # Scale-down sonrası 5 dk bekle
  minReplicaCount: 2
  maxReplicaCount: 50
  triggers:
  - type: kafka
    metadata:
      bootstrapServers: kafka-cluster:9092
      consumerGroup: order-processor-group
      topic: orders
      lagThreshold: "100"       # Her pod başına 100 mesaj lag -> yeni pod

Bu sayede ne oluyor?

  • Kafka topic'inde orders kuyruğu birikmeye başladığında → saniyeler içinde pod sayısı artar
  • İş bitince → cooldownPeriod sayesinde yumuşak bir şekilde scale-down olur
  • CPU/memory'e bakmadan, iş yükünün gerçek sinyaline (lag) göre ölçeklenir 🎯

🛠 Ne Zaman KEDA Kullanmalısın?

Kısaca: Event-driven bir iş yükün varsa, KEDA kullanma.

  • 📥 Message queue consumer'ları (Kafka, RabbitMQ, SQS, vb.)
  • 🔄 Async job processor'lar
  • 📈 Prometheus metriklerine göre scale (örn. HTTP request latency, error rate)
  • ⏰ Zamanlı yoğunluklar (cron scaler ile gece/gündüz farklı kapasite)
  • 🌐 Webhook / HTTP endpoint'lerine gelen trafiğe göre scale

CPU tabanlı scale hâlâ işinizi görüyorsa (örn. CPU-bound bir hesaplama servisi), native HPA yeterli. Ama kuyruk, event, mesaj dili konuşan bir sistemdeysen — KEDA tam size göre. ✨


Bir sonraki bölümde ScaledObject'in anatomisini (triggers, authentication, fallback, advanced config) tek tek inceleyeceğiz. Hadi oraya gidelim! 🚀


🔁 KEDA Mimarisi ve Bileşenleri: ScaledObject, TriggerAuthentication ve Scaler'lar

KEDA, bir Kubernetes cluster'ı için "event-driven autoscaling" sunan bir operatördür. Bunu oluşturan üç temel parça:

  • ScaledObject – ölçeklendirilecek hedef deploy'ı (veya başka bir K8s işlemi) tanımlar.
  • TriggerAuthentication – scaler'ların güvendeyken event'leri okuması için gereken güvenlik bilgilerini (dış servis URL'leri, API anahtarları, OAuth etc.) saklar.
  • Scaler – dinlediği event türüne göre pod sayısını artırıp azaltan görevleri çalıştırır (örneğin: v1/pod, kafka, rabbitMQ, aws-sqs, prometheus, GPU kullanımı vb.).

Bunlar birbiriyle nasıl bağlanır? 🎯

ScaledObject
 ├─ scaleTargetRef → Deployment/my-app
 ├─ triggers
 │    ├─ type: kafka
 │    ├─ authenticationRef → TriggerAuthentication/my-kafka-auth
 └─ (opsiyonel) pollingInterval, cooldown, etc.
  1. ScaledObject veritabanından gelen mesajlar veya başka bir event kaynağı seçer (triggers alanı).
  2. Bir event (örneğin yeni bir Kafka mesajı) geldiğinde, ilgili Scaler (Kafka için yazılan scaler) hesabı yapmaya çalışır.
  3. Scaler, belirlenen limitlere dayanarak pod sayısını nasıl değiştireceğini bilmek ister. Bu nedenle TriggerAuthentication nesnesine bakar ve API anahtarlarını veya bağlı kuruluş kimlik bilgilerini alır.
  4. İsteğe bağlı bir HPA (horizontalPodAutoscaler) tanımlayabilirsin ve KEDA operatörü bunu cluster'ın kendi HPA kaynakları gibi yönetir. Böylece renkli UI'ye bakarken bile her şey otomatik olarak çalışıyor olur.

ScaledObject'ı basitçe inceleyelim

Aşağıda, standart bir ScaledObject'ın minimum tanımı var:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: myapp-scaledobject
spec:
  scaleTargetRef:
    name: myapp-deployment      # ölçeklendirilecek deployment
  pollingInterval: 30           # saniye cinsinden (isteğe bağlı)
  triggers:
    - type: kafka
      metadata:
        bootstrapServers: my-kafka:9092
        topic: my-topic
        lagThreshold: "10"
      authenticationRef:
        name: my-kafka-auth

Ne oluyor?

  • scaleTargetRef sayesinde KEDA operatörü myapp-deployment adındaki Deployment'u bulur ve öncelikle bu hedefi bilir.
  • triggers listesi içinde bir Kafka scaler'ı tanımladık. Scaler kendisi Kafka broker'ını okumaz, yalnızca bizim tanımladığımız kimlik bilgilerine dayanır.
  • authenticationRef ile my-kafka-auth adında bir TriggerAuthentication nesnesine bağlanıyoruz. Bu nesne aşağıdaki gibi görünebilir:
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: my-kafka-auth
spec:
  secret:
    name: kafka-secret
    key: credentials   # örneğin: "username" ve "password" anahtarlarını içerir

Sonuç: KEDA operatörü bir ScaledObject tanıdığında, otomatik olarak bir HPA kaynağı oluşturur ve kendi kapsayıcısına referans verir. Bu, cluster'ın standart autoscaling yönetiminde olduğunu düşünmene neden olurken, aynı zamanda event-driven "gerçekten" çalıştığını görebilmen için size kilitli bir pencere sunar.

Desteklenen event kaynakları listesi (hızlı bir bakış) 🔄

  • v1/pod – pod ölçümleri (CPU, bellek, veya özel metrikler)
  • kafka – Kafka topic'leri (topic'lerdeki bekleyen mesajlar üzerinden ölçeklendirme)
  • rabbitmq – RabbitMQ kuyruğu uzunluğu
  • aws-sqs / aws-dynamodb – AWS Service mesh
  • prometheus – özgür metrikler (örneğin: sistem kullanım oranı, CPU kullanım süresi)
  • gpu – GPU kullanılabilirliği
  • cron – zamanlanmış tetikleyiciler (ölçeklendirme "uyandırma zamanlaması" gibi)
  • custom – herhangi bir REST API veya HTTP endpoint'i

Cluster'ımda nasıl görünebilir? 📦

Cluster'ında zaten KEDA operatörü kurmuşsan (örneğin helm install keda /charts/keda), işte neler görürsün:

  1. ScaledObjectkubectl get scaledobjects.keda.sh içinde görünür ve pod sayısını direkt kontrol edebilirsin.
  2. TriggerAuthenticationkubectl get triggerauthentications.keda.sh içinde görünür; bilgilerini kontrol et.
  3. HPAkubectl get hpa içinde scaledobjectın adı ile görünür (operatör ona bir "scaleTargetRef" içeren bir HPA oluşturur).

Eğer operatörün güncel değilse, HPA kaynağını kendin ekleyebilirsin, ancak KEDA operatörü gerçek event-driven ihtiyaçları göz önüne alarak bu yönetimi üstlenmeyi tercih eder.


Sonuç olarak: ScaledObject bir target, TriggerAuthentication bir kimlik bilgileri deposu, Scaler bir event tetikleyicisidir. Bir araya geldiklerinde, bir event başladığında pod sayısını otomatik olarak ayarlamak için ihtiyacın olan her şeye sahip olursun ve tüm bu süreci KEDA operatörü tarafında yerleşik bir HPA ile yönetebilirsin. 🚀


🛠 HPA'dan KEDA'ya Adım Adım Geçiş: Pratik Migrasyon Rehberi

Hazırsan başlayalım. Ben kendi cluster'ımda şu sırayla hareket ettim ve her adımda karşılaştığım küçük sürprizleri de not aldım 👇

1️⃣ KEDA'yı cluster'a kur (Helm ile)

Önce repo ekleyip chart'ı yüklüyorum. Namespace keda olsun diye --create-namespace kullanıyorum.

helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace

İpucu: helm install sonrası kubectl get pods -n keda ile keda-operator podunun Running olduğunu kontrol et. Ben ilk denemede ImagePullBackOff aldım; imagePullSecrets ekleyip tekrar helm upgrade --install yaptım ve sorun çözüldü ✅


2️⃣ Mevcut HPA'yı sil (veya suspend et)

KEDA kendi HPA'sını yöneteceği için eski HPA çakışmasın. Ben silmeyi tercih ettim, ama kubectl patch hpa my-app-hpa -p '{"spec":{"replicas":0}}' ile suspend de edebilirsin.

kubectl delete hpa my-app-hpa

Not: HPA silinmeden önce kubectl get hpa my-app-hpa -o yaml > hpa-backup.yaml almayı unutma. Rollback planın bu dosya olsun 🔁


3️⃣ Uygulamanın event kaynağını belirle

Ben Kafka topic lag'ini tetikleyici olarak seçtim. Diğer yaygın seçenekler: RabbitMQ kuyruk uzunluğu, Prometheus metriği, Azure Queue vb. Hangi kaynağı kullanacaksan o provider'ın trigger type'ını not et.


4️⃣ TriggerAuthentication oluştur (SASL/SSL secret)

Kafka için SASL kimlik bilgilerini bir Secret'te tutuyorum, sonra TriggerAuthentication ile referans veriyorum.

# Secret oluştur (bir kere)
kubectl create secret generic kafka-secret \
  --from-literal=saslUser=myuser \
  --from-literal=saslPassword=mypassword \
  -n default
# triggerauth.yaml
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: kafka-trigger-auth
  namespace: default
spec:
  secretTargetRef:
    - parameter: saslUser
      name: kafka-secret
      key: saslUser
    - parameter: saslPassword
      name: kafka-secret
      key: saslPassword
kubectl apply -f triggerauth.yaml

Hata aldım: TriggerAuthentication namespace uyuşmazlığı verdi. metadata.namespace ile Secret'ın aynı namespace'te olduğundan emin ol ❗️


5️⃣ ScaledObject yaz ve apply et

Şimdi asıl ölçeklendirme tanımı. pollingInterval 15 sn, lagThreshold 10 mesaj olarak ayarladım.

# scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: my-app-scaledobject
  namespace: default
spec:
  scaleTargetRef:
    name: my-app
  pollingInterval: 15
  cooldownPeriod: 300
  minReplicaCount: 1
  maxReplicaCount: 20
  triggers:
    - type: kafka
      metadata:
        topic: my-topic
        bootstrapServers: kafka:9092
        consumerGroup: my-consumer-group
        lagThreshold: "10"
      authenticationRef:
        name: kafka-trigger-auth
kubectl apply -f scaledobject.yaml

Pratik ipucu: cooldownPeriod çok kısa olursa thrashing olur. Ben 5 dk (300 sn) verdim, gözlemledim ve istikrarlı oldu ✅


6️⃣ Ölçeklendirme davranışını doğrula

Her şey yolunda mı? Şu komutlarla anlık durum kontrol ediyorum.

kubectl get scaledobject my-app-scaledobject -n default
kubectl get hpa -n default
kubectl describe scaledobject my-app-scaledobject -n default
  • scaledobject Ready ve Active göstermeli.
  • KEDA otomatik bir HPA oluşturur; kubectl get hpa çıktısında my-app-scaledobject isimli HPA görmelisin.
  • describe altındaki Metrics bölümünde kafka trigger'ının current lag değerini takip et 🎯

🔄 Rollback planı hatırlatması

  1. kubectl delete scaledobject my-app-scaledobject -n default
  2. kubectl apply -f hpa-backup.yaml (1. adımda aldığın yedek)
  3. Gerekirse helm uninstall keda -n keda ile KEDA'yı kaldır.

Bu adımları sırayla uyguladığında HPA → KEDA geçişi sorunsuz ve gözlemlenebilir olur. Herhangi bir adımda hata alırsan logları (kubectl logs -n keda deploy/keda-operator) incele, genellikle authenticationRef veya bootstrapServers typo'su çıkıyor 😉

Keyifli ölçeklendirmeler! 🚀


🛠 Gerçek Hayat Örneği: Kafka Tüketici Mikroservisini KEDA ile Ölçeklendirme

Hazırsan şimdi somut bir örnek üzerinden KEDA'nın gücünü görelim. Elimizde basit bir Node.js Kafka consumer mikroservisi var. Mesajları işliyor, veritabanına yazıyor. Trafik geceleri düşük, gündüzlerde patlıyor. Eski yöntemle (CPU tabanlı HPA) ya geç ölçekleniyor ya da gereksiz pod yaratıyorduk. KEDA ile lag'e (kuyruk birikimine) bakarak tam isabetli ölçeklendirme yapacağız. 🎯


1️⃣ Deployment — Uygulama Pod'u

Önce consumer deployment'ımız. Image, env vars ve resource limitleri burada.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-consumer
  namespace: production
  labels:
    app: order-consumer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: order-consumer
  template:
    metadata:
      labels:
        app: order-consumer
    spec:
      containers:
        - name: consumer
          image: myorg/order-consumer:v1.4.2
          env:
            - name: KAFKA_BOOTSTRAP_SERVERS
              value: "kafka-cluster:9092"
            - name: CONSUMER_GROUP
              value: "order-processing-group"
            - name: TOPIC
              value: "orders"
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10

Ne oluyor burada?

  • replicas: 1 → Başlangıçta tek pod. KEDA bunu yönetecek.
  • Resource limitleri KEDA'nın scale kararlarına dolaylı etki eder (pod eviction riskini azaltır).
  • Health endpoint'leri KEDA değil, Kubernetes'in kendi liveness/readiness'i için.

2️⃣ Eski Yöntem — CPU Tabanlı HPA (Referans için)

Bunu silmeyeceğiz, yan yana koyup farkı görelim. Bu HPA KEDA ile çakışmaz ama aynı deployment'ı hedef almaz (label selector farklı olmalı). Sadece "eski nasıldı?" diye hatırlatma.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-consumer-hpa-cpu
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-consumer
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

Neden bu yeterli değildi? 🤔

  • Consumer I/O bound (network/disk), CPU %70'e neredeyse hiç çıkmıyor.
  • Lag 10.000'e çıkarken pod sayısı 2'de kalıyor → mesajlar saatte işlenmiyor.
  • Scale-down 5 dakika (300s) bekliyor → gece maliyet patlıyor.

3️⃣ Yeni Yöntem — KEDA ScaledObject (Kafka Trigger)

İşte sihirli dosya. Lag threshold'a göre ölçeklendiriyor.

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-consumer-scaledobject
  namespace: production
  labels:
    app: order-consumer
spec:
  scaleTargetRef:
    name: order-consumer
  pollingInterval: 15
  cooldownPeriod: 180
  minReplicaCount: 1
  maxReplicaCount: 20
  fallback:
    failureThreshold: 3
    replicas: 2
  advanced:
    restoreToOriginalReplicaCount: false
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 120
          policies:
            - type: Percent
              value: 20
              periodSeconds: 60
  triggers:
    - type: kafka
      metadata:
        bootstrapServers: kafka-cluster:9092
        consumerGroup: order-processing-group
        topic: orders
        lagThreshold: "1000"
        offsetResetPolicy: latest
        allowIdleConsumers: "true"

⚙️ Parametre Seçimleri — Neden Bu Değerler?

Parametre Değer Gerekçe
pollingInterval 15 saniye KEDA her 15 sn'de bir Kafka'dan lag çeker. Daha düşük (örn. 5 sn) → Kafka broker yükü artar. Daha yüksek → tepki gecikir. 15 sn production için dengeli.
cooldownPeriod 180 saniye (3 dk) Scale-in sonrası 3 dk bekle. Mesaj patlamaları genelde burst olur. 3 dk altına alırsan flapping (sürekli up/down) yaşarsın.
minReplicaCount 1 Gece/hafta sonu trafiği sıfıra yakın. 0 da olabilirdi (scale-to-zero) ama consumer group rebalance maliyeti ve cold-start latency'si için 1 tutuyoruz.
maxReplicaCount 20 Partition sayımız 20. Kafka consumer partition başına 1 pod kuralı. 20'in üzeri idle pod demek → kaynak israfı.
lagThreshold "1000" Her pod ~1000 mesaj/sn işliyorsa, lag 1000'in üzerindeyse yeni pod gerekir. Load testlerinizle bu sayıyı kalibre edin.
offsetResetPolicy: latest Yeni pod ayağa kalktığında eski mesajları tekrar işleme (at-least-once riski). Idempotent consumer'ınız varsa earliest de olabilir.
allowIdleConsumers: "true" Lag 0 olsa bile minReplicaCount kadar pod ayakta kalsın. Rebalance storm'u önler.

💡 İpucu: lagThreshold string olarak veriliyor (YAML'de tırnak içinde). KEDA bunu integer'a çevirir.


🧪 Test Senaryosu — Producer Hızı Artıyor, Ne Oluyor?

Sahne: Gündüz 10:00, siparişler patlıyor. Producer saniyede 5.000 mesaj basıyor. Consumer pod başına ~1.000 msg/sn işleyebiliyor.

Zaman Lag (toplam) Pod Sayısı Ne Oluyor?
10:00:00 0 1 Sessizlik.
10:00:15 5.000 1 → 6 KEDA 15 sn'de bir poll eder. Lag 1.000'in üzerine çıktı → 5 yeni pod tetikler (her pod 1.000 lag alır).
10:00:45 12.000 6 → 12 Producer hızı arttı. Lag yine threshold üzerinde → daha fazla pod.
10:02:00 800 12 → 6 Producer yavaşladı. Lag 1.000'in altına düştü. cooldownPeriod (180 sn) dolmadan scale-in başlamaz.
10:05:00 200 6 → 2 Lag stabil düşük. KEDA yavaş yavaş (scaleDown policy: %20/dk) pod'u indiriyor.
10:10:00 0 2 → 1 Gece moduna giriş. minReplicaCount: 1'e iner.

Loglardan ne görürsün? 📋

[INFO]  ScaledObject order-consumer-scaledobject: current metric value=5000, desired replicas=6
[INFO]  ScaledObject order-consumer-scaledobject: current metric value=12000, desired replicas=12
[INFO]  ScaledObject order-consumer-scaledobject: cooldown period active, skipping scale down
[INFO]  ScaledObject order-consumer-scaledobject: current metric value=800, desired replicas=6
[INFO]  ScaledObject order-consumer-scaledobject: current metric value=200, desired replicas=2

Grafik yerine metinsel özet:
Lag yukarı doğru keskin çıkış yapar → pod sayısı merdiven basamakları gibi artar. Lag eğimli düşüş yapar → pod sayısı yumuşak bir eğriyle iner (cooldown + scaleDown policy sayesinde). ❗️ Flapping yok. ✅ Maliyet kontrollü. ✅ SLA korunuyor.


🎁 Bonus — Fallback Mekanizması

fallback:
  failureThreshold: 3
  replicas: 2

Kafka broker'a 3 ardışık poll başarısız olursa (network blip, auth hatası vs.) KEDA panikle 2 replica'ya sıçrar. Uygulamanız down değil, yavaşlar — bu arada alert'iniz tetiklenir, siz müdahale edersiniz. 🛠


Özetle:

  • CPU HPA → "İşlemci ne kadar yoruldu?" sorar.
  • KEDA Kafka Trigger → "Kuyrukta ne kadar iş kaldı?" sorar.

İkinci soru business metric'e çok daha yakın. Bu yüzden order-consumer için KEDA doğru seçim. 🚀

Bir sonraki bölümde Prometheus Adapter ile custom metric (örn. http_request_duration_seconds_p99) nasıl scale edeceğimize bakalım. Hazır mısın? 😉


🔁 HPA vs KEDA: Ölçümler, Gözlemler ve Kazanımlar

Hazırsan geçiş sonrası farkları madde madde gözden geçirelim 👇

  • Ölçeklendirme tepki süresi

    • HPA: genellikle 1–2 dakika (metrics‑server polling aralığı)
    • KEDA: saniyeler içinde event‑driven tetiklenir
  • Resource utilization verimliliği 📉

    • HPA ile boşta bekleyen pod sayısı yük düşse bile yavaş azalırdı
    • KEDA’da event kuyruğu boşaldığı anda scale‑to‑zero (veya minReplica) çalışır → %30‑%40 daha az pod
  • Operasyonel basitlik 🛠

    • HPA + custom metrics → Prometheus Adapter kurulumu, RBAC, CRD yönetimi
    • KEDA: ScaledObject tek CRD, adapter gerektirmez
  • Event kaynakları çeşitliliği 🎯

    • HPA: CPU / Memory + custom metrics (Prometheus)
    • KEDA: Kafka, RabbitMQ, Azure Queue, NATS, Cron, HTTP, 50+ scaler hazır

Karşılaştırma Tablosu

Metrik HPA (Önce) KEDA (Sonra) Yorum
Tepki süresi ~90 sn – 2 dk < 10 sn Event‑driven sayesinde anında genişler/daralır
Boşta pod sayısı (ortalama) 12 7 %40 daha az pod ile aynı throughput
Kurulum karmaşıklığı Prometheus Adapter + Metrics Server Tek ScaledObject CRD Operasyonel yük azaldı
Desteklenen tetikleyiciler CPU, Memory, Custom (Prometheus) 50+ scaler (Kafka, RabbitMQ, Cron, …) Esneklik arttı

Benim cluster’ımda %40 daha az podla aynı işi yaptık 🚀
KEDA’ya geçtikten sonra kuyruk uzunluğuna göre anında scale‑out yapabiliyoruz, gece saatlerde ise scale‑to‑zero sayesinde maliyet düştü.

Küçük bir ipucu: Kendi ortamınızda kubectl get hpa ve kubectl get scaledobject çıktılarını yan yana koyun, saniyeler vs dakikalar farkını kendi gözlerinizle görün. Ölçün, karşılaştırın, karar verin ✅


❗️ Kaçınılması Gereken Tuzaflar: Sık Yapılan Hatalar ve İpuçları

  • TriggerAuthentication secret’ını yanlış namespace’e koymak 🎯

    • Nasıl fark ettim: ScaledObject “authenticationRef not found” hatası verdi ve pod’lar scale olmadı.
    • Nasıl çözdüm: Secret’ı ScaledObject ile aynı namespace’e taşıdım ve metadata.namespace alanını kontrol ettim.
    • İpucu: Secret ve ScaledObject her zaman aynı namespace’de olmalı; aksi takdirde KEDA auth bulamaz.
  • lagThreshold çok düşük ayarlayıp flapping’e neden olmak 🔁

    • Nasıl fark ettim: HPA sürekli 1‑2 saniyede bir scale‑up/scale‑down yapıyordu, loglarda “desiredReplicas changed” mesajları doluyordu.
    • Nasıl çözdüm: lagThreshold değerini gerçek trafik dalgalanmasına göre (ör. 30‑60 sn) yukarı çekip stabilizationWindow ekledim.
    • İpucu: Çok agresif eşikler “yo‑yo” efekti yaratır; biraz tolerans verin.
  • pollingInterval çok kısa yapıp KEDA operator’ünü yormak

    • Nasıl fark ettim: Operator pod’ların CPU’su %90’u buldu, metric‑adapter loglarında “polling timeout” uyarıları gördüm.
    • Nasıl çözdüm: pollingInterval’ı 15‑30 sn aralığına çıkardım; metrik toplama yükü aniden düştü.
    • İpucu: Varsayılan 30 sn çoğu iş için yeterlidir; özel durumlarda artırın, asla 5 sn altına indirmeyin.
  • ScaledObject’te fall‑back HPA metrikleri (cpu/memory) eklememek 🛡

    • Nasıl fark ettim: Kuyruk boşken bile beklenmedik bir trafik patlamasında pod’lar yetersiz kaldı, KEDA metric kaynaklı scale tetiklenmedi.
    • Nasıl çözdüm: Aşağıdaki fallback trigger’ı ekleyerek CPU‑based bir güvenlik ağı kurdum.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: my-worker
spec:
  scaleTargetRef:
    name: my-worker
  pollingInterval: 30
  fallback:
    failureThreshold: 3
    replicas: 2
  triggers:
    - type: kafka
      metadata:
        bootstrapServers: kafka:9092
        consumerGroup: my-group
        topic: my-topic
        lagThreshold: "100"
    - type: cpu
      metadata:
        type: Utilization
        value: "70"
  • İpucu: Her zaman en az bir cpu veya memory fallback trigger’ı ekleyin; bu, metrik kaynakları down olduğunda sistemin hayatta kalmasını sağlar.

  • Canary deployment stratejisi olmadan üretime basmak

    • Nasıl fark ettim: Yeni ScaledObject config’ünü canlıya pushladım, aniden tüm trafik yeni versiyona gitti ve hata oranı %40’a çıktı.
    • Nasıl çözdüm: Argo Rollouts / Flagger ile canary adımları ekledim: %10 trafik → manuel onay → %100.
    • İpucu: Küçük bir canary bile büyük felaketleri önler; “production‑first” değil, “canary‑first” düşünün.

🎯 Bonus Tavsiye: KEDA ile İleri Seviye Stratejiler ve Son Söz

Bende de bu makaleyi okuduktan sonra kafam biraz karıştı, nasıl paylaşayım dedim. Gelip KEDA'yı en sık kullandığımız temel scaler'larla kurduğumu düşünelim. İşte o an, daha da esnek bir kontrol sahnesi istediğimiz zaman ortaya çıkar. Hadi ileri seviye stratejileri anlatalım ve arkasından motivasyonla bir kapanış yapalım! 🚀


🎯 KEDA'yı bir üst seviyeye taşıyan ipuçları

  • Çoklu tetikleyici (AND/OR mantığı)

    • Tek bir kuralla sınırlı kalmazsın. Birden fazla tetikleyiciyi bir araya getirerek hem CPU % > 70 hem de istemci sayısı > 100 olduğu zamanı yakalayabilirsin.
    • Mantığıda AND ya da OR olarak tanımlarsın. Böylece ölçeklendirme, yalnızca performansa göre değil, iş mantığına göre de gerçekleşir. 🎛️
  • ScaledJob → Batch işleri için

    • Birden çok iş parçasını tek bir iş görevinde çalıştırmak, tek tek değil.
    • KEDA'yı kullanarak ScaledJob tanımlarsın; işe başlamadan önce dış tetikleyiciyi (örneğin bir kuyruk uzunluğu) bekleyip sonra çalıştırman olur.
    • Tam otomatik bir düşük gecikmeli batch altyapısı oluşturun, tek bir ScaledJob kaydıyla. 📦
  • External Scaler → Kendi ölçeklendirme mantığını yazın

    • KEDA, sadece CPU, RAM, istemci sayısı ile sınırlı değildir. Size dış bir hizmetten (örneğin bir veritabanından, bir emredilecek değerden, bir özel metriğe) göre karar verme hakkını verir.
    • OADA (Open Application) formatını kullanarak kendi scaler'ınızı yazın, istediğiniz ölçümde karar verin; bu da size özel iş mantığına özgü ölçeklendirme sağlar. 🛠️
  • Metric Server entegrasyonu

    • Başka bir Kubernetes ölçeklendirme bileşeni olan Metric Server, KEDA'ya doğru metriği sunar. Doğru yüklemeyi ve ölçümlemeyi yaptığınızdan emin olun.
    • Bir kez kurulduktan sonra, yalnızca ölçeklendirme tanımlayıcılarını güncellemek yeterli olur. 📈

Özetle, ölçeklendirme sadece CPU ile sınırlı değildir

Artık ölçeklendirme içeriğinize, iş mantığınıza, harici tetikleyicilere göre gerçekleşiyor. KEDA ile basit bir "cpu > 80" kuralından, kapsamlı bir "cpu > 70 VE istek sayısı > 100 VEYA bir kuyruk uzunluğu > 500" kuralına geçebilirsiniz.


Sıradaki adım: Deneyin, öğrenin, paylaşın! 👩‍💻

  • Deneyin – Yukarıdaki ipuçlarıyla bir ScaledJob tanımlayın, bir ScaledObject ayarlayın ya da kendi External Scaler'ınızı yazmaya başlayın.
  • Sorunlar – Slack veya Discord topluluklarımıza sorun, her zaman birisini bekliyoruz! 🎤
  • Deneyimlerinizi – Başardıklarınızı, kafanızı kurcalayan şeyleri, hataları anlatın; hepimiz gelişelim.

Bir sonraki yazıda External Scaler yazmaya çalışacağım, görüşmek üzere! 🎉


Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.

Burak Sağlık

Burak Saglik

©2024 Desing and Developed by @Burak Sağlık

All rights reserved