Azure Kubernetes Service(AKS), Kubernetes platformunun Azure üzerindeki yansıması. Malum artık Kubernetes son birkaç yılın oldukça tercih edilen bir platformu. Günümüzün uygulama geliştirme ihtiyaçları ile paralel gelişen, “microservices”, “container” gibi yaklaşımlar ile hızlı değişiklikleri kolay gerçekleştirmeyi sağlayan bir platform olması, günümüzdeki belli kalitedeki çözümlerin temel taşlarından biri olmasını sağladı. O zaman biz de şimdi Azure üzerindeki servisler ile, Kubernetes’den nasıl faydalanabiliriz buna bir bakalım.

Azure Kubernetes Service üzerinde, Azure DevOps ile uygulamalarımızı nasıl dağıtırızı, temel noktaları ile anlatmaya çalışacağım. Hem Kubernetes’i anlamak, hem de Azure servisleri ile tanışmak için güzel bir başlangıç olur umarım. Birkaç anahtar kelime ile de bazı konular için kapı açan bir yazı olur umarım. Yol biraz uzun, çok zaman kaybetmeden başlayalım hemen…

Azure Kubernetes Service dedin de, bu ne alaka…

İlk adımımız Azure Kubernetes Service(AKS) tarafında host edilecek, yönetilecek Docker Container’larımızı saklayacağımız Azure Container Registery(ACR)’i oluşturmak. Bu depodan Container’lar alınıp, imaj şeklinde AKS tarafında çalışacak.

ACR’ı oluşturmak portal üzerinden oldukça basit, aşağıdakine benzer şekilde temel bilgiler ile geliştirme ve test amaçlı ACR’ı oluşturabiliriz.

Azure Kubernetes Service’i kuralım bakalım…

Şimdi sıra geldi Azure Kubernetes Service(AKS) ortamını oluşturmaya. Portal’de tepedeki kutucuğa direkt kubernetes diye yazarak servislere yöneliyoruz. Tabi ki daha henüz hiçbir AKS ortamımız olmadığı için “Add” deyip, “Add Kubernetes Cluster” ekliyoruz.

Aşağıdaki gibi belli adımlardan oluşacak bir ekran ile AKS’yi oluşturmaya başlayabiliriz. İlk temel bilgilerin olduğu ekran bir çok standart Azure servisi oluşturmak için benzer şeyleri soruyor bize. Burada AKS özelinde önemli olan Node bilgileri. Kubernetes için bir cluster daha da basit olması için; bir “kümeler” yapısı diyebilirim. Bu kümelerin çalışma gücü(Node Size) ve küme adeti(Node Count) bu ilk ekrandan belirleniyor. AKS’nin ücretlendirmesinde direkt etki ettiği için bu ilk ekran önemli. Şimdilik en düşük size’ı seçerek ilerleyelim.

“Next: Node Pools” ile ikinci adıma geçiyoruz. Daha hala AKS ortamımızı yaratmadık.

Bu adımda Virtual nodes ve VM scale set özelliklerini, bizim senaryo için disable ederek ilerliyoruz. Farklı ölçeklendirme ihtiyaçlarımız ve özellikle Windows Server VM’leri de tercih etmemiz gerekirse bu adımda tanımlayabiliyoruz.

Daha sonra “Next: Authentication” ile devam ediyoruz.

Bu adım da önemli bir adım. Burada AKS servisimize, diğer Azure kaynakları için yetkilendirme yapısını tanımlıyoruz. Yani AKS için bir “Service Principal” oluşturuluyor ve rol odaklı bir erişim kontrolü(RBAC) ile AKS’ye kaynaklar için yetki verileceği tanımını yapıyoruz. İlerleyen kısımlarda AKS’nin ilgili Container’lara erişmesi için ACR üzerinde yetki vereceğiz bu sayede.

“Next: Networking” diyerek ilerliyoruz. Hala daha AKS ortamını tamamlamadık.

