Helm ile Durum Taşıyan Kaynaklar: Yeniden Adlandırma, resource-policy ve CRD Tuzakları
Helm bir adı değişen PVC'yi siler, StatefulSet'i boş disklerle yeniden açar, parolayı her yükseltmede üretir ve CRD'yi hiç güncellemez. Veri taşıyan kaynakları Helm ile yönetirken kaybın nedenleri ve her biri için çalışan önlem.
Helm'in zihinsel modeli basittir: şablonları işler, çıkan manifestleri kümeye uygular, sonucu bir release kaydı olarak saklar. Durumsuz bir web uygulaması için bu model sorunsuz çalışır. Sorun, chart'ın içinde veri taşıyan bir şey olduğunda başlar: bir PersistentVolumeClaim, bir StatefulSet, ilk kurulumda üretilen bir parola, chart'ın getirdiği bir CRD. Bu kaynaklar için Helm'in "eskiyi sil, yeniyi yarat" refleksi doğru davranış değildir ve Helm bunu sizin yerinize ayırt etmez. Bu yazı, durum taşıyan kaynakları Helm ile yönetirken kaybedilen verinin nedenlerini ve her biri için çalışan önlemi anlatıyor.
Helm neyi takip eder, neyi etmez
Bir release, son işlenmiş manifestin tamamını bir Secret içinde saklar. Yükseltmede Helm üç şeye bakar: önceki release manifesti, yeni manifest ve kümedeki canlı nesne. Bu üç yönlü birleştirme, sizin elle yaptığınız değişiklikleri korumaya çalışır ama karar mantığı kaynağın kimliğine dayanır: aynı API grubu, tür, ad ve namespace. Kimlik değişirse Helm bunun bir düzenleme olduğunu bilemez. Eski kimlikli nesneyi siler, yeni kimliklisini yaratır.
Helm'in hiç takip etmediği kaynaklar da vardır. StatefulSet'in volumeClaimTemplates alanından doğan PVC'ler release manifestinde yoktur, onları StatefulSet denetleyicisi yaratır. crds/ dizininden kurulan tanımlar yalnızca ilk kurulumda uygulanır. Hook olarak işaretlenen nesneler release'in parçası sayılmaz. Bu üç istisnanın her biri ayrı bir tuzağın kaynağıdır.
Yeniden adlandırma bir silme işlemidir
En sık yaşanan veri kaybı senaryosu, bir kaynağın adının değişmesidir. Bu çoğu zaman bilinçli bir yeniden adlandırma bile değildir: fullnameOverride değerini değiştirmek, chart'ın yeni sürümünde adlandırma şablonunun düzeltilmesi ya da release'i başka bir adla yeniden kurmak aynı sonucu verir.
Adı değişen bir PVC için Helm eski PVC'yi siler. StorageClass'ın geri kazanım politikası Delete ise altındaki disk de gider. StatefulSet için durum daha sinsidir: Helm StatefulSet'i siler ve yenisini yaratır, ancak eski PVC'ler Helm'in malı olmadığından yerinde kalır. Yeni StatefulSet kendi adına göre yeni PVC adları üretir ve boş disklerle başlar. Veri silinmemiştir ama uygulama sıfırdan açılmıştır; siz de eski PVC'leri fark etmeden temizlik yaparsanız bu sefer gerçekten gitmiştir.
Önlem iki katmanlıdır. İlk katman, veri taşıyan her kaynağa şu ek açıklamayı koymaktır:
metadata:
annotations:
helm.sh/resource-policy: keep
Bu açıklama iki durumda devreye girer: release kaldırılırken ve kaynak şablonlardan çıkarıldığında ya da adı değiştiğinde. Helm kaynağı silmez, yalnızca takibini bırakır. Bedeli, kümede öksüz kaynak birikmesidir; bunun için kaynağa release adını taşıyan bir etiket koyup periyodik olarak öksüzleri listeleyen bir denetim yazmak gerekir.
İkinci katman, adı hiç değiştirmemektir. Chart'ın adlandırma şablonunu bir kez yayımladıktan sonra sözleşme sayın. Chart sürümü yükseltilirken adlandırma değişiyorsa bunu chart yazarı bir taşıma notu ile duyurmalıdır, siz de yükseltmeden önce helm diff upgrade ile çıkan farkta silinen bir PVC ya da StatefulSet olup olmadığına bakmalısınız. Farkı okumadan yükseltmek, veriyi rastlantıya bırakmaktır.
Aynı kural chart'ın bir operatör aracılığıyla yarattığı dış kaynaklar için de geçerlidir. Bir nesne depolama kovasını ya da veritabanını "chart yaratsın" bayrağıyla açtıysanız, o kaynağın adını değiştirmek operatörün eski kaynağı silmesine yol açabilir. Önce operatörün silme politikasını okuyun, sonra adı değiştirin.
Her yükseltmede yeniden üretilen parolalar
Chart'lar sıkça şu deseni kullanır: kullanıcı parola vermediyse randAlphaNum 32 ile üret. Şablon her yükseltmede yeniden işlendiği için parola her yükseltmede değişir. Secret güncellenir, ama veritabanı diskinde eski parolayla oluşturulmuş kullanıcı durur. Sonuç, yükseltme sonrasında kimlik doğrulama hatasıdır ve çoğu kişi bunu önce ağ sorunu diye araştırır.
Doğru çözüm, kümedeki mevcut değeri okumaktır:
{{- $existing := (lookup "v1" "Secret" .Release.Namespace "db-credentials") }}
{{- if $existing }}
password: {{ index $existing.data "password" }}
{{- else }}
password: {{ randAlphaNum 32 | b64enc }}
{{- end }}
Bu yaklaşımın bilinmesi gereken bir sınırı var: lookup yalnızca kümeye bağlı çalışır. helm template çıktısında ve istemci tarafında yapılan kuru koşuda boş döner, dolayısıyla her seferinde "yeni parola üretilecek" gibi görünür. Sunucu tarafı kuru koşu bu farkı kapatır. Daha temiz alternatif, parolayı chart'ın dışında üretmek ve chart'a yalnızca var olan Secret'in adını vermektir. Sır yaşam döngüsü ile uygulama yaşam döngüsünü ayırmak, sonraki her yükseltmeyi kolaylaştırır.
CRD'ler ve hook'lar release'in dışındadır
crds/ dizinindeki tanımlar ilk kurulumda uygulanır ve bir daha dokunulmaz. Chart'ın yeni sürümü CRD'ye alan eklediyse Helm bunu kümeye taşımaz. Uygulama yeni alanı kullanmaya kalktığında API sunucusu alanı sessizce düşürür ya da doğrulama hatası verir. Yükseltme akışında CRD'leri ayrı bir adım olarak kubectl apply --server-side ile uygulamak gerekir. Aynı nedenle Helm CRD'leri kaldırma sırasında da silmez; bu bir hata değil, o CRD'ye bağlı tüm özel kaynakların yok olmasını engelleyen bilinçli bir tercihtir.
Hook'lar için tuzak ters yönde işler. helm.sh/hook: pre-upgrade ile işaretlenen bir Job release'in parçası değildir; başarılı olsa da kümede kalır. Aynı adla bir sonraki yükseltmede yeniden yaratılmak istenince "zaten var" hatası verir. Silme politikasını açıkça yazın:
metadata:
annotations:
helm.sh/hook: pre-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
Başarısız hook'u kümede bırakmak bilinçli bir seçimdir; günlüğü inceleyebilirsiniz. Ama bir sonraki denemede before-hook-creation olmadan aynı hata yeniden çıkar.
Değiştirilemez alanlar
Bazı alanlar API sunucusu tarafından oluşturulduktan sonra kilitlenir: StatefulSet'te volumeClaimTemplates ve serviceName, Deployment'ta spec.selector, Job'da spec.template. Chart'ın yeni sürümü bu alanlardan birini değiştiriyorsa yükseltme "field is immutable" hatasıyla durur ve release failed durumuna düşer. Bu hatanın Helm ile çözümü yoktur. Ya kaynağı denetleyiciyi bozmadan silmek gerekir (kubectl delete statefulset --cascade=orphan Pod'ları ve PVC'leri yerinde bırakır), ya da eskiyi yanında tutarak yenisini farklı adla açıp veriyi taşımak.
Disk boyutu bu kuralın kısmi istisnasıdır: PVC yalnız büyütülebilir ve yalnız StorageClass izin veriyorsa. StatefulSet şablonundaki boyutu değiştirmek ise değiştirilemez alan hatasına girer; mevcut PVC'leri tek tek büyütüp şablonu ancak --cascade=orphan ile yeniden yaratarak eşitleyebilirsiniz.
Yarım kalan yükseltmeler
CI'da zaman aşımına düşen ya da öldürülen bir helm upgrade, release'i pending-upgrade durumunda bırakır. Sonraki her deneme "another operation is in progress" hatası verir. Çözüm, helm history ile son sağlıklı sürümü bulup helm rollback yapmaktır; bu da olmuyorsa asılı kalan release sürümünün Secret'ini silmek gerekir. Bunu önlemek için CI adımlarında --timeout değerini gerçek başlatma süresine göre verin ve --atomic kullanın. --atomic başarısızlıkta kendiliğinden geri alır, fakat ilk kurulumda başarısız olursa release'i tümüyle kaldırır. Chart PVC yaratıyorsa ve keep açıklaması yoksa ilk denemede yazılan veri de gider. İlk kurulumda --atomic yerine hatayı görüp elle karar vermek daha güvenlidir.
Bir de değerler tuzağı var. --reuse-values önceki release'in değerlerini olduğu gibi alır ve yeni chart sürümünün values.yaml dosyasındaki yeni varsayılanları görmezden gelir. Yeni sürümün eklediği bir güvenlik ayarı böylece hiç devreye girmez. Değerleri her zaman depoda bir dosyada tutun ve her yükseltmede -f ile açıkça verin.
Ne zaman Helm'e yaslanmamalı
Veritabanı, mesaj kuyruğu ya da arama motoru gibi bir sistemin ömrü uygulamanın dağıtım ritminden bağımsızsa, onu uygulama chart'ının alt bağımlılığı olarak kurmak kötü bir fikirdir. Uygulamayı yeniden adlandırdığınızda, release'i başka namespace'e taşıdığınızda ya da chart'ı değiştirdiğinizde veri katmanının da o dalgaya kapılmasını istemezsiniz. Bu sistemleri kendi release'iyle, tercihen kendi operatörüyle kurun; uygulamaya yalnız bağlantı bilgisini taşıyan bir Secret verin. Helm, yaşam döngüsü kısa ve tekrar üretilebilir şeyler için iyi bir araçtır. Uzun ömürlü veri için, en azından o veriyi çevreleyen kaynaklarda Helm'in silme yetkisini elinden almak gerekir.