Web uygulamaları hayatımızın içine bu kadar çok girince ve bu kadar büyük kitlelere yayılınca kesintilerin maliyetleri de artmaya başladı. Bugün çoğu büyük yazılım firması uygulama geçişlerinde kesinti yaşatmamak için zero downtime deployment’a geçmiş durumda.
Bu yazıda size Azure Kubernetes Service üzerinde bir zero downtime türü olan canary release’i nasıl yapabileceğinizi anlatacağım. Benim örneğim Azure Kubernetes Service üzerinden olacak ama siz farklı Kubernetes platformlarında da deneyebilirsiniz.
Kubernetes Üzerinde Canary Release
Kubernetes üzerinde koşan uygulamalarınız için Canary release yapmak istiyorsanız çeşitli release tool’ları kullanabilirsiniz. Örneğin Spinnaker veya Tekton gibi release tool’larıyla tüm CI/CD hattınızı istediğiniz gibi oluşturup, uygulamalarınızı da canary release ile kesintisiz deploy edebilirsiniz. Bugün kullanacağımız tool ise, Kubecon 2020'de de bolca söz edilen
Flagger.
https://github.com/weaveworks/flagger
Flagger bir CI/CD tool’u değil. Kubernetes için yazılmış bir release tool’u. Yukarıda bahsettiğim tool’lar kadar çok iş yapmıyor. Tek odak noktası release stratejileri. Blue-green, A/B veya canary release yapmanıza yardımcı oluyor. Diğer CI/CD tool’larıyla beraber de kullanabilirsiniz. Flagger’ı kurduğunuzda ve uygulamanızı dinlemesini sağladığınızda, örneğin Azure DevOps üzerinden imaj tag’ini güncellediğiniz uygulamanızın Azure DevOps tarafında hiçbir değişiklik yapmadan canary release ile deploy olmasını sağlayabilirsiniz.
Flagger’ın bir başka özelliği de güncel service mesh uygulamaları ve ingress tool’larıyla kolayca entegre olması. Istio, Linkerd, Nginx Ingress, Contour gibi tool’larla nasıl kullanılacağına dair güzel tutorial dokümanları var:
https://docs.flagger.app/tutorials/istio-progressive-delivery
Şimdi Flagger’ı Nginx Ingress ile kurmaya ve kullanmaya başlayalım.
Flagger — Nginx Ingress Kullanımı
Flagger’ı kurmak için Helm repo’sunu Helm kurulumumuza ekleyip kurulum komutunu çalıştıralım:
#Add repo
helm repo add flagger https://flagger.app#Install flagger
helm upgrade -i flagger flagger/flagger \
--namespace ingress-nginx \
--set prometheus.install=true \
--set meshProvider=nginx
Daha sonra demo uygulamamızı kuralım. Ben demo uygulama kuruyorum ama siz kendi mevcut uygulamalarınızın üzerinde de deneyebilirsiniz. Bu uygulamanın tek özelliği sürekli kendisini çağırması. Dolayısıyla canary release sırasında trafik dağılımını daha kolay görebiliyorsunuz.
Uygulama için test namespace’ini oluşturup, altına deployment ve horizontal pod autoscaler’ımı kuruyorum:
kubectl create ns test
kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo
Yük testi yapmak için flagger’ın load tester uygulamasını kuruyorum, siz yine farklı bir uygulamayla da load test yapabilirsiniz.
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=test
Nginx Ingress Controller’da trafik önce Ingress Controller’a gelir, oradan Ingress kurallarınıza göre servislere dağılır.
AKS benim Ingress Controller’ımın LoadBalancer tipindeki servisine 52.236.159.38 adresini vermiş. Biraz sonra yapacağım Ingress tanımımın host kısmına bu IP’yi vermek istiyorum. Fakat Ingress benden domain name istiyor. Sizin uygulamalarınız için Ingress Controller’a verdiğiniz bir domain name varsa onu kullanabilirsiniz. Yoksa, benim yaptığım gibi, nip.io’ya kendi ip’nizi ekleyerek dns oluşturabilirsiniz: 52.236.159.38.nip.io
Ingress tanımımı yapıyorum:
Ingress tanımı yaptım fakar servis tanımı yapmadım. Bu tanımı benim için Flagger yapacak.
Canary Tanımı
Şimdi de Flagger’ın oluşturduğu Custom Resource Definition (CRD) ile Canary tanımımı yapıyorum:
Bu tanımda ne anlattığımıza bir bakalım.
targetRef’e podinfo deployment’ımı veriyorum, Flagger’ın bu deployment üzerinde çalışmasını istiyorum.
ingressRef’te az önce yarattığım ingress’i gösteriyorum.
autoscalerRef’te hpa tanımı gösteriyorum.
progressDeadlineSeconds’ı 60 saniye’ye çekiyorum, 1 dakika içinde deployment başarılı olmazsa rollback yapacak. Service bilgilerini klasik service tanımı gibi veriyorum.
interval
kaç saniyede bir sonraki adıma geçeceğimi belirtiyor.
threshold
kaç denemeden sonra fail durumuna karar verileceğini,
maxWeight
geçici canary pod’larını hangi yük seviyesine kadar deneyeceğimizi belirtiyor.
stepWeight
ise yük oranını her adımda yüzde kaç olarak arttıracağımızı belirtiyor.
metrics
hangi metriklerle canary analizinin yapılacağını belirtiyor. Burada başarılı requestlerin oranının >%99 olmasını istiyoruz. Fakat dilerseniz Prometheus Operator, New Relic gibi tool’lardan aldığınız metrikleri de kullanabiliyorsunuz. Fakat bu ayrı bir yazının konusu.
webhooks
zorunlu değil. Örneğin benim uygulamam çalışırken sürekli yenilendiği için yük testi yapmam şart değil. Fakat;
- Daha hızlı denemek için yük testi webhhok’u
(load-test)
- Geçici canary deployment’ının ayağa kalktığını kontrol etmek için pre-rollout webhook’u
(acceptence-test)
kullanıyorum.
Webhook’ları tamamen silip uygulamamı browser’dan açıp yükü kendim de yaratabilirim. Fakat eğer yeterince yük vermezsem Canary yeterince veri alamadığı için iptal olur.
Bu tanımı apply ettiğimde aşağıdaki tanımları Flagger üretecek:
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
ingresses.extensions/podinfo-canary
Artık benim stabil deployment’ım podinfo-primary. Bütün yüküm bu podlara gidiyor ve podinfo deployment’ım 0 replica’ya çekiliyor. Henüz yeni bir release başlatmadım. Bu değişiklik, deployment’ımı Flagger’a emanet ettiğimde Flagger tarafından yapılıyor. Hadi şimdi release başlatalım.
Canary Release
Yukarıdaki görselde v1'i podinfo-primary pod’larım, v2'yi ise podinfo pod’larım olarak düşünebilirsiniz.
- 1. İlk adımda, henüz release başlamadan, yük
podinfo-primary’ye geliyor.
- 2. Release başladığında (podinfo-canary ingress’i ve podinfo-canary service’i ile bağlı olan)
podinfo
deployment’ımın pod’u ayağa kalkıyor ve %5 yük bu deployment’a geçiyor.
- 3. Metrikler sorun göstermiyorsa, yük adım adım %50'ye kadar çıkartılıyor.
- 4. %50'den sonra hala sorun yoksa yeni v2 sınavı geçiyor ve
podinfo-primary
yeni versiyonla güncelleniyor.
- 5. podinfo-primary yeni versiyonla ayağa kalktıktan sonra yük tekrar podinfo-primary’ye geçiriliyor.
- 6. podinfo pod’ları artık ihtiyaç olmadığı için siliniyor ve podinfo deployment bir sonraki release’i beklerken 0 replica’ya dönüyor.
Tüm bu süreci aşağıdaki değişikliği yaparak deneyebilirsiniz:
kubectl -n test set image deployment/podinfo
\podinfod=stefanprodan/podinfo:3.1.1
İzlemek için de aşağıdaki iki komutu kullanabilirsiniz:
kubectl -n test describe canary/podinfo
watch kubectl get canaries -n test
Ben daha iyi anlamak için canary tanımımdaki progressDeadlineSeconds’ı 600 saniyeye, interval’ı 60 saniyeye çıkarttım. Böylece yük 10 saniyede değil, dakikada %5 arttı. Bu süreçte de Ingress Controller’ımın ip’sinden uygulamayı açarak yükün yavaş yavaş yeni versiyona (3.1.1) geçtiğini gördüm ve sonunda tüm uygulamanın yeni versiyona promote edildiğini gözlemledim.
Rollback
Eğer rollback’i de denemek istiyorsanız ve podinfo uygulamasını kullanıyorsanız, canary release sırasında aşağıdaki komutu çalıştırarak uygulamanın düzenli olarak 500 hatası almasını sağlayabilirsiniz. Flagger da threshold sayısı kadar fail olduğunda rollback olmasını sağlayacaktır:
watch curl http://52.236.159.38/status/500
Kaynaklar