Kubernetes yükseltmesi, ertelendiği her ay biraz daha zorlaşan işlerden biridir. Sürüm farkı açıldıkça atlanması gereken basamak sayısı artar, kaldırılan API sayısı birikir ve tek seferde yapılacak iş büyür. Aşağıda bir production cluster'ı yükseltirken hangi sırayla ilerlendiğini, nerelerde takılma yaşandığını ve geri dönüşün nasıl mümkün tutulduğunu anlatıyoruz.
Ertelemenin gerçek maliyeti: destek penceresi
Kubernetes yılda üç minor sürüm yayımlıyor ve her sürüm yaklaşık on dört ay yama alıyor. Aynı anda yalnızca üç minor sürüm desteklenir; bir cluster yılda en az iki kez yükseltilmezse destek penceresinin dışına düşer.
Pencereden düşmüş bir cluster çalışır, ama güvenlik yaması almaz. Sorun yükseltmenin zor olması değil, yükseltmeme kararının sessizce bir güvenlik kararına dönüşmesidir. Bu yüzden yükseltme, takvime bağlı düzenli bir bakım kalemi olarak ele alınmalıdır. Kendi işlettiğimiz ortamlarda Kubernetes cluster yönetimi kapsamında yükseltme planı baştan sözleşmenin parçasıdır, çünkü sonradan sıkıştırılan yükseltme her zaman daha pahalıya patlar.
Yükseltmeyi bozan asıl şey: API kaldırmaları
Yükseltmelerde kırılan şey genelde control plane değil, kaldırılmış bir API sürümünü kullanmaya devam eden manifest'lerdir. Kubernetes bir API sürümünü önce deprecated ilan eder, birkaç sürüm boyunca uyarı verir, sonra tamamen kaldırır. Kaldırıldığı sürüme geçtiğiniz anda o nesneyi oluşturan her şey hata vermeye başlar.
Geçmişte örnekleri yaşandı: 1.22 ile extensions/v1beta1 Ingress
kaldırıldı, 1.25 ile PodSecurityPolicy gitti. İkisinde de sorun cluster'da
değil, dokunulmamış Helm chart'larında ve CI deposundaki eski manifest'lerde
çıktı.
Yükseltme öncesi tarama üç yerde birden çalıştırılmalı:
- Cluster'da duran canlı nesneler, yani halihazırda oluşturulmuş kaynaklar.
- Git deposundaki manifest ve Helm chart dosyaları, çünkü bir sonraki deployment onları geri yazacak.
- Operator ve controller'lar, çünkü kendi oluşturdukları nesnelerin API sürümünü siz kontrol etmiyorsunuz.
Üçüncü madde en çok atlanandır. Cluster'daki nesneleri temizlemiş olsanız bile, eski bir operator yükseltmeden hemen sonra kaldırılmış API ile nesne oluşturmaya çalışır ve sorun geri gelir. Operator ve CRD sürümlerini Kubernetes sürümünden önce hizalamak gerekir. Aynı mantık kullandığınız imajlar için de geçerlidir: container registry tarafında sabitlenmiş imaj etiketleri, yükseltme sonrası hangi sürümün çalıştığını belirsizlikten kurtarır.
Version skew: neyin neyden ne kadar geride kalabileceği
Kubernetes bileşenleri arasında ne kadar sürüm farkına izin verildiği kesin kurallara bağlıdır ve bu kurallar yükseltme sırasını belirler. Özet olarak:
- kube-apiserver en yeni bileşendir, yükseltme her zaman ondan başlar.
- kube-controller-manager ve kube-scheduler, apiserver'dan bir minor sürüm geride olabilir.
- kubelet apiserver'dan birkaç minor sürüm geride kalabilir, ama asla ondan ileride olamaz.
- kubectl, apiserver'ın bir minor altında ya da bir minor üstünde olabilir.
Buradan çıkan tek kural şudur: önce control plane, sonra node'lar. Ters sırada ilerlemek, yani kubelet'i apiserver'ın önüne geçirmek desteklenmeyen bir durumdur ve davranışı öngörülemez. Ayrıca minor sürümler tek tek atlanır. 1.30 üzerinde olup 1.32 hedefliyorsanız 1.31 basamağına uğramak zorundasınız. Her basamakta cluster'ın sağlıklı olduğunu doğrulamadan bir sonrakine geçilmez.
PodDisruptionBudget ve drain'in takıldığı yer
Node yükseltmesi kubectl drain ile başlar: node önce
zamanlanamaz işaretlenir, sonra üzerindeki podlar tahliye edilir. Tahliye,
podu doğrudan silmez; eviction API üzerinden gider ve bu API
PodDisruptionBudget kurallarına uymak zorundadır.
En sık karşılaşılan tıkanma şudur: tek replikalı bir deployment için
minAvailable: 1 tanımlanmıştır. O tek podu tahliye etmek bütçeyi
ihlal edeceğinden eviction reddedilir ve drain sonsuza kadar bekler. Buradaki
hata bütçede değil, tek replikalı bir iş yükünü kesintisiz sanmaktır. Doğru
çözüm bütçeyi silmek değil, replika sayısını artırmak ya da bütçeyi
maxUnavailable üzerinden ifade etmektir.
Bütçe doğru olsa bile kesintisiz tahliye için iş yükü tarafında üç şey
gerekir: podların topologySpreadConstraints ile düğümlere
yayılması, readiness probe'un dürüst olması ve preStop kancası ile yeterli
terminationGracePeriodSeconds. Ayrıca cluster kapasitesi darsa
tahliye edilen podlar Pending kalır;
auto-scaling ve self-healing
tarafında Cluster Autoscaler'ın açık olması bu tıkanmayı baştan engeller.
Node havuzunu döndürmek mi, yerinde yükseltmek mi
Node'ları yükseltmenin iki yolu var. Yerinde yükseltmede mevcut node drain edilir, kubelet ve çalışma zamanı güncellenir, node geri alınır. Havuz döndürmede ise hedef sürümde yeni bir node havuzu açılır, iş yükleri oraya taşınır, eski havuz boşaltılıp kapatılır.
Yerinde yükseltme daha az kaynak ister ama geri dönüşü zordur; sorun çıkarsa node'u eski haline getirmek yeni bir işlemdir. Havuz döndürmede geri dönüş çok daha basittir, çünkü eski havuz siz kapatana kadar ayakta durur ve sorun görürseniz iş yüklerini geri alırsınız. Karşılığında yükseltme süresince iki havuz birden kaynak tüketir.
Yükseltme öncesi kontrol listesi
Yükseltme gününde sürpriz yaşamamanın yolu, listenin günler öncesinden çalıştırılmasıdır. Aşağıdakiler sırasıyla kontrol edilmeden node'lara dokunulmamalıdır.
- Hedef sürümün sürüm notlarındaki kaldırılan API listesi okundu ve cluster, git deposu ve operator'lar bu listeye karşı tarandı.
- CNI eklentisi, CSI sürücüleri, ingress controller ve izleme yığını için hedef Kubernetes sürümüyle uyumlu sürümler belirlendi. Bu bileşenler genelde Kubernetes'ten önce yükseltilmelidir.
- Kritik iş yüklerinin replika sayısı birden büyük ve PodDisruptionBudget tanımları tahliyeyi kilitlemeyecek şekilde yazılmış durumda.
- Cluster'da yükseltme sırasında tahliye edilen podları alacak kadar boş kapasite var ya da autoscaler yeni node açabilecek durumda.
- etcd anlık görüntüsü ya da eşdeğeri alındı ve geri yükleme adımı en az bir kez denendi. Denenmemiş bir yedek, yedek sayılmaz.
- Yükseltme öncelikle üretim dışı bir cluster üzerinde uçtan uca çalıştırıldı.
Doğrulama ve geri alma
Yükseltmeden önce alınacak yedek, control plane'i kimin yönettiğine göre değişir. Kendi control plane'inizi işletiyorsanız etcd anlık görüntüsü şarttır, çünkü cluster durumunun tamamı orada durur. Yönetilen bir control plane kullanıyorsanız etcd size kapalıdır; bu durumda tüm namespace nesnelerinin dışa aktarımı ve kalıcı disklerin anlık görüntüsü alınır.
Yükseltme sonrası doğrulamayı tek bir kubectl get nodes çıktısına
indirgememek gerekir. Sırayla bakılacaklar: tüm node'ların Ready olması,
kube-system altındaki bileşenlerin ayakta olması, CNI eklentisinin yeni
sürümle uyumlu çalışması, DNS çözümlemesinin ayakta olması, Ingress
üzerinden dışarıdan gerçek bir istek geçmesi ve kalıcı disklerin yeniden
bağlanabilmesi. Bu listenin son iki maddesi genelde en geç fark edilen
sorunları yakalar.
Geri alma konusunda dürüst olmak gerekir: Kubernetes control plane'inde minor sürüm düşürme desteklenmez. Yani gerçek geri alma planı sürümü geri almak değil, ilerlemeyi durdurup son sağlıklı duruma dönebilmektir. Bu da iki şeyle mümkün olur: yükseltmeyi tek adımda değil kademeli yapmak ve her kademede duracak kadar bilgiye sahip olmak. Havuz döndürme stratejisinin yerinde yükseltmeye göre asıl üstünlüğü de burada ortaya çıkar.
Yükseltmenin ağ tarafında da bir karşılığı var. CNI eklentisi değiştiğinde ya da yükseltildiğinde NetworkPolicy davranışı sessizce değişebilir; bu konuyu ayrıca ele aldığımız zero-trust ve mikro segmentasyon yazısında NetworkPolicy'nin hangi durumlarda hiç uygulanmadığına da değiniyoruz.
Sık sorulan sorular
Kubernetes cluster ne sıklıkla yükseltilmeli?
Kubernetes yılda üç minor sürüm yayımlar ve her sürüm yaklaşık on dört ay yama desteği alır. Bu da pratikte yılda en az iki yükseltme demektir. Destek penceresinin dışına düşen bir cluster güvenlik yaması almaz, bu yüzden yükseltmeyi takvime bağlamak tek sağlıklı yöntemdir.
Minor sürüm atlayarak yükseltme yapılabilir mi?
Control plane için hayır. kubeadm ve yönetilen servislerin çoğu tek seferde yalnızca bir minor sürüm ilerlemenize izin verir. 1.30 üzerindeki bir cluster 1.32 hedefleniyorsa önce 1.31 basamağından geçmek gerekir. Atlamaya çalışmak yükseltmeyi yarıda kesip cluster durumunu belirsiz bırakabilir.
PodDisruptionBudget yükseltmeyi neden engeller?
Tek replikalı bir deployment için minAvailable değeri 1 verildiğinde, o podu tahliye etmek bütçeyi ihlal edeceği için kubectl drain süresiz bekler. Çözüm bütçeyi kaldırmak değil, replika sayısını artırmak ya da maxUnavailable üzerinden ifade etmektir.
Yükseltme öncesi hangi yedek alınmalı?
Kendi yönettiğiniz control plane için etcd anlık görüntüsü şarttır, çünkü cluster durumunun tamamı orada durur. Yönetilen bir control plane kullanıyorsanız etcd size kapalıdır, bu durumda tüm namespace nesnelerinin dışa aktarımı ve kalıcı disklerin anlık görüntüsü alınmalıdır.
Yükseltme sırasında uygulama kesintisi nasıl önlenir?
Her iş yükünün en az iki replikası, düğümlere yayılmasını sağlayan bir topologySpreadConstraints kuralı, gerçekçi bir readiness probe ve bağlantıları düzgün kapatan bir preStop kancası gerekir. Bu dördü eksikse kesinti yükseltmeden değil uygulama tanımından gelir.
