Ana sayfa → Teknik
Pod Scale Etme Mantığı
Kampanya başladı, trafik üç katına çıktı. Otomatik ölçekleyici devreye girdi, pod sayısı 4’ten 24’e çıktı. Ve yanıt süresi daha da kötüleşti. Sonra veritabanı yeni bağlantı kabul etmeyi bıraktı.
- Ölçekleme darboğazı taşır, kaldırmaz. Önce darboğazın nerede olduğunu bul.
- CPU çoğu web uygulaması için yanlış ölçü. Bekleyen uygulama CPU harcamaz ama yavaştır.
- Pod sayısı arttıkça veritabanı bağlantısı çarpılarak artar. Aşağıyı korumadan yukarıyı büyütme.
- Yanlış
request= yanlış ölçekleme. Yüzde hesabı limite göre değil request’e göre yapılıyor.
Otomatik ölçekleyici aslında ne yapıyor?
Karmaşık görünüyor ama tek bir orantı hesabı:
istenen_pod = mevcut_pod × (mevcut_deger / hedef_deger)
# ornek: 4 pod, ortalama CPU %90, hedef %60
# istenen = 4 × (90 / 60) = 6 pod
Bu kadar. Bütün mesele “mevcut_deger” olarak neyi seçtiğin. Ve çoğu ekip burada CPU seçip konuyu kapatıyor.
CPU neden çoğu zaman yanlış ölçü?
Tipik bir web isteğinin ömrüne bak: 3 ms JSON ayrıştırma, 80 ms veritabanı bekleme, 2 ms serileştirme. Uygulama zamanının %95’ini beklemekle geçiriyor ve beklemek CPU harcamıyor.
Yani sistem dizlerinin üstünde sürünürken CPU %25 görünebiliyor. Ölçekleyici de “her şey yolunda” deyip hiçbir şey yapmıyor.
| Uygulama tipi | Doğru ölçü | Neden |
|---|---|---|
| Görsel işleme, şifreleme, rapor üretimi | CPU | Gerçekten işlemci sınırlı |
| Tipik REST API | Eşzamanlı istek / saniyedeki istek | Zamanın çoğu beklemede geçiyor |
| Kuyruk işçisi | Kuyruk uzunluğu | Biriken iş, gecikmenin öncü göstergesi |
| Uzun bağlantılar (WebSocket, SSE) | Açık bağlantı sayısı | Bellek ve soket sınırı belirleyici |
# KEDA ile: her 100 bekleyen mesaj icin 1 worker
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
spec:
scaleTargetRef:
name: bildirim-worker
minReplicaCount: 2
maxReplicaCount: 40
triggers:
- type: rabbitmq
metadata:
queueName: bildirimler
queueLength: "100"
Bu, işçiler için CPU’dan kat kat iyi: kuyruk büyümeye başladığı anda tepki veriyor, işçiler bekleme yüzünden yavaşlasa bile doğru çalışıyor.
request ve limit: en çok yanlış kurulan iki alan
Bu ikisi karıştırılınca ölçekleme baştan yanlış hesaplanıyor.
Garanti edilen pay. Zamanlayıcı pod’u yerleştirirken buna bakar. Ve kritik olan şu: ölçekleyicinin yüzde hesabı buna göre yapılır.
Aşılamaz tavan. CPU’da aşarsan yavaşlatılırsın, bellekte aşarsan öldürülürsün. İkisi çok farklı hisler.
resources:
requests:
cpu: 100m # ← gercekte 400m kullaniyor
limits:
cpu: 1000m
# HPA hedefi %70 olsun. Hesap request'e gore:
# 400m / 100m = %400 kullanim
# istenen = 4 × (400 / 70) ≈ 23 pod
#
# Uygulama aslinda rahat calisiyor. Olcekleyici panige kapildi.
Tersi de oluyor: request’i çok yüksek verirsen (örneğin 2000m) kullanım hep düşük görünür, ölçekleyici hiç açılmaz ve sistem yük altında ezilir.
Pratik kural: request, normal yükteki gerçek kullanımın biraz üstü olmalı. Tahminle değil, ölçerek. Bir hafta boyunca gerçek kullanımın %90’lık dilimine bak ve onu yaz.
CPU limiti meselesi
CPU limitine takılan bir süreç öldürülmüyor, kısıtlanıyor (throttling). Bu, teşhisi en zor gecikme kaynaklarından biri: hata yok, log temiz, CPU grafiği bile limitin altında görünüyor — ama istekler takılıyor.
Sebep şu: kısıtlama çok kısa aralıklarla uygulanıyor. Ortalamada limitin altında kalsan bile, kısa ani yükselmelerde milisaniyeler boyunca durduruluyorsun. Bunu görmenin tek yolu doğrudan kısıtlama metriğine bakmak:
container_cpu_cfs_throttled_seconds_total
# Bu deger surekli artiyorsa CPU limitin dusuk demektir.
# Cok gorulen cozum: gecikmeye duyarli servislerde
# CPU limitini hic koymamak (request koymak yeterli).
Asıl tuzak: aşağıdaki darboğaz
Baştaki hikâyeye dönelim. Pod 4’ten 24’e çıktı. Her pod veritabanına 20 bağlantılık bir havuz açıyordu.
4 pod × 20 baglanti = 80 ✓ (limit 200)
24 pod × 20 baglanti = 480 ✗ (limit 200)
Sonuc: veritabani yeni baglantiyi reddediyor.
Uygulama "connection timeout" firlatiyor.
Saglik kontrolu basarisiz oluyor, pod yeniden baslatiliyor.
Yeniden baslayan pod tekrar 20 baglanti istiyor...
Bu bir kısır döngü ve ölçekleme onu hızlandırıyor. Sistem, hiç ölçeklenmemiş olsaydı yavaş ama ayakta kalacaktı.
Üç savunma:
-
Havuz boyutunu pod sayısına göre hesapla.
pod_başına_havuz × max_pod ≤ veritabanı_limiti × 0,8. 24 pod hedefliyorsan pod başına havuz 6 olmalı, 20 değil. - Bağlantı havuzlayıcı kullan. PgBouncer gibi bir katman, yüzlerce uygulama bağlantısını onlarca gerçek bağlantıya indirir. Bu, ölçeklemeyi mümkün kılan tek yapısal çözüm.
-
maxReplicas’i keyfi koyma. O sayı, aşağıdaki en zayıf halkanın kaldırabileceğinden türetilmeli. Ölçekleyicinin üst sınırı bir güvenlik kemeri; “100 yazalım rahat olsun” demek kemeri çıkarmak.
Yavaş açılan uygulama problemi
Bir Java servisinde şunu yaşadık: yeni pod ayağa kalkıyor, hazır olduğunu bildiriyor, trafik alıyor — ama ilk 30 saniye çok yavaş cevap veriyor (JIT ısınması, önbellek doldurma, bağlantı kurma).
Sonuç: yeni pod ortalama gecikmeyi yükseltiyor, ölçekleyici bunu “yetersiz kapasite” sanıp bir pod daha açıyor, o da yavaş başlıyor... Buna salınım (flapping) deniyor ve grafikte testere dişi gibi görünüyor.
# 1) Isinana kadar trafik alma
readinessProbe:
httpGet: { path: /hazir, port: 8080 }
initialDelaySeconds: 20
periodSeconds: 5
# 2) Yavas acilisi olum sanma (yeniden baslatma dongusunu keser)
startupProbe:
httpGet: { path: /saglik, port: 8080 }
failureThreshold: 30
periodSeconds: 5
# 3) Olcekleyiciye sogumasi icin zaman ver
behavior:
scaleUp:
stabilizationWindowSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
Üçüncüsü özellikle önemli: küçülme yükselmeden yavaş olmalı. Trafik dalgalanıyorsa hızlı küçülen bir sistem, iki dakika sonra aynı pod’ları yeniden açar ve soğuk başlangıç maliyetini tekrar tekrar öder.
Küçülürken iş kaybetmek
Ölçekleme yazılarında en az konuşulan kısım bu. Pod kapatılırken üzerinde yarım kalmış işler olabilir.
- Kapanış sinyali gelince yeni iş almayı bırak, elindekini bitir.
terminationGracePeriodSeconds, en uzun işinden büyük olsun. Varsayılan 30 saniye; 5 dakika süren bir işin varsa yetmez.- Uzun süren işler için: pod kapanırken işi kuyruğa geri koy ki başka bir worker devralsın.
- Web servisinde: hazırlık kontrolünü önce başarısız yap, birkaç saniye bekle, sonra kapan — yük dengeleyicinin listeden çıkarması zaman alıyor.
Ne zaman dikey büyümek daha doğru?
Her şeyin cevabı yatay değil. Pod eklemek şu durumlarda işe yaramaz:
- Tek bir isteğin kendisi yavaşsa. 8 saniyelik bir sorgu, 40 pod ile de 8 saniye sürer.
- Uygulama bellekte büyük bir önbellek tutuyorsa. Her pod aynı veriyi ayrı ayrı tutar; bellek israfı katlanır.
- Lisans veya bağlantı başına maliyet varsa. Kopya sayısı doğrudan faturaya yazılır.
Bu durumlarda önce sorguyu, önbelleği veya algoritmayı düzeltmek gerekiyor. Ölçekleme bir performans çözümü değil, bir kapasite çözümü.
Kontrol listesi
- Darboğazın nerede olduğunu ölçtüm mü? (Uygulama mı, veritabanı mı, dış servis mi?)
- Ölçüm metriği doğru mu? (CPU mu olmalı, kuyruk mu, eşzamanlı istek mi?)
requestdeğerleri gerçek kullanıma göre mi ayarlandı?- CPU kısıtlama (throttling) metriğine baktım mı?
maxReplicas × pod_başına_bağlantıveritabanı limitinin altında mı?- Hazırlık kontrolü uygulama gerçekten hazır olduğunda mı geçiyor?
- Küçülme penceresi büyümeden uzun mu?
- Pod kapanırken yarım kalan iş ne oluyor?
- Ölçekleme testi yaptım mı, yoksa canlıda mı öğreneceğim?
Sonuç
Otomatik ölçekleme, doğru kurulduğunda gece uykusu demek; yanlış kurulduğunda çöküşü hızlandıran bir mekanizma. Aradaki farkı belirleyen şey pod sayısı değil, hangi sayıya baktığın ve aşağıdakini koruyup korumadığın.
Baştaki olayda çözüm pod azaltmak oldu — 24’ten 8’e indirdik, araya bağlantı havuzlayıcı koyduk, ölçüyü CPU’dan eşzamanlı isteğe çevirdik. Sistem hem daha az kaynakla hem de daha hızlı çalıştı.