<

Kubernetes HPA Bizi Nasıl Batırıyordu: Yanlış Metrikle Ölçeklenmek

Mart'ın ilk cuması, öğlen 12.30. Yemek tarafında günün en tatlı piki başlamış, sipariş oluşturma isteği saniyede 2 bin 400'e tırmanmıştı. Sonra panolar kızarmaya başladı: p99 gecikme 300 milisaniyeden 4 saniyeye, timeout oranı yüzde 6'ya. Nöbetçi mühendisin ilk mesajı içimi rahatlattı: "HPA çalışıyor, pod sayısı 12'den 38'e çıktı." İkinci mesajı ise bu yazının sebebi oldu: "Pod'lar arttıkça durum kötüleşiyor." Otomatik ölçekleme, yangına körükle gidiyordu. O gün öğrendik ki HPA'nın sorusu "kaç pod lazım?" değildir; HPA yalnızca kendisine verdiğiniz metriği kovalar ve yanlış metrik verdiyseniz, onu kovalayarak sizi uçuruma taşır.

CPU Yüzde 45'te, Servis Ölüyor: Nasıl?

Sipariş servisimizin HPA'sı klasik ayardaydı: hedef CPU kullanımı yüzde 65. Kriz anında ortalama CPU yüzde 45 görünüyordu — yani HPA'ya göre her şey yolundaydı, ölçeklenme sadece trafik artışını izliyordu. Ama servis boğuluyordu. İlk yanlış hipotezimiz veritabanıydı: "PostgreSQL bağlantı havuzu doldu, pod'lar bekliyor." Havuz metrikleri doluydu gerçekten, ama sebep değil sonuçtu. İkinci hipotez Kafka'ydı; lag artıyordu ama o da sonuçtu. Gerçek ipucu, tek bir pod'un derinine indiğimizde geldi: container_cpu_cfs_throttled_periods_total. Pod'larımız, ortalama CPU yüzde 45'teyken bile CFS periyotlarının yüzde 70'inde throttle ediliyordu.

Mekanizma şu: CPU limit'i, Linux CFS'te 100 milisaniyelik pencerelerle uygulanır. Limitiniz 1 core ise her 100 ms'lik pencerede 100 ms CPU hakkınız var. Java servisimiz istek başına kısa ama yoğun CPU patlamaları yapıyordu (JSON işleme, fiyat hesabı) ve GC ile JIT de üstüne binince pencere başı hak birkaç milisaniyede tükeniyor, thread'ler pencerenin geri kalanında donduruluyordu. Ortalama CPU düşük görünür, çünkü ortalama, donmuş geçen milisaniyeleri de böler. Yani HPA'nın baktığı metrik, tam da sorunun görünmesini engelleyen metrikti. Üstelik her yeni pod açılışında JIT ısınması ve sınıf yüklemesi ekstra CPU patlaması yaratıyor, yeni pod'lar ilk iki dakika boyunca eskilerden beter throttle yiyor, readiness'ı geçtikleri halde istekleri sürüncemede bırakıyordu. Pod sayısı arttıkça toplam ısınma yükü artıyor, load balancer yavaş pod'lara istek dağıtmaya devam ediyordu. Ölçeklenme, kelimenin tam anlamıyla yangını büyütüyordu.

Yanlış Metrikle Doğru Ölçek Olmaz

Acil müdahale iki hamleydi: CPU limitlerini geçici olarak kaldırdık (request'ler kaldı) ve minimum replica'yı pikten önce yükselttik. Throttling anında düştü, p99 90 saniye içinde 600 milisaniyeye indi. Ama "limitleri kaldırdık, bitti" demek, bu yazıyı yazmaya değmezdi; asıl iş, ölçekleme felsefesini baştan kurmaktı.

Birinci karar: HPA'nın kovalayacağı metrik, müşterinin hissettiği şeye en yakın metrik olmalı. CPU, bir vekil metriktir ve bizim iş yükümüzde kötü bir vekildi. Prometheus Adapter'ı devreye alıp HPA'yı custom metric'e bağladık: pod başına saniyedeki istek sayısı (RPS) ve onun yanına ikinci sinyal olarak istek kuyruğu bekleme süresi. Yük testleriyle her pod'un sağlıklı taşıyabildiği RPS'i ölçtük — bizim konfigürasyonda pod başına 85 istek/sn'de p99 hâlâ 250 ms altındaydı, 110'da kırılıyordu — ve HPA hedefini 70 istek/sn'ye koyduk: kırılma noktasının yüzde 35 altı, ısınma payı dahil. Kafka tüketen servislerde ise ölçek metriği consumer lag oldu; orada RPS'in anlamı yok, birikmiş iş var. Bugün 14 kritik servisimizin yalnızca üçü hâlâ CPU ile ölçekleniyor — üçü de gerçekten CPU'ya bağlı işler yapan, gecikmeye duyarsız batch servisleri.

İkinci karar limit politikasıydı ve bu, ekipte en çok tartışılan konu oldu. Gecikmeye duyarlı servislerde CPU limit'i koymuyoruz; request'i gerçekçi ölçüyoruz (yük testinden, p95 kullanımın üstüne pay koyarak) ve node doluluk riskini limit'le değil, doğru request + ayrı node pool + PodDisruptionBudget ile yönetiyoruz. "Limitsiz pod komşularını ezer" itirazı meşru; cevabımız, ezmenin ancak request'ler yalan söylüyorsa mümkün olduğu. Request dürüstse, scheduler zaten aşırı yerleştirme yapmaz. Java tarafında ayrıca açılış sancısını küçülttük: AppCDS ve katmanlı JIT ayarlarıyla ısınma süresi 110 saniyeden 35 saniyeye indi, readiness probe'u "HTTP 200 dönüyor"dan "ısınma turu tamamlandı"ya çevirdik — pod, ilk gerçek isteği yemeden önce kendi kendine 200 sentetik istek işliyor.

Custom metric hattının kendisinin de bir arıza noktası olduğunu unutmadık. HPA'nın beslendiği Prometheus Adapter düşerse ölçekleyici kör kalır; Kubernetes bu durumda mevcut replica sayısını korur, bu da pik öncesi düşük sayıda yakalanırsanız felaket demektir. İki önlem aldık: adapter ve Prometheus, kritik altyapı sınıfında ayrı bir node pool'da, kendi alarmlarıyla koşuyor; ve her kritik servisin minimum replica'sı, "metrik hattı ölüyken öğlen pikini idare edecek" seviyeden aşağı hiç inmiyor. Otomasyona güveniyoruz ama güvenin de bir yedeği olmalı.

Ölçeklenme Hızı da Bir Metriktir

Üçüncü ders, kimsenin ilk gün düşünmediği yer: HPA'nın zamanlaması. Varsayılan davranışla HPA, metrik eşiği aşıldıktan sonra ölçeklenmeye karar verene ve pod gerçekten trafik alana kadar bizde ortalama 3 dakika geçiyordu — metrik toplama aralığı, kararlılık penceresi, image çekme, ısınma. Öğlen pikimiz ise 5 dakikada dikleşiyor. Yani en iyi ihtimalle pikin ortasında yetişiyorduk. Çözüm üç parça: behavior alanıyla scale-up'ı agresifleştirdik (15 saniyelik pencerede yüzde 100 artışa izin), scale-down'ı yavaşlattık (5 dakikalık stabilizasyon — flapping'i kesti), ve öngörülebilir pikler için işin en dürüst aracını ekledik: zamanlanmış ölçekleme. Öğlen ve akşam pikinden 10 dakika önce minimum replica otomatik yükseliyor. Reaktif sistemden tahmin beklemek yerine, bildiğimiz geleceği ona söylüyoruz. Havalı değil; çalışıyor.

Sonuçların özeti: o cuma benzeri trafik profilinde (sonraki ayın kampanya cuması, saniyede 2 bin 900 istek) p99 gecikme 310 milisaniyede sabit kaldı, timeout oranı binde 2'nin altında, pod sayısı pik boyunca 44. Throttling oranı kritik servislerde artık sıfıra yapışık bir çizgi ve o çizginin üstünde alarm var: throttle oranı yüzde 5'i geçen her deployment, otomatik olarak şüpheli ilan ediliyor. Aylık bulut faturasında da yan etki gördük: doğru metrikle ölçeklenince gereksiz "korku replica'ları" azaldı, sipariş başına altyapı maliyeti yüzde 17 düştü.

Otomasyonun Aynası

HPA olayından çıkardığım genel ders şu: otomasyon, ona verdiğiniz dünya modelinin aynasıdır. "CPU yüzde 65'i geçince pod ekle" cümlesi, aslında "bizim darboğazımız CPU'dur ve CPU ortalaması sağlığı temsil eder" iddiasıdır — ve bu iddia bizim için yanlıştı. Sisteminiz ölçeklendikçe kötüleşiyorsa, ölçekleyicinin baktığı metriğin müşterinizin hissettiği şeyle ilişkisini sorgulayın; cevap çoğu zaman throttling sayaçlarında, kuyruk sürelerinde ya da lag'dedir. Kendi HPA kurulumunuzdan şüpheleniyorsanız — özellikle "pod sayımız artıyor ama gecikme düşmüyor" cümlesi tanıdık geliyorsa — buradan yazın; yük testi şablonumuzu ve HPA behavior ayarlarımızı paylaşırım, iki saatlik bir bakışla çok şey netleşiyor.

O cuma gününün savaş odası kaydında en sevdiğim an, kıdemli SRE'mizin sakin cümlesi: "Sistem bize itaat ediyor; sorun, ne emrettiğimizi bilmememiz." Otoscaling'in bütün teorisi o tek cümlenin dipnotudur.

📅 Yayınlanma:  ·  Yakup Zengin