Şahsen, Bir Kubernetes kümesini ilk kez “neden bu node’da OOM kill yiyoruz?” diye debug ettiğinizde, bellek işinin ne kadar kaba saba yürüdüğünü görüyorsunuz (bizzat test ettim). Limit koyuyorsun, request koyuyorsun, gerisini kernel’a bırakıyorsun. Sonra kernel de dönüp size “ben elimden geleni yaptım kardeşim” havası veriyor.
İşte v1.36 ile gelen Memory QoS güncellemesi tam da bu boşluğu biraz toparlamaya çalışıyor. Aslında özellik yeni değil; v1.22’de çıkmıştı, v1.27’de bir revizyon yemişti. Ama açık konuşayım, önceki halleri sahada biraz fazla hevesliydi diyebilirim, şimdi işe daha oturmuş, daha kontrollü bir sürüm var karşımızda.
Lafı uzatmadan girelim. Ne değişti, niye önemli, kimin umurunda olmalı?
Önce Şu cgroup v2 Meselesini Hatırlayalım
Bak şimdi, Memory QoS’un işi aslında cgroup v2’nın memory controller’ına dayanıyor. Üç dosya var, hepsi kritik: memory.min, memory.low ve memory.high. İlk bakışta kafa karıştırıyor, açık konuşayım; ben de ilk gördüğümde “bu kadar dosyaya ne gerek var” demiştim.
- memory.min — “Bu hafıza benim, dokunamazsın.” Hard reservation. Kernel sistem sıkışsa bile bu sayfaları geri almaya çalışmaz, gider başka bir yeri OOM eder.
- memory.low — “Bu hafıza benim ama çok sıkışırsan al.” Soft koruma. Normal baskıda el sürülmez, ama ortalık iyice daralırsa feda edilebilir.
- memory.high — “Şu sınırı aşarsan seni yavaşlatırım.” Throttle. Direkt öldürmez, ama process’i öyle bir frenler ki nefes nefese kalır.
v1.27’deki Memory QoS, memory request’i olan tüm container’lara doğrudan memory.min yazıyordu. Yanı işin özü hard reservation yapıyordu. Daha açık söyleyeyim, peki neden? Çünkü mantık basit görünüyordu: request varsa körü, bitti gitti. Ama dür bir saniye — pratikte bu iş o kadar temiz yürümüyor.
Ve işler burada ilginçleşiyor.
Evet. Daha fazla bilgi için VSTest Newtonsoft.Json Bağımlılığını Bırakıyor: Etkisi Ne? yazımıza bakabilirsiniz.
Araya gireyim: Böyle olunca düğüm üzerindeki bellek baskısı büyüyebiliyordu, hele bir de request değerleri şişkinse, sistem bir anda beklenmedik şekilde dar boğaza giriyordu. Neyse uzatmayalım; asıl problem tam burada başlıyor, çünkü iyi niyetle koyduğun koruma bazen diğer workload’ların ayağına dolanıyor.
Peki, siz ne dersiniz?
v1.27’nın Sahada Yaşattığı Sorun
2024 sonunda bir telekom müşterisinde test ortamında Memory QoS’u açmıştık — feature gate ile, hani “bakalım ne olacak” diye. 8 GiB RAM’li node’lardı, üstlerinde Burstable sınıfında onlarca pod dönüyordu; toplam memory request’leri de aşağı yukarı 7 GiB civarındaydı. Kısacası, kağıt üstünde idare eder gıbı duruyordu.
Ne öldü dersiniz? Kernel’in elinde sistem daemon’ları, kubelet, containerd ve BestEffort iş yükleri için 1 GiB bile kalmadı, çünkü 7 GiB’i hard reservation gıbı kilitledik; işte asıl sıkıntı burada başladı (inanın bana). Ufak bir trafik dalgalanması geldiğinde node — itiraz edebilirsiniz tabi — üzerindeki DaemonSet’ler birer birer OOM yemeye başladı — dürüst olayım, biraz hayal kırıklığı —. Ciddi can sıktı.
Burada öğrendiğim ders şuydu: Burstable pod’un request’i bir tahmin, taahhüt değil. Bunu kernel’a “bu adamın hakkı, kıymık bile koparma” diye söylemek, sistemi kırılgan hâle getiriyor; hani beklediğiniz şey bu değil. Guaranteed pod ile Burstable pod arasındaki sınıf ayrımının bir nedeni olmalıydı, yoksa denge hemen bozuluyor. v1.27 bunu pek görmüyordu.
Bu hikâyeyi neden anlattım? Çünkü v1.36’nın getirdiği “tiered protection”, yanı katmanlı koruma, tam da bu acının üstüne kurulmuş. Evet.
Bak şimdi, mesele sadece bellek ayırmak değil; asıl konu kime ne kadar güveneceğini doğru tartmak. Siz de benzer bir ortamda “request verdim öldü” sandınız mı hiç? Neyse uzatmayalım, aşağıda göreceğimiz şey tam olarak bu dengenin daha akıllı kurulması.
v1.36 Ne Getiriyor: Throttling ile Reservation Ayrıldı
En can alıcı değişiklik şu: artık memory.high. Throttling ile memory.min/low yanı reservation tarafı ayrı switch’lerle yönetiliyor. İlk bakışta ufak bir ayrıntı gıbı duruyor, ama değil; feature gate’i açınca throttling otomatik devreye giriyor ve kubelet memoryThrottlingFactor (default 0.9) üzerinden memory.high değerini yazıyor, öte yandan reservation için de ayrıca memoryReservationPolicy diye yeni bir kubelet ayarı geliyor.
Kısacası iki yol var. Evet, bu kadar basit.
- None (varsayılan): Hiç
memory.minya damemory.lowyazılmıyor. Yanı sadece throttling çalışıyor, gerisi boşta kalıyor. - TieredReservation: Pod’un QoS sınıfına göre koruma uygulanıyor. Asıl hikâye burada başlıyor.
Tiered Reservation Nasıl Çalışıyor?
İlginç olan şu ki, Peki neden önemli? Çünkü burada kernel’a gönderilen sinyal, pod’un ne kadar korunacağını doğrudan belirliyor; Guaranteed için başka, Burstable için başka, BestEffort için işe neredeyse hiç şefkat yok. Garip ama mantıklı.
| QoS Class | cgroup Alanı | Kernel Davranışı |
|---|---|---|
| Guaranteed | memory.min = request |
Hiçbir koşulda reclaim edilmez. Gerekirse başkası OOM ölür. |
| Burstable | memory.low = request |
Sıradan baskıda korunur, sistem-wide OOM riskinde feda edilebilir. |
| BestEffort | Hiçbiri | Tamamen reclaimable. İlk gözden çıkarılan da bu ölür. |
Açık konuşayım, bu tasarım bana baya yerinde geldi. Çünkü Kubernetes’in QoS sınıfları zaten bunu anlatıyordu; sadece kernel tarafına tercüme ederken biraz tökezlenmişti, şimdi o eksik parça yerine oturuyor gıbı.
Neyse, çok dağıtmadan devam edelim.
Sahadan Bir Bakış
Diyelim ki Guaranteed bir pod 512 MiB request etti. Node’a SSH atıp baktığınızda şuna benzer bir çıktı görüyorsunuz:
$ cat /sys/fs/cgroup/kubepods.slice/kubepods-pod6a4f....slice/memory.min
536870912
Aynı request Burstable pod için geldiğinde tablo değişiyor, dosya adı da değişiyor (işin aslı fark tam burada), çünkü bu kez değer memory.low altına yazılıyor:
$ cat /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8b3c....slice/memory.low
536870912
İşin garibi, Aynı 512 MiB, evet. Ama farklı dosya. Kernel açısından bu küçük farkın etkisi büyük; biri “dokunma” diyor, öteki “önce başkasını sıkıştır” diyor (en azından benim deneyimim böyle). Kısacası, siz ne dersiniz?
Burada, tam da öyle. Daha fazla bilgi için Docker İmajını Küçültmek: 1,58 GB’dan 186 MB’a yazımıza bakabilirsiniz.
memoryReservationPolicy: Aşamalı Geçiş Yapma Şansı
Bu ayrımın benim için en kıymetli tarafı şu: artık önce throttling’i açıp ortamı izleyebiliyorsunuz, sonra da işin içine reservation’ı sokuyorsunuz. v1.27’de pek öyle değildi; ya hep ya hiç vardı, biraz sertti yanı.
İşin güzel yanı burada başlıyor. Evet.
Pratikte ben şöyle giderdim:
- Önce dev/test ortamında feature gate’i açın,
memoryReservationPolicy: Nonebırakın. Böylece sadecememory.highthrottling davranışını görürsünüz; ilk bakışta sıkıcı gıbı duruyor ama aslında en temiz sinyal oradan geliyor. - Workload’ların throttle altında nasıl davrandığını metriklerden takıp edin. Java uygulamaları, GC yoğun iş yükleri ve arada bir nefes alan servisler burada sürpriz yapabiliyor, hani “bu niye yavaşladı?” dediğiniz anlar çıkabiliyor.
- Bir hafta iki hafta sonra, node kapasitesinde rahat bir headroom olduğundan eminseniz
TieredReservation‘a geçin. Burada acele etmeyin; çünkü kağıt üstünde iyi duran şey, pratikte bazen ufak bir kuyruk etkisi yaratıyor. — ciddi fark yaratıyor - Production’a ancak staging’de en az bir trafik piki gördükten sonra çıkın. Yoksa işin rengi değişiyor; testte sakın duran sistem, gerçek trafikte bir anda bambaşka konuşabiliyor.
Açık konuşayım, bunu adım adım yapmadan production’a almak biraz cesaret işi. Ben olsam direkt dalmazdım; önce gözlemlerim, sonra karar veririm. Bir bakıma, siz ne dersiniz?
Burada, neyse, çok dağıtmadan şunu da ekleyeyim: bu yaklaşım bana göre sadece daha güvenli değil, aynı zamanda ekip içinde tartışmayı da azaltıyor. Çünkü herkes aynı anda “neden yavaşladı” diye birbirine bakmıyor; önce veri geliyor, sonra yorum yapılıyor. GitHub Copilot CLI: Interactive vs Non-Interactive Mod Farkı yazımızda bu konuya da değinmiştik.
Vallahi, Bakın, tam da öyle.
Yeni Gözlemlenebilirlik Metrikleri
v1.36 ile kubelet’in /metrics endpoint’ine iki yeni alpha metrik geldi. Kısa kısa bakınca basit duruyorlar, ama işin içi biraz daha karışık, çünkü node tarafındaki memory reservation durumunu artık doğrudan görebiliyorsunuz: (bizzat test ettim)
kubelet_memory_qos_node_memory_min_bytes— Node’da toplam ne kadar hard reservation yazıldığıkubelet_memory_qos_node_memory_low_bytes— Node’da toplam ne kadar soft reservation yazıldığı
Şunu söyleyeyim, Bunlar küçük gıbı görünebilir. Ama bence can alıcı nokta tam da burada. Çünkü bir node’un ne kadarının “kilitli” olduğunu görmeden FinOps tarafında sağlıklı karar vermek zor oluyor; şimdiye kadar bu bilgiyi almak için /sys/fs/cgroup altında manuel script yazmak gerekiyordu (ve açık konuşayım, bunu yapan çok az kişi vardı).
Bi saniye — Prometheus’a alıp bir Grafana paneli kurarsanız, olay bir anda daha okunur hâle geliyor. Hatta “node-A’da %85 reservation lock’lu, oraya yeni pod schedule etmeyelim” gıbı kararları otomatikleştirebilirsiniz. Güzel fikir, ama dür bir saniye — bu veriyi doğru yorumlamazsanız yanlış alarm da üretebilir.
MemoryQoS=true) ve API stabilitesi garanti değil. Production için beta’yı beklemenizi öneririm — ki bu da v1.37 veya v1.38 ölür muhtemelen.
Kernel Versiyonu Uyarısı: memory.high Tuzakları
Bak şimdi, v1.36’nın getirdiği küçük ama işe yarayan detaylardan biri bu. Kubelet, eski kernel sürümlerinde memory.high kullanırken artık uyarı veriyor; çünkü 5.x serisinin erken dönemlerinde bu ayarın davranışı biraz nazlıydı, özellikle reclaim tarafında gereksiz sertleşme ve direct reclaim latency yüzünden işler beklediğiniz gıbı gitmeyebiliyordu. Bu konuyla ilgili GA4’ü Bırakıp Next.js + Supabase’e Geçmek: Neden? yazımıza da göz atmanızı tavsiye ederim.
Kabaca şöyle: kernel 5.10 öncesinde memory.high‘ı fazla sıkı bastığınızda, container içinde gecikmeler bir anda zıplayabiliyordu. Burada, peki neden? Çünkü v1.36 bunu görüp size açık açık “kardeşim, kernel eski” diye işaret ediyor; yanı sessiz sedasız geçip gitmiyor, sızı önceden dürtüyor. Bence fena değil.
Türkiye’deki Kurumsal Yapılar İçin Bu Ne Anlama Geliyor?
Şimdi asıl yere geldik. Bu özelliği Türkiye’deki kurumsal müşterilerde değerlendirirken, birkaç çıplak gerçeği masaya koymak lazım; yoksa konu kolayca teoride kalıyor, pratikte de herkes birbirine bakıp duruyor. Bu konuyla ilgili .NET’in Composable AI Stack’i: ConferencePulse Vakası yazımıza da göz atmanızı tavsiye ederim.
Birinçisi: Çoğu Türk şirketi hâlâ on-prem Kubernetes ya da managed olmayan kümelerde çalışıyor. AKS kullananlarda bile node image güncellemeleri bazen gecikiyor, kernel versiyonları beklediğinizden eski çıkabiliyor; işte burada Memory QoS’tan tam verim almak için en az 5.15+ kernel lazım, ben olsam önce uname -r ile bakar, sonra konuşurdum.
Evet.
İkincisi: Türkiye’deki ekiplerde “feature gate açma kültürü” pek yaygın değil. Alpha özellikler genelde vendor’ın (Azure, AWS) bunu GA yapmasını bekliyor, ve açık konuşayım bu çok da tuhaf değil; bir bankacılık projesinde alpha bir şeyi production’a itmeye çalışan CTO görmedim henüz, görsem muhtemelen yarım saat tartışırdık.
Peki neden?
İnanın, Üçüncüsü, ki bence en önemlisi: Kurumsal yapılarda pod’ların büyük kısmı Burstable oluyor. Çünkü developer’lar request/limit ayarıyla uğraşmayı sevmiyor, “limit aynı request” deyip Guaranteed’e geçmek de maliyet ve karmaşıklık korkusu yaratıyor; Memory QoS’un en işe yaradığı yer tam burası zaten, yanı o Burstable kalabalığında memory.low ile makul bir koruma verince mevcut karmaşa biraz nefes alıyor.
Tam da öyle.
Küçük Ekip vs Enterprise: Kim Ne Yapsın?
Bu tıp işlerde herkese aynı reçeteyi yazmak, açık konuşayım, biraz havada kalıyor. Kimi yerde hiç gerek yok, kimi yerde işe kaçınılmaz oluyor; yanı önce kümeye bakmak lazım, node sayısına değil sadece, iş yüküne de, ekipteki sabra da (evet, o da önemli), sonra karar vermek daha mantıklı.
Startup veya Küçük Ekipseniz
Açıkça söyleyeyim: bu özelliği şu an açmak zorunda değilsiniz. 10-20 node’lük bir kümede memory baskısı sorunu yaşamıyorsanız, alpha bir feature ile oyalanmaya pek değmez; hani elinizde daha açıl işler varken bunu öne almak biraz garip kaçıyor. Şu an enerjinizi Kubernetes v1.36: Pod-Level In-Place Resize Beta’da gıbı daha pratik beta özelliklerine ayırın — onların getirisi daha net, en azından neye yaradığı ilk bakışta belli oluyor.
Enterprise Yapıdaysanız
Vallahi, Burada tablo değişiyor. 100+ node’lük kümeleriniz varsa, multi-tenant ortamlar işletiyorsanız, OOM kill log’ları neredeyse günlük rutine döndüyse — bak şimdi — bu özellik tam da incelemeye değer bir noktaya geliyor; ama yine altını çizeyim, alpha. Yanı “hemen production” diye atlamak yok. Yapacağınız şey de aslında basit:
Çok konuştum, örnekle göstereyim.
- Bir test kümesinde feature gate aç
- 2-3 hafta gözlem yap, metriklerin önemli paneli kur
- Kernel versiyon envanterini çıkar (Ansible ile beş dakikalık iş)
- v1.37’de beta’ya geçtiğinde production roadmap’ine al
Evet.
Bu arada multi-tenant Kubernetes işletiyorsanız Kubernetes v1.36 Controller Staleness: Artık Daha Az Acı yazımdaki konuyla bunu beraber okumanızı öneririm — ikisi de v1.36’nın “yetişkin Kubernetes” tarafına ait; biri controller tarafında biraz nefes aldırıyor, diğeri de memory tarafında kapıyı aralıyor gıbı duruyor. Kısacası, siz ne dersiniz?
Pratik İlk Adımlar: Bugün Ne Yaparsınız?
Bu özelliği denemek istiyorsanız, ben olsam sırayı şöyle kurarım:
- Kernel kontrolü:
uname -rile node’larınızın kernel versiyonuna bakın. 5.15 altındaysa, işin rengi biraz değişiyor; Memory QoS’tan beklediğiniz verimi tam alamayabilirsiniz. - cgroup v2 kontrolü:
stat -fc %T /sys/fs/cgroup/komutunu çalıştırın, çıktıcgroup2fsdönsün. cgroup v1 tarafındaysanız, açık konuşayım, bu özellikle çok da anlamlı olmaz. - Test kümesinde feature gate aç: kubelet config içine
featureGates: {"MemoryQoS": true}ekleyin. Ama öncememoryReservationPolicy: Noneile başlayın; bir anda yüklenip sistemi sıkıştırmaya gerek yok. - Metrikleri scrape edin: Yeni alpha metriklerini Prometheus’a alın, sonra bir baseline çıkarın (hmm, asıl kıymetli kısım orası), yoksa neyin değiştiğini sonradan ayıklamak zorlaşıyor.
- İki hafta sonra: Davranış stabil görünüyorsa
memoryReservationPolicy: TieredReservation‘a geçin. Evet, acele etmeyin; bazen en temiz sonuç biraz bekleyince geliyor.
Hani, Neyse, işin özü bu kadar basit değil ama ilk temas için yeterli (evet, doğru duydunuz). Siz ne dersiniz?
Evet.
Kişisel Görüşüm: Doğru Yöne Atılmış Bir Adım, Ama Eksikler Var
Bu özelliğe bakınca ilk hissettiğim şey şu öldü: fena değil. v1.27’deki “tek anahtarla her şeyi açıyoruz” yaklaşımının sahada nasıl can yaktığını bizzat görmüş biri olarak, tiered protection ile memoryReservationPolicy ayrımını yerinde buluyorum; ama işin içine biraz girince, tamam güzel, yine de bazı boşluklar göze çarpıyor.
Burada, açık konuşayım, bu ayrım kağıt üstünde düzgün duruyor. Evet. Ama pratikte insan hemen şunu soruyor: neden pod seviyesinde daha ince bir kontrol yok?
- Pod-level granularity yok. Belirli bir pod için “ben Burstable’ım ama bu kritik bir iş yapıyorum, bana memory.min ver” diyemiyorsunuz. Annotation tabanlı bir override olsa, iş baya toparlardı; çünkü her workload aynı kefeye konmuyor, bunu sahada defalarca gördüm. (bu kritik)
- Metrikler sadece node düzeyinde. Pod bazında ne kadar protection yazıldığını görmek için hâlâ cgroup’ları elle okumak gerekiyor. Şey, bu da çok keyifli değil; özellikle olay büyüyünce node tarafında görünen tablo ile pod tarafındaki gerçek durum birbirinden epey sapabiliyor.
- Alpha olduğu için production hikâyesi henüz netleşmiş değil. Beta gelene kadar gerçek dünyada nasıl davrandığını tam bilemeyeceğiz; yanı evet, yön doğru ama ayağımızı gazdan çekmeden önce biraz daha veri görmek lazım.
Peki, neyse, çok dağıtmadan devam edeyim. Bu yine de doğru yönde atılmış bir adım. Kubernetes’in bellek yönetimi 2020’lerin başında baya “ya çalışır ya OOM” kafasındaydı; şimdi en azından kernel’a daha düzgün sinyal göndermeyi öğreniyoruz (küçük gıbı duruyor. Etkisi sandığınızdan büyük olabiliyor).
Peki yeter mi — Bence henüz değil. Tam da öyle.
Sıkça Sorulan Sorular
Memory QoS GA olmadan production’da kullansam ölür mu?
Hani, Açıkçası, hayır. Alpha feature’ların API’leri değişebiliyor, davranışları da zaman zaman sürpriz yapabiliyor. Bence Beta’ya geçene kadar bunları test/staging ortamlarında deneyimlemek çok daha mantıklı. Production için en erken v1.37-v1.38 dönemini beklemenizi öneririm.
cgroup v1’de Memory QoS çalışıyor mu?
Hayır, çalışmıyor. Memory QoS büyük ölçüde cgroup v2’nın memory controller’ına dayanıyor yanı önce node’larınızı v2’ye geçirmeniz şart. Hâlâ cgroup v1’deyseniz bu adımı atlayamazsınız. Neyse ki modern Linux dağıtımlarında — mesela Ubuntu 22.04+ veya RHEL 9+ — zaten varsayılan olarak v2 geliyor (ilk duyduğumda inanamadım)
Bakın, burayı atlarsanız yazının kalanı anlamsız kalır.
memoryThrottlingFactor değerini değiştirmeli mıyım?
Default olan 0.9, çoğu workload için gayet makul. Ama tecrübeme göre GC-heavy iş yükleriniz varsa — özellikle eski JVM uygulamalarında — bunu 0.95’e çıkarmak throttle stress’ını oldukça azaltabiliyor. Latency-sensitive servisler için büyük ihtimalle test edip karar verin, hani her şeyi kitaptan uygulamak burada pek işe yaramıyor.
Guaranteed pod’larım az, bu özellikten faydalanabilir mıyım?
Evet, hatta bu mimarı aslında tam size göre tasarlanmış. Tiered reservation’ın asıl gücü, Burstable pod’lara memory.low ile soft koruma vermesinden geliyor. Zaten çoğu kümede Burstable çoğunluk, Guaranteed azınlık oluyor — bu yapı da tam bu gerçeklik üzerine kurulmuş.
OOM kill’leri tamamen ortadan kaldırıyor mu?
Maalesef hayır. Bellek fiziksel olarak biterse bir şeyler ölecek, soru sadece “kim ölecek” oluyor (ciddiyim). Memory QoS, yanı doğru pod’ların doğru zamanda ölmesini (ya da hiç ölmemesini) sağlamaya çalışıyor —. Bellek yokluğunu sihirle var etmiyor. Bence bu noktayı net görmek önemli. Kapasite planlaması hâlâ sizin işiniz.
Kaynaklar ve İleri Okuma
Kubernetes v1.36: Tiered Memory Protection with Memory QoS — Resmî Blog Yazısı
Kubernetes Pod QoS Classes Dokümantasyonu
Linux Kernel cgroup v2 Memory Controller Referansı (bizzat test ettim)
Ne yalan söyleyeyim, KEP-2570: Memory QoS Enhancement Proposal
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



