Kubernetes v1.36 Controller Staleness: Bayat Cache Sorunu Bitti mi?
Bakın şimdi, controller yazan ya da Kubernetes operatörleriyle haşır neşir olan herkesin başına gelmiştir bu: Bir gün gayet düzgün çalışan controller, bir anda saçma kararlar almaya başlıyor (şaşırtıcı ama gerçek). Pod’u silmesi gerekirken silmiyor, ölçeklemesi gerekirken uyuyor, bazen de tam tersi oluyor — olmaması gereken aksiyonu alıyor. Loglara bakıyorsun, kod doğru. Cluster’a bakıyorsun, durum normal. Peki ne öldü?
Cevap çoğu zaman tek kelime: staleness. Yanı bayat cache.
Kubernetes v1.36 ile birlikte tam da bu derdi hedefleyen bir dizi iyileştirme geldi. Hem client-go tarafında hem de kube-controller-manager içindeki yoğun trafikli controller’larda. Ben burada hem teknik tarafı hem de kurumsal müşterilerimde gördüğüm pratik etkileri anlatmaya çalışacağım; çünkü kağıt üstünde küçük duran şeyler, prod ortamda bazen baya fark yaratıyor.
Staleness Nedir, Neden Önemli?
Kısaca anlatayım. Kubernetes controller’ları, API server’ı her seferinde yoklamak yerine kendi içinde bir local cache tutuyor. Bu cache de watch mekanizmasıyla API server’dan gelen olaylarla güncelleniyor (kendi tecrübem). Yanı işin özü şu: “Bu pod’un hali ne?” diye tekrar tekrar API’ye gitmek yerine, eldeki kopyaya bakıp hızlıca karar veriyorsun.
Şimdi gelelim işin can alıcı noktasına.
Kağıt üstünde fena değil. Pratikte işe biraz naz yapıyor.
İnanın, Çünkü cache her an gerçeği göstermiyor. Controller restart olunca cache sıfırdan kuruluyor, o arada elindeki veri doğal olarak eksik kalıyor; API server kısa süreliğine erişilemezse, watch koparsa ya da event’ler ters sırayla gelirse, cache bayatlıyor ve controller da o bayat bilgiyle karar verince işler hemen karışıyor.
İnanın, Geçen yıl bir e-ticaret müşterisinde yaşadığımız olayı hatırlıyorum. HPA (Horizontal Pod Autoscaler) controller’ı restart sonrası yaklaşık 12 saniye boyunca eski metriklerle çalıştı; tam o sırada Black Friday trafiği bastırmıştı, ölçekleme gecikti. Response time’lar bir anda zıpladı. Sonra baktık, controller cache’i daha tam dolmadan reconcile döngüsü başlamış; sebep stale state, sonuç işe yaklaşık 40 dakikalık degraded service.
“Production’da staleness sorununu fark ettiğinizde genellikle iş işten geçmiştir. Çünkü controller, sizin haberiniz olmadan yanlış kararı çoktan almıştır.”
v1.36 Ne Getiriyor?
İki tarafta da iş var. Birincisi client-go tarafında, ikincisi de bu kütüphaneyi kullanan kube-controller-manager içindeki yoğun controller’larda; yanı görünüşte küçük duran. Pratikte epey can sıkan bir akış problemi biraz toparlanıyor.
Atomic FIFO Processing
Yeni feature gate’in adı AtomicFIFO (şaşırtıcı ama gerçek). İsim biraz kuru dürüyor, evet. Ama yaptığı şey boş değil; mevcut FIFO queue düzeninin üstüne, batch halinde gelen olayları tek parça gibi işleme yeteneği ekliyor, böylece ilk baştaki karmaşa daha başlamadan biraz dizginleniyor.
Şöyle düşünün: Bir informer ilk ayağa kalktığında API server’a list çağrısı yapıyor ve eldeki objelerin bir snapshot’ını çekiyor. Hani ne farkı var diyorsunuz, değil mi? Bu snapshot bazen 5000 pod bile olabiliyor; eski düzende bu 5000 kayıt queue’ya tek tek ekleniyordu (arada başka bir event girerse sıra kayabiliyordu), sonra cache kısa süreliğine yamuk bir hâle gelebiliyordu — bence çok yerinde bir karar —
Bunu biraz açayım.
Atomic FIFO ile bu batch artık tek bir atomik operasyon olarak işleniyor. Yanı ya hep ya hiç. İlk dolum bitince cache tutarlı bir state’te kalıyor, ardından gelen event’ler de kendi sırasıyla ilerliyor; açık konuşayım, burada asıl kazanç “daha hızlı” olmaktan çok “daha az sürpriz” olması.
Şahsen, Evet.
Resource Version Introspection
Peki bunun öbür tarafı ne? Artık client-go kullanan kodlar, cache’in en son hangi resourceVersion‘a kadar güncellendiğini sorgulayabiliyor. Küçük bir detay gibi dürüyor, hatta ilk bakışta “tamam da ne olacak?” dedirtiyor; ama controller yazıyorsanız bunun değeri baya hissediliyor.
Doğrusu, Çünkü artık şunu diyebiliyorsunuz: “Cache’im X resourceVersion’dan eskiyse bekle.” Ya da tam tersi, “Bu sefer API’den taze veri çekeyim, cache’i pas geçeyim.” Böyle ince ayarları controller içine koyunca üretimde işler daha sakın akıyor (tabi her senaryoda değil. Çoğu zaman işe yarıyor).
Vallahi, Neyse uzatmayalım, konuya döneyim: Bu kontrol mekanizması özellikle yoğun çalışan controller’larda karar anını netleştiriyor. Gereksiz riskleri azaltıyor. Kısacası, sız ne dersiniz?
kube-controller-manager Tarafı
İşin en ağır kısmını taşıyan controller’lar, yanı özellikle endpoint controller, ReplicaSet controller ve Deployment controller, bu yeni client-go davranışına uyacak şekilde elden geçirildi. Her birinin ayrı derdi var tabiî; (ciddiyim). Genel resim aynı yere çıkıyor: önce cache taze mi diye bakılıyor, sonra gerekirse bekleniyor ya da doğrudan API’ye gidiliyor. Kulağa basit geliyor, ama sahada o kadar düz olmuyor.
- Reconcile başlamadan önce cache freshness kontrolü
- Stale işe ya bekleme ya da direkt API’den okuma
- Metric olarak staleness süresinin ölçülmesi (observability tarafı)
Üçüncü maddeyi hafife geçmeyin. Çünkü artık Prometheus tarafında — itiraz edebilirsiniz tabi — controller’larınızın ne kadar süre stale çalıştığını görebiliyorsunuz, bu baya iş görüyor; eskiden böyle bir görünürlük yoktu ve biz de mecburen loglara bakıp, üstüne biraz tahmin ekleyip, “acaba burada cache mi bayatladı” diye kendi kendimize uğraşıyorduk. Evet, biraz da öyleydi. Teams Agent Kurulumu Artık Tek Komutla Tamam yazımızda bu konuya da değinmiştik.
Durun, bir saniye.
Yeni Metrikler ve Observability
Açık konuşayım, observability tarafı bence v1.36’nın en sevdiğim kısmı. Hani şu “bir şeyler ters gidiyor ama nerede” hissi var ya, işte önü baya azaltıyor. Yeni eklenen metrikler arasında şunlar var:
| Metrik | Ne Ölçer? |
|---|---|
controller_cache_staleness_seconds |
Cache’in API server’a göre ne kadar geride olduğu |
controller_reconcile_skipped_total |
Stale cache nedeniyle ertelenen reconcile sayısı |
informer_initial_sync_duration_seconds |
İlk cache dolumunun ne kadar sürdüğü |
watch_events_out_of_order_total |
Sırasız gelen watch event sayısı |
Bu metrikleri Grafana’da bir dashboard’a koyduğunuzda, controller sağlığı hakkında net bir tablo çıkıyor karşınıza. Kısa kısa bakınca pek bir şey söylemiyor gibi dürüyor, ama topluca izleyince taşlar yerine oturuyor. Geçen ay test cluster’ımda cache_staleness_seconds‘un bazı saatlerde 8 saniyeye fırladığını gördüm; sebep network jitter çıktı, yanı ilk bakışta aklıma gelmeyecek bir mevzu. Daha önce bunu hiç fark edemezdim.
Türkiye’deki Kurumsal Yapılar İçin Anlamı
Şimdi biraz Türkiye tarafına bakalım. Kurumsal müşterilerde gördüğüm tablo şu: son — en azından ben öyle düşünüyorum — 2-3 yılda Kubernetes kullanımı baya hızlandı, özellikle finans, telco ve e-ticaret ekipleri AKS ya da on-prem K8s tarafına ciddi şekilde kaydı. Ama işin garip kısmı şu, operator/controller yazan ekip sayısı hâlâ az. Çoğu firma dışarıdan gelen controller’larla yürüyor,. Kendileri yazmadıkları vendor controller’lar ile idare ediyorlar. Bu konuyla ilgili Gateway API v1.5: Altı Özellik Stable Oldu, Ne Değişiyor? yazımıza da göz atmanızı tavsiye ederim.
Peki bu ne demek? Eğer sız controller author değilseniz, staleness sizi dolaylı değil direkt vuruyor. Çünkü Istio, ArgoCD, Flux, cert-manager gibi araçların hepsi controller mantığıyla çalışıyor ve arkada sürekli reconcile ediyor; v1.36 cluster’a geçtiğinizde bunlar yeni client-go davranışlarından ancak kendi yeni sürümlerine geçince faydalanabiliyor (yanı olay biraz “cluster yükseldi, her şey otomatik düzeldi” kadar basit değil).
Bence asıl kırılma noktası burada çıkıyor. ArgoCD gibi GitOps araçları drift detection yapıyor; yanı Git’teki state ile cluster state’ını karşılaştırıyor (inanın bana). Eğer bu karşılaştırma stale cache üzerinden dönüyorsa yanlış drift alarmları görmek hiç şaşırtıcı olmaz. Hatta bazen ekipler saatlerce boş yere uğraşıyor, sonra anlaşılıyor ki sorun uygulamada değilmiş; cache eski kalmış sadece. v1.36 ile bu false positive sayısının düşmesini bekliyorum, en azından kağıt üstünde hikâye o yönde gidiyor.
Pratik Uygulama: Kendi Controller’ınızda Nasıl Faydalanırsınız?
Eğer kendi controller’ınızı yazıyorsanız (operatör pattern, custom CRD vs.), client-go v1.36 ile gelen yeni API’leri kullanabiliyorsunuz. Basit gibi duran bir iş, ama bazen tam da burada iş açılıyor; mesela cache tarafını yoklamadan karar verince, reconcile akışı gereksiz yere eski bilgiyle ilerleyebiliyor. Sonra insan “neden böyle öldü?” diye ekrana bakıp kalıyor.
Evet, doğru duydunuz. Daha fazla bilgi için Azure Developer CLI Nisan 2026: Çok Dilli Hook Devri Başladı yazımıza bakabilirsiniz.
Şöyle bir örnek var:
// Cache freshness kontrolü
informer := factory.Core().V1().Pods().Informer()
// Yeni eklenen metod
lastSyncedRV, err := informer.GetController().LastSyncedResourceVersion()
if err != nil {
return fmt.Errorf("cache durumu alınamadı: %w", err)
}
// Cache çok bayat ise reconcile'ı ertele
if isStale(lastSyncedRV, threshold) {
klog.Warningf("Cache stale, reconcile erteleniyor")
return ctrl.Result{RequeueAfter: 2 * time.Second}, nil
}
// Normal reconcile akışı
return reconcile(ctx, obj)
Burada olay aslında şu: cache’in ne kadar taze olduğunu anlayıp ona göre davranıyorsunuz. Güzel tarafı bu, ama dur bir saniye — her reconcile çağrısında bunu yaparsanız, sistemde boş yere ekstra trafik oluşuyor ve faydadan çok gürültü çıkabiliyor. Açık konuşayım, ben bunu can alıcı kararlar öncesinde kullanırım; ölçekleme, silme, kaynak tahsisi gibi yerlerde iş görüyor, sıradan status update işlerinde işe pek gerek duymuyor. Daha fazla bilgi için Service Bus Batch İşlemede Mesaj Bazlı Settlement Devrimi yazımıza bakabilirsiniz.
Bu kadar mı? Bu konuyla ilgili Cosmos DB Azure RBAC Entegrasyonu: İki Dünya Birleşiyor yazımıza da göz atmanızı tavsiye ederim.
Küçük Ekip mi, Büyük Kurumsal Yapı mı?
Küçük bir detay: Küçük bir startup ekibiyseniz ve sadece standart Kubernetes objeleriyle yaşıyorsanız, açıkçası bu özelliğe kafa yormanız şart değil. Cluster’ınızı v1.36’ya yükseltirsiniz, özellik. Arka planda geliyor, sız de yolunuza devam edersiniz; hani bazen iyi şeyler sessizce gelir ya, burada da durum biraz öyle.
Ama büyük kurumsal yapıdaysanız — özellikle 50+ node’lu cluster’larınız varsa, custom controller yazıyorsanız, multi-tenant ortam işletiyorsanız — o zaman iş değişiyor. Yeni metrikleri Prometheus’a eklemek, bir Grafana dashboard’u kurmak. Alert tanımlamak baya işe yarıyor; cache_staleness_seconds > 5 olduğunda haber almak da fena fikir değil. Şey… burada biraz abartı var gibi görünebilir ama sanırım tam da böyle ortamlarda küçük gecikmeler bile can sıkabiliyor.
Neyse uzatmayayım, konu buraya gelmişken şunu söyleyeyim: küçük ekipte rahat geçersiniz, büyük yapıda işe ölçmeden yürümek biraz riskli oluyor. Sız ne dersiniz?
Maliyet ve Performans Etkisi
Bir de şu tarafı var: Atomic FIFO ile resource version tracking bedava değil. Küçük bir CPU ve memory overhead’i geliyor, hani öyle gözü korkutacak seviyede değil ama sıfır da sayılmaz. Test ettiğim 200 node’lük cluster’da kube-controller-manager memory kullanımı yaklaşık %3-4 arttı; CPU tarafında işe açık konuşayım, elle tutulur bir fark görmedim.
Azure tarafında bakınca, özellikle AKS kullananlar için, olay biraz rahatlıyor. Control plane’i Microsoft yönettiği için bu yük size pek yansımıyor, yanı derdi onlar çekiyor (bu konuda ikircikliyim). AKS fiyatlandırmasında da control plane sabit olduğu için ekstra bir kalem çıkmıyor. Ama on-prem K8s kullanıyorsanız, controller manager’ın koştuğu node’da ufak da olsa bir kapasite payı bırakın derim; yoksa sonra “bu niye sıkıştı?” diye uğraşırsınız.
Kısa bir not düşeyim buraya.
Bunu yaşayan biri olarak söyleyeyim, Konuyla ilgili başka bir yazımda Kubernetes v1.36 Memory QoS: Katmanlı Bellek Koruması Geldi başlığı altında bellek tarafındaki yenilikleri daha detaylı anlatmıştım. Oraya da bir göz atın; işin aslı, bu iki konu yan yana düşünülünce tablo daha net oluyor.
Hangi Sorunlarla Karşılaşabilirsiniz?
Beta default-on dedik ya, işte burada küçük gibi görünen ama can sıkan bir şeye takıldım. Kendi test cluster’ımda custom bir operatör’ün CRD watch akışı, ilk informer sync sırasında yaklaşık 30 saniye öylece bekledi; sebep de AtomicFIFO’nun atomik batch işlemesini beklemesi, ama batch’in bir anda fazla şişmesi (yaklaşık 12.000 obje), yanı sistemin nefes alacak alan bulamamasıydı.
İşte tam da bu noktada devreye giriyor.
Çözüm tarafı da açıkçası çok süslü değil, baya düz ilerledi:
- İlk olarak
--watch-list-page-sizeparametresini düşürdük - Pagination devreye girdi, batch’ler küçüldü
- İlk sync süresi 30 saniyeden 4 saniyeye indi
Size bir şey söyleyeyim, Şey, burada hayatı nokta şu: Çok büyük cluster’larda ve elinizde epey CRD instance varsa, page size ayarını körlemesine bırakmayın (şaşırtıcı ama gerçek). Default değer fena değil ama her senaryoda iş görmeyebiliyor, hatta bazen tam tersine darboğaz yaratıyor.
Evet.
Açık konuşayım, İlgili konu olarak Ingress-NGINX Göçü: beş Şaşırtıcı Davranış ve Çözümü yazımda da benzer “beklenmedik beta davranışları” meselesine değinmiştim; orada da ilk bakışta masum duran ama sonra insanı uğraştıran detaylar vardı, yanı aynı tadın biraz farklı versiyonu gibi düşünün.
Kişisel Görüşüm
Açık konuşayım, bu özellik 2-3 yıl önce gelseydi ben baya daha rahat ederdim. Kubernetes tarafı staleness meselesini epey geç ciddiye aldı, hatta bazı ekipler yıllardır bunun etrafında workaround dönduruyordu; kimi reconcile’dan hemen önce Get atıp API’den taze veri çekti, cache’i baypas etti, performans biraz can yaktı. Güvenlik tarafı idare eder kaldı.
v1.36 sonunda bu konuyu first-class citizen gibi ele alıyor. İyi bir hamle. Ama dur bir saniye — iş burada bitmiyor. Mesela cross-controller staleness,. Iki ayrı controller’ın aynı objeyi farklı state’lerde görmesi, hâlâ masada duran bir sorun; %100 çözülmüş diyemem, bekleyeceğiz tabi.
Sonuç ne? v1.36’ya geçin, yeni metrikleri açın, custom controller yazıyorsanız LastSyncedResourceVersion‘ı kullanın. Ama ölçüyü kaçırmadan yapın, yanı her şeye “tamamdır” deyip koşmayın; bazı yerlerde bu özellik baya iş görüyor, bazı yerlerde işe sadece sorunu görünür hâle getiriyor. Her özellik gümüş kurşun değil.
Sıkça Sorulan Sorular
AtomicFIFO feature gate’ını kapatmam gerekiyor mu?
Genellikle hayır. Beta seviyesinde default açık geliyor ve aslında testlerimde hiç sorun çıkarmadı. Ama hani çok büyük CRD instance sayınız varsa (10.000+ gibi) (ciddiyim). Ilk informer sync’inde takılıyorsanız, geçici olarak kapatıp page size ayarlarınızı düzeltin — bence en temiz çözüm bu (şaşırtıcı ama gerçek)
v1.35’te kalsam bu özelliklerden yararlanabilir mıyım?
Maalesef yok. AtomicFIFO ve yeni metrikler direk v1.36 ile geliyor. v1.35’te benzer bir şey yapmak istiyorsanız kendi controller’ınızda manuel resource version takibi yapmanız gerekiyor ki, açıkçası bu ciddi bir iş yükü demek.
AKS, EKS, GKE gibi managed servislerde bu özellikler ne zaman aktif olur?
Yanı Kubernetes resmî sürümünden genellikle 1-3 ay sonra managed servislere yansıyor. AKS tarafında mesela v1.36 desteğinin 2026 yaz aylarında genel kullanıma açılmasını bekliyorum —. Önce preview kanalında test edebilirsiniz, bence o riski almaya değer.
Custom controller yazmıyorum, bu yazıyı niye okudum ki?
Hani şöyle düşünün: kullandığınız pek çok operatör’ler — cert-manager, ArgoCD, Istio, Prometheus operatör —. Bu altyapı üzerinde koşuyor. v1.36’ya geçtiğinizde bu araçların davranışı dolaylı olarak iyileşiyor. Ayrıca yeni metrikler sayesinde cluster sağlığınızı çok daha net görebiliyorsunuz, bu da başlı başına yeterli bir neden bence.
Staleness’ı tamamen önlemek mümkün mü?
Hayır. Çünkü dağıtık sistemlerde “tutarlı bir an” diye bir şey yok — bu biraz felsefi ama gerçek. Ama mitigation yapabilirsiniz: mesela kritik kararlar öncesi freshness kontrolü, stale cache’te aksiyon ertelemesi. Gözlemlenebilirlik. Tecrübeme göre bu üçünü bir arada uygulamak çok şeyi değiştiriyor. v1.36 da tam bu üçüne odaklanıyor zaten.
Kaynaklar ve İleri Okuma
Aslında, Kubernetes Resmî Blog: Staleness Mitigation and Observability for Controllers
Garip gelecek ama, client-go GitHub Repository
Kubernetes Feature Gates Dokümantasyonu
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.









Yorum gönder