Bu yazıdaki senaryomuz için cluster yapımızda özel bir network gereksinim olmadığı için, buraya çok dokunmadan ilerliyoruz. Ama cluster’daki temel network yapısının tanımlarını buradan yapıyoruz. AKS ortamı oluşurken, Kubernetes’in network gereksinimleri de ayrı birer Azure kaynağı olarak oluşuyor. Daha sonra gerektiğinde bu kaynaklar üzerinde değişiklikler yapabiliriz.

“Next: Integrations” diyip bir sonraki adım ile ilerliyoruz.

Burada AKS ile entegre çalışacak temel yapıların ayarını yapıyoruz. Biri Azure Container Registery(ACR) diğeri de Azure Monitor.

Authentication adımında, service principal ile ilerlediğimiz için bu kısımda direkt ACR ile alakalı bir ayar yapamıyoruz. Eğer Authentication adımında “System-assigned Managed Identity” seçseydik, bu adımda bir tane de ACR oluşturmamız mümkün olabilirdi. Ama zaten ilk adımda ACR oluşturduğumuz için gerek yok. Önemli diğer kısım Azure Monitor. Oluşacak ASK yapısındaki çeşitli metrikleri ve çalışacak imajlar ile ilgili bilgileri Azure Monitor’e aktarıp takip edebiliyoruz. Ama bizim senaryomuz için gerekli olmadığı için Disabled diyip ilerliyoruz.

“Review + create” diyip artık ASK ortamımızı yaratabiliriz.

“Create” dedikten sonra, 3-5dk. içinde AKS ortamımız hazır olacak. Gerekli tüm Azure kaynaklarının oluşturulması tamamlandıktan sonra AKS’ye bir göz atalım.

Bizi karşılayan ilk ekranda AKS ile ilgili temel bilgileri görebiliriz.

Sol taraftaki menü gibi olan yerden AKS’nin diğer özelliklerine göz atmakta fayda var. Burada önemli olan kısım “Kubernetes resources” altındaki kısımlar. Daha doğrusu diğer kısımlar da önemli de, ben direkt onlardan da bahsetmek istiyorum. Kubernetes ortamımızda çalışan bileşenlere bu resources altından ulaşabiliyoruz.

“Workloads”’a baktığımızda bazı AKS içinde olan bazı bileşenleri göreceğiz. Bunlar Kubernetes’in ve Kubernetes’in Azure ortamında çalışmasını sağlayan default bileşenler. Henüz kendi uygulamamızı koymadığımız için tanıdık bir şey göremeyeceğiz.

Benzer şekilde “Services…” kısmına baktığımızda da yukardaki bileşenlerin çalışan servislerini göreceğiz.

Kabaca Azure üzerindeki Kubernetes yapımızı tanımış olduk. Kubernetes üzerinde çalışacak uygulamalarımıza geçmeden önce, Azure Resource Group’larında otomatik olarak oluşan bir grup dikkatinizi çekecektir.

Azure üzerinde Kubernetes’in çalışması için gereken tüm diğer bileşenler, bu “Resource Group”’u içerinde bulunuyor.

Direkt bu resource’lar üzerinde değişiklik yapmak pek doğru olmayacaktır. Kubernetes’in standart bazı çalışma durumları direkt etkilenir.

Azure Kubernetes Service’i tanımak için direkt Azure Portal üzerinden gerçekleştirdik kurulumu ama değişiklikleri yönetmek ve ortamları daha kolay yaratmak için Terraform, Pulumi gibi “infrastructure as code” çözümleriyle de AKS’yi oluşturmak oldukça kolay.

Eeee Azure Kubernetes Service’i kurduk, şimdi ne…

Şimdi Kubernetes’de çalışacak uygulamalara bir bakalım. Kubernetes, Container yapısı ile oluşturulmuş uygulamaların yönetimini, çalışmasını, ölçeklenmesini, dağıtımını sağlayan bir platform. Orkestrasyon gibi daha kısa ve afilli bir ifade de kullanabiliriz. Açıkcası başta da belirttiğim gibi Kubernetes özelinde bu konulara çok değinmeyeceğim. Başlı başına ayrı konular her biri. Ben biraz daha yukarıdan bakarak bu konulara giriş niyetinde ilerleyeceğim. Ama yazının sonunda vereceğim kaynaklardan daha derinlere dalın derim. Neyse..

Bir uygulamanın Kubernetes üzerinde çalışabilmesi için belli konfigürasyonlarının ve tanımlarının olması gerekiyor. Bu tanımlamalar ile yavaş yavaş ilerleyelim.

Öncelikle; bir ASP.NET Core Web Application ve ASP.NET Core Web API şeklinde 2 uygulama üzerinden örneklendirmeler ile ilerleyeceğim. Varsayılan dotnet şablonları ile oluşturulmuş uygulamalar oldukları için öyle aman aman uygulamalar değiller. Örneğimizde SampleAPP, SampleAPI’ı kendi içinde çağıran basit bir frontend, SampleAPI’da bir backend uygulaması.

Projemizin yapısı da aşağıdaki gibi;

  • SampleAPI – Bir Web API projesi, projemizdeki backend tarafına denk geliyor.
  • SampleApp – Bir web(Razor Pages) projesi, projemizdeki frontend tarafına denk geliyor.
  • k8s – Yukarıdaki uygulamaların Kubernetes ortamında tanımlanması için gerekli, konfigürasyon dosyaları için bir klasör. Adının bir önemi yok tabi ki ama içeriği önemli.

Az biraz anlaşılması için aşağıdaki gibi bir klasör yapısı ile gerekli konfigürasyon dosyalarını oluşturuyoruz.

Dikkat ederseniz katmanlar(frontend,backend) için hem deployment.yaml hem de service.yaml dosyalarım var. Öncelikle bunlar ne kabaca biraz bahsetmek isterim. Çok özet ve basit olacak ama ciddi anlamda Kubernetes için önemli kavramlar bu yüzden direkt Kubernetes ile alakalı dökümanlara mutlaka zaman ayırın derim.

AKS ortamımızı ayağa kaldırdıktan sonra, kabaca neler var neler yok bakmıştık yukarıda hatırlarsanız. Orada “Workloads”’lar altında Kubernetes’deki çalışan bileşenleri görmüştük. Bu örnekteki deployment.yaml dosyaları ile aslında Kubernetes için bu bileşenleri tanımlıyoruz. Kubernetes dünyasında Pod diye bir kavram var. Kubernetes üzerinde dağıtılabilir en küçük parçalar; aslında uygulama imajları. Bu deployment.yaml dosyaları ile bu dağıtılabilir küçük parçaları, AKS’ye tanımlamış oluyoruz.

Şimdi deployment_frontend.yaml dosyasının içeriğine bakarak biraz bu tanımlamaları anlayalım.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample
      tier: frontend
      track: stable
  template:
    metadata:
      labels:
        app: sample
        tier: frontend
        track: stable
    spec:
      containers:
      - name: sampleapp-container
        image: k8simagesregistery.azurecr.io/app:v1
        # image: k8simagesregistery.azurecr.io/app:#{tag}#
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: Development

Burada metada ile bu deployment’ın ismini tanımlıyoruz. spec: altında bu dağıtımın biraz daha derin özelliklerini belirtebiliyoruz. Kaç tane olacak, şablonu ne olacak gibi.. Şablon özelliklerinde de bu dağıtımda çalışacak uygulama imajını belirtiyoruz. Belli Container ayarlarını da bu dağıtım dosyası üzerinden yapabiliyoruz. Mesela ASPNETCORE_ENVIRONMENT ile uygulamanın Development ortamı gibi çalışacağını belirttim örnek olması adına.

Burada önemli olan hangi Container imajının da olacağını belirtiyor olmamız. Bu dağıtım dosyasındaki tanım ile Azure Container Registery’den v1 tag’i olan uygulamamız Kubernetes’e aktarılacaktır.

Bu dağıtım konfigürasyonları, uygulamaların Kubernetes üzerinde dağıtılacağını tanımlıyor ama nasıl sunulacağını belirtmiyor. Bunun için de servis tanımlamaları yapıp Kubernetes üzerinde sunumu belirtiyoruz. Bunun içinde frontend_service/service.yaml dosyasına bakalım.

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: sample
    tier: backend
  ports:
  - port: 8090
    targetPort: 80
  type: LoadBalancer

Burada bizim örneğimiz basit olduğu için çok önemli ifadeler yok. Ama type:LoadBalancer burada önemli. Bu şekilde, servisin bir LoadBalance üzerinde sunulacağını belirtmiş oluyoruz. AKS ortamında çalışacağı için, Azure Load Balancer üzerinde dağıtımı sunulacak. Bu sayede aslında external bir IP almasını ve erişilebilir olmasını da sağlamış oluyoruz. Belki biraz fazla basite indirgeyerek, daha kolay anlaşılması adına ifade etmeye çalıştım ama ilginizi çekiyorsa Kubernetes’in kendi iç dinamikleri ile daha derinlere inebilirsiniz.

Diğer *.yaml tanımlarıda bunlara benzer olduğu için tek tek bahsetmek istemiyorum. Uygulamalarımızın, AKS için gerekli olan en basit tanım yapıları da bu şekilde. Bu kadar yazdık ettik ama hala uygulamalar Kubernetes daha doğrusu Azure Kubernetes Service(AKS) üzerinde değil.

Bu arada bu yazıda geçen tüm kodlar ve Kubernetes dosyaları GitHub‘da mevcut, oradan bakabilirsiniz.

Azure Devops ile uygulamaları dağıtalım…

DevOps prensipleri günümüz uygulamalarının önemli özelliklerinden. CI/CD, performans, ölçekleme, ölçme gibi gibi çeşitli yaklaşımlar artık uygulamaların yaşam döngüleri içinde olmazsa olmaz. Özellikle “cloud-native” uygulamaların ve “microservices” mimari stili ile geliştirilen çözümlerin kalitesini olumlu yönde etkileyen prensipler. Her çözümün içinde olan daha doğrusu olması gereken bir kavram, DevOps prensipleri. O zaman Azure Kubernetes Service(AKS)’e uygulama dağıtımımızı, Azure DevOps ile de yaparak, kısmen bazı DevOps prensiplerine dokunarak yazının sonuna gelelim.

Şu an için elimizde olanları bir hatırlayalım;

  • Azure Container Registery
  • Azure Kubernetes Services ortamı
  • 1 tane ASP.NET Core Web (Frontend) uygulaması
  • 1 tane ASP.NET Web API (Backend) uygulaması
  • Kubernetes için uygulama tanımlamaları

Son adımımız, Azure DevOps üzerinde CI/CD Pipeline’ları ile uygulamalarımızın, Docker imajlarını oluşturup, imajları ACR’e göndermek daha sonrada AKS üzerine uygulamalarımızı dağıtmak olacak. Kabaca hangi adımlar, nasıl oluyor bunları özetlemeye çalışacağım. Öncelikle Azure DevOps üzerinde bir Build Pipeline oluşturalım. Kabaca aşağıdaki gibi olsun;

Öncelikle ilk adımımız uygulamamızın ve gerekli tanım dosyalarımızı alması için kod depomuzu tanımlamak. GitHub üzerinden kodları alacak şekilde tanımımızı yapıyoruz. Daha sonra uygulamalarımızın Docker imajlarını oluşturalım. Bunun için Docker görevinden yararlanıyoruz. Bu imaj dosyalarını oluşturduktan sonra bunları docker push ile ACR’e gönderelim. Burada önemli olan kısım Image Name’i belirtirken, BuildId ile etiketliyoruz. Böylece her başarılı derleme yeni bir build olarak imajın oluşmasını sağlıyor.

Daha sonra Replace Tokens görevi ile, deploymentXXX.yaml dosyasında tanımladığımız bir ifadeyi değiştiriyoruz. Bu sayede Kubernetes’e yapılacak dağıtımın hangi Docker imajı olacağını net bir şekilde belirtmiş oluyoruz. Böylece her yeni versiyon sorunsuz bir şekilde Kubernetes’e dağılmış olacak.

........
....
... 
   spec:
      containers:
      - name: sampleapi-container
        image: k8simagesregistery.azurecr.io/api:#{tag}#
        imagePullPolicy: Always
        ports:
        - containerPort: 80

Daha sonra “Copy” görevi ile tüm k8s klasöründeki içeriklerin BuildArtifact olarak çıkması için kopyalıyoruz ve “Publish build artifact” görevi ile de Release adımı için ilgili yere çıkmasını sağlıyoruz.

Uygulamamızın Docker imajlarını oluşturup, Kubernetes için gerekli tanımları da dağıtım için hazır hale getirdik. Şimdi bu çıktıları Azure Kubernetes Service(AKS)’e iletelim.

Bunun için Azure DevOps üzerinde bu sefer bir tane Release Pipeline oluşturuyoruz.

Build adımında oluşturduğumuz çıktıları kullanacağını belirtip, Stage 1 adındaki aşamaları tanımlıyoruz. Bu aşamalar “kubectl” görevlerinden oluşuyor sadece.  

Bu görevlerin Azure üzerinde oluşturduğumuz AKS servisi üzerinde çalışması için “Kubernetes Service Connection” tanımlıyoruz.

Bu sayede kubectl görevi bu servis bağlantısı sayesinde bizim AKS ortamımızda çalışacak.

Kubernetes CLI  ile bir çok işlemi yapabiliyoruz. Çeşitli ayarlar ile kendi geliştirme ortamımızda da CLI komutları ile, geliştirdiğimiz bir imajı AKS üzerinde dağıtmak mümkün.

Azure DevOps üzerindeki kubectl görevide aslında tamamen bu. Kubernetes CLI’ını verdiğimiz parametreler ile Azure DevOps Pipelines üzerinde çalıştırabiliyoruz.

Yukarıdaki gibi tüm “kubectl” görevleri için ilgili *.yaml tanımlamalarını “apply” ediyoruz.

Release ve Build Pipeline’ların tetiklenme ayarlarını da tamamladıktan sonra ilk build işlemimizi çalıştıralım. 2-3dk. içinde hem Build, hem de Release Pipeline’ları başarılı bir şekilde tamamlanacaktır. Şimdi Azure tarafına dönüp, AKS ortamımıza bakalım…

AKS ortamında, Workloads kısmında gerçekleştirdiğimiz dağıtımları görebiliriz. Baktığımızda dağıtım dosyalarındaki ayarlara göre iki tane dağıtımı görebiliriz. fontend ve backend şeklinde iki tane dağıtım tamamlanmış…mı acaba?

Dikkat ederseniz birer uyarı göreceksinizdir. Turuncu üçgenler ile birer sorun olduğunu anlayabiliriz. Dağıtımların üstüne tıkladığımızda, ImagePullAcr gibi bir ifade dikkatinizi çekecektir.

Dağıtımların tanımlandığı deployment.yaml dosyasında AKS’nin, ACR’dan hangi imajları alacağını belirtmiştik. Hatırlarsanız, başlarda ACR üzerinde AKS’ye yetki vermemiz gerekiyor gibi bir şey demiştim. Ama unuttuk, Azure Container Registery(ACR) üzerinde AKS’ye yetki vermediğimiz için, AKS gerekli imajları ACR üzerinden çekemedi.

Şimdi hızlıca ACR’da, AKS’nin “Service Principal”’ına rol verelim. Bunun için oluşturduğumuz k8imagesresgistery’e gidip, “Access Control” kısmından, “Add a role assignment” diyip gerekli rolü verelim.

Burada AcrPull rolünü vermemiz yeterli olacaktır. Select kutucuğuna, Azure üzerinde hangi objeye vermek istiyorsak onu bulmak için AKS servisimizin ismini yazmak yeterli. XXXSP-20200101… gibi otomatik olarak oluşturulan “Service Principal”’ın ismini seçip Save dememiz yeterli olacaktır.

Şimdi artık ACR üzerinde AKS’nin de gerekli imajları alması için rolü yani yetkisi var. Azure DevOps üzerinden tekrardan yeni bir “Build” oluşturup, Azure Kubernetes Service’i tekrar kontrol edelim. 3-5dk. sonra yine her taraf yeşerince, Azure Portal üzerinden AKS’ye baktığımızda Workloads kısımda uyarı olan kısımların düzeldiğini göreceğiz.

Aynı şekilde Services… kısmına da baktığımızda, dağıtımlarımızın frontend ve backend diye servisler olarak da yapıldığını göreceğiz.

Burada dikkat edecek olursak, External IP kolonunda servislerimiz için IP adresleri olacaktır. Bu IP adreslerine ilgili portlar ile gidersek de uygulamalarımıza erişebiliriz.

Şimdi frontend uygulamamız da, yani ASP.NET Core Web App. yani SampleApp’de herhangi bir şekilde backend uygulamamızın, yani ASP.NET Core Web API’ın yani SampleAPI’ın herhangi bir bilgisini vermedik. Peki frontend uygulaması, backend uygulamasından nasıl haberdar oldu da, ekranda hava durumlarını gösterdi? 🤔

Kubernetes için container yapısındaki uygulamaların orkestrasyonunu sağlayan plaftorm demiştik. Kendi içinde oluşturduğu network yapısı, servis konfigürasyonları ve bunları paylaşması ile belli bir şekilde “service discovery” özelliğini de sağlıyor. Tabi ki arka tarafta bu 1-2 cümle kadar basit değil ama şimdilik bizim için yeterli olsun. Bu bağlamda SampleAPP uygulamamıza bir bakalım. Startup.cs tam olarak bakmamız gereken nokta;

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("backend", c =>
    {
        var host = Configuration["BACKEND_SERVICE_HOST"];
        var port = Configuration["BACKEND_SERVICE_PORT"];

        var uri = $"http://{host}:{port}";
        c.BaseAddress = new Uri(uri);
    });
    services.AddRazorPages();
}

Uygulamada HTTPClient üzerinden servise erişiyoruz. Çok standart ve klasik bir şekilde. Configuration üzerinden de BACKEND_SERVICE_HOST ve BACKEND_SERVICE_PORT değerleri ile, AKS üzerindeki backend servisinin adresine ulaşabiliyoruz. Burada AKS daha doğrusu Kubernetes bize bu bilgileri sunuyor. Dağıtım ve servis dosyalarındaki isimlendirmeler bu yüzden önemli. {isim}_SERVICE_HOST ve {isim}_SERVICE_PORT şeklinde ayarlar direkt ilgili servislerin bilgileri ile oluşuyor. Bu sayede bağlılıklar daha kolay yönetilir durumda oluyor. backend tarafındaki IP değişikliği için frontend uygulamasında herhangi bir değişiklik yapmadan işleyiş aynen devam edebiliyor. Bağlılıkların bu şekilde minimuma indirgenmesi, uygulamalardaki değişikliklerin de bağımsız olarak gerçekleştirilmesini sağlıyor diyerek artık yazıyı yavaştan bitirelim.

Günümüzün getirdikleri ile belli kalitede yazılım çözümleri sunmak için bazı yaklaşımları uygulamak gerekiyor. “Cloud-native”, “Kubernetes”, “Docker”, “DevOps”, “Microservices” gibi günümüzün artık önemli başlıklarına, çeşitli araçlara dokunarak değinmeye çalıştım. Uzun zamandır bu kadar uzun yazı yazmamıştım sanırım. Buraya kadar okuduysanız tebrik ederim ve asıl teşekkür ederim. Umarım faydamın dokunduğu ve merak uyandıran bir yazı olmuştur.

Bir sonraki yazıda görüşmek üzere, mutlu kodlamalar.

Kubernetes ile daha fazla haşır neşir olmak için de aşağıdaki kaynakları tavsiye ederim;

* Kubernetes Learning Path – https://azure.microsoft.com/en-us/resources/kubernetes-learning-path/
* Kubernetes Basics – https://www.youtube.com/playlist?list=PLLasX02E8BPCrIhFrc_ZiINhbRkYMKdPT
* Kubernetes Basics – https://kubernetes.io/docs/tutorials/kubernetes-basics/
* Azure Kubernetes Service – https://docs.microsoft.com/en-us/azure/aks/intro-kubernetes