background

Flagger ve Nginx Ingress ile Canary Release

Flagger ve Nginx Ingress ile Canary Release

Modern dünyada web uygulamaları hayatımızın her yerinde. Market, giyim, oyun vb. hemen her şeyin alışverişini online yapabiliyoruz. Alışveriş dışında okul, haber, kitap gibi temel günlük ihtiyaçlarımızı da online karşılamaya başladık. Taksi çağırma, araba kiralama, dava durumunu kontrol etme gibi detay işler bile online yapılabiliyor.

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

Nasıl yardımcı olabiliriz?