Windows Container’lar için Yararlı Bilgiler

Tahmin edebileceğimiz gibi genellikle zaman ve maliyet sebeplerinden dolayı mevcut uygulamaları lift & shift yaklaşımıyla cloud ortamına migrate etmeye çalışmak, bazen sandığımız kadar kolay bir şekilde gerçekleşmeyebiliyor. Özellikle .NET Framework ile geliştirmiş olduğumuz mevcut uygulamalarımızı containerize edilmiş olarak kubernetes gibi ortamlarda host etmek istiyorsak. Ayrıca çoğu zaman tahmin edemeyeceğimiz farklı challenge’ları da handle etmemiz gerekebiliyor.

https://www.ais.com/wp-content/uploads/2019/07/Lift-n-Shift-Approach-App-Modernization-768×279.jpg

Ben yaklaşık 1 yıl gibi bir süredir aktif olarak Windows Container’lar üzerinde çalışmaktayım. Bu süreçte bir çok farklı challenge’larla karşılaştık ve farklı çözümler deneyimledik. Bu makale kapsamında ise Windows container’lar ile ilgili bazı yararlı bilgileri ve notlarımı paylaşmaya çalışacağım.

Base Windows Container Image’inin Seçimi

Belki de en önemli konulardan birisi, base Windows container image’inin seçimi. Eğer Windows container’lar ile çalışmaya yeni başlıyorsanız, farklı base Windows container image türleri (Windows Server Core, Nano Server, Windows IoT Core, Windows) ve versiyonları sebebiyle bu süreç biraz kafa karıştırıcı olabiliyor.

Eğer mevcut uygulamalarımız full .NET Framework ihtiyacı duyuyorsa, Windows Server Core tabalı bir base image kullanmamız gerekmektedir.

Bir diğer nokta ise seçecek olduğumuz image’in support lifecycle’ı. Örneğin Windows server core için olan tag’lere buradan bakarsanız, “LTSC” ve “SAC” olmak üzere iki farklı release channel’ı olduğunu görebilirsiniz. Bu noktada ise “LTSC” (Long-Term Servicing Channel) olan tag’lerden birisini seçmemiz, her türlü faydamıza olacaktır.

Ayrıca seçenek olduğumuz container version’ının , host makinasının version’ı ile de uyumlu olması gerekmektedir. Örneğin container’larınızı benim gibi sizde Azure Kubernetes Service (AKS) üzerinde host etmek istiyorsanız, 2019 version tabanlı bir container image’i kullanmanız gerekmektedir. Çünkü AKS, host OS version’ı olarak Windows Server 2019 kullanmaktadır.

Detaylı bilgiye ise, buradan erişebilirsiniz.

Örneğin .NET Framework ile geliştirilmiş olan bir Web API’ı containerize etmek ve AKS’de host etmek istiyorsak, aşağıdaki gibi bir image kullanabiliriz.

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019

Eğer containerize edeceğimiz uygulama, içerisinde IIS gerektirmiyorsa da aşağıdaki gibi bir image’i kullanabiliriz.

FROM mcr.microsoft.com/windows/servercore:ltsc2019
FROM mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019

Diğer tag’lere ise, aşağıdaki link’lerden erişebilirsiniz.

Windows Container’ı Up-to-date Tutmak

Microsoft kullandığımız OS’lerde olduğu gibi belirli aralıklarla Windows container image’lerini de güncellemektedir. Bu güncellemeleri elde edebilmek için ise, uygulamalarımızın deployment anında her zaman güncel image’leri çekmelerini sağlamamız gerekmektedir. Böylece base image’in güncel security ve bug fix güncellemelerine sahip olduğundan emin olabiliriz.

Dockerfile’ı Hazırlamak

Özellikle mevcut uygulamalarımızın bazı third-party kütüphanelere bağımlılığı var ise, dockerfile’ı hazırlamak ve container’ı çalışır bir hale getirebilmek bazen can sıkıcı olabiliyor. Çünkü ilgili third-party kütüphanelerin silent installation command’larını bulmak ve istediğimiz gibi çalıştırmak bazen çok da kolay olmuyor.

Eğer uygulamalarımızın bazı kütüphanelere bağımlılıkları varsa, aşağıdaki gibi bir özel base image oluşturabilir ve o base image’i ilgili uygulamaları containerize etmek için kullanabiliriz.

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
WORKDIR /app

// Third-party dependencies
ADD ./AppPath/DependedLibrary.exe /app

// Silent installation
RUN ./DependedLibrary.exe /install /quiet

// Installation işleminden sonra silmeyi de unutmayalım
RUN DEL ./DependedLibrary.exe

Çalışan Bir Container’a Erişmek

Çalışan bir Windows container’a erişip -içerisinde bir command çalıştırmak istiyorsak, aşağıdaki gibi bu işlemi gerçekleştirebiliriz.

docker exec -it container_id cmd/powershell
kubectl exec -it container_id -- cmd/powershell

TLS’i Ayarlamak

Bir diğer challenged konulardan birisi de, uygulamaların secure communication’ı için TLS’i ayarlamak.

Örneğin .NET Framework ile geliştirilmiş olan mevcut bir uygulamamızın, TLS’i etkinleştirilmiş bir backing service’e bağlanması gerektiğini varsayalım. Bunun için ise root ca certificate’ini ilgili uygulama başlatılmadan önce Windows container’ın root store’una eklememiz gerekmektedir.

İlgili certificate’i ise “certoc.exe” tool’unu kullanarak, işletim sisteminin Trusted Root store’una ekleyebiliriz.

FROM mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019
WORKDIR /app
COPY . .
RUN powershell certoc.exe -addstore root path\\ca.crt
ENTRYPOINT ["MyConsoleApp.exe"]

Eğer bir ASP.NET Framework Web API container’ında SSL için bir PFX certificate’ini install etmek istiyorsak, işler burada biraz daha farklı gerçekleşiyor.

Bu noktada, PowerShell’in “Import-PfxCertificate” cmdlet’ini kullanarak certificate’i import etmemiz ve 443 port’una bind etmemiz gerekmektedir.

Bu işlemi ise aşağıdaki gibi gerçekleştirebiliriz.

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
COPY . /inetpub/wwwroot

RUN powershell.exe -Command "\
  Import-Module IISAdministration; \
  Import-Module WebAdministration; \
  $pwd = SOME_PASS_HERE | ConvertTo-SecureString -Force -AsPlainText; \
  $cert = Import-PfxCertificate -Exportable -FilePath 'C:\PFX_PATH\FILE' -CertStoreLocation cert:\localMachine\My -Password $pwd; \
  new-item -Path IIS:\SslBindings\0.0.0.0!443 -value $cert; \
  New-WebBinding -name 'Default Web Site' -Protocol https -Port 443; \
  Remove-WebBinding -Port 80; \
  c:\ServiceMonitor.exe w3svc"

Eğer kubernetes gibi bir platform’a deploy ediyorsanız, bu PowerShell command’lerini oluşturacağınız helm chart’ın “deployment.yaml” file’ı içerisinde tekrar kullanılabilir bir hale getirebilirsiniz.

Ayrıca password ile certificate’in oluşturulma aşamalarında da aşağıdaki gibi cert-manager’ın “Certificate” resource’undan ve kubernetes’in “Secret” objesinden yararlanabilirsiniz.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: {{ template "my-apis.fullname" . }}
spec:
  secretName: {{ template "my-apis.fullname" . }}-tls
  dnsNames:
    - {{ template "my-apis.fullname" . }}
  keystores:
    pkcs12:
      create: true
      passwordSecretRef:
        key: pfx-password
        name: {{ include "my-apis.fullname" . }}-pfx-secret
  issuerRef:
    name: ca-issuer
    kind: ClusterIssuer
  duration: 8760h

SSL’in test işlemini de SNI ile birlikte herhangi bir pod içerisinden aşağıdaki komut yardımıyla gerçekleştirebiliriz.

curl -k https://MY_API_HOST/PATH --resolve MY_API_HOST:443:SVC_IP

IIS Binding İşlemleri

Özellikle SSL işlemleri için container içerisindeki IIS binding’lerini listelemek istersek, aşağıdaki komutu kullanabiliriz.

powershell Get-WebBinding 'Default Web Site'

Log’ları Elde Etmek

Windows container içerisinde, Linux container’larda olduğu gibi “kubectl logs” komutu ile istediğimiz uygulama log’larını elde edebilmek çok da kolay olmuyor. Bu sebeple bazen uygulamanın neden çalışmadığını anlayabilmek için çok fazla vakit harcayabiliyoruz.

Neyseki Microsoft tarafından Windows container’lar için geliştirilmiş olan Log Monitor tool’unu kullanabiliriz. Log Monitor tool’u custom uygulama log’larını, ETW ve Event logs gibi source’ları monitor ederek, STDOUT için logları erişilebilir bir hale getirmektedir.

Böylece “kubectl logs” komutu ile log detaylarına ulaşabilir hale gelebiliriz.

Log Monitor tool’unun kullanımı hakkındaki bilgiye ise, buradan erişebilirsiniz.

Event Logs

Bazı durumlarda da sorunun nereden kaynaklandığını anlayabilmemiz için, Windows Event Log’larına bakmamız gerekmektedir.

Bunun için ise aşağıdaki komut satırlarını kullanabiliriz.

Get-EventLog -list
Get-EventLog application -newest 1

File Storage

Eğer mevcut uygulamalarımız bir shared file storage’a ihtiyaç duyuyorsa, bu konuda Azure File Share service’inden de yararlanabiliriz.

Azure file share, fully managed bir cloud file share service’idir. En güzel tarafı ise SMB desteği. Yani mevcut uygulamalarımız üzerinde refactoring işlemlerine gerek duymadan, azure file share’i istediğimiz bir container’a mount edebilmekteyiz.

Azure file share’i ise buradaki adımları takip ederek oluşturabilirsiniz.

Azure file share’i mount etmenin en basit yolu ise, helm chart’ın “deployment.yaml” file’ına aşağıdaki gibi implemente etmek.

apiVersion: apps/v1
kind: Deployment
metadata:
...
spec:
  ...
  template:
    ...
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: ...
          volumeMounts:
          - name: azurefileshare
            mountPath: /path
      volumes:
      - name: azurefileshare
        azureFile:
          secretName: storage-secret
          shareName: path
          readOnly: false
      ...

Ardından Azure file share’e erişim sağlayabilmek için ise, aşağıdaki gibi bir “storage-secret” file’ı oluşturmamız gerekmektedir.

apiVersion: v1
kind: Secret
metadata:
  name: storage-secret
  namespace: default
type: Opaque
data:
  azurestorageaccountname: Base64_Encoded_Value_Here
  azurestorageaccountkey: Base64_Encoded_Value_Here

Resource Request Limits

Özellikle içerisinde IIS barındıran bir Windows container kullanacaksak, IIS’i de göz önüne alarak container’ın allocate edeceği resource’u iyi hesaplamamız gerekmektedir.

Bununla ilgili bilgilere ise, buradan erişebilirsiniz.

Node Selector’u Ayarlamak

Windows container için bir helm chart hazırlarken, “nodeSelector” değerini “windows” olarak ayarlamamız gerekmektedir. Eğer bu parametreyi ayarlamazsak, pod, herhangi bir node üzerinde schedule edilebilir.

nodeSelector:
  kubernetes.io/os: windows

Health Checks’i Ayarlamak

Uygulamalarımızın kubernetes ortamında sağlıklı bir şekilde çalışabilmesi için bir diğer önemli konu da, “liveness” ve “readiness” probe’lerinin ayarlanması.

Özellikle Windows container’lar için “readiness” probe’unu daha dikkatli ayarlamamız gerekmektedir. Çünkü Windows container’ın trafiği kabul edebillir bir hale gelmesi, biraz daha fazla bir zaman gerektirebiliyor.

Referanslar

https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-base-images?WT.mc_id=DT-MVP-5003382
https://gokhan-gokalp.azurewebsites.net/kubernetes-for-production-some-useful-information/
https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility?tabs=windows-server-1909%2Cwindows-10-1909&WT.mc_id=DT-MVP-5003382

Gökhan Gökalp

Recent Posts

Event-Driven Architecture’larda Conditional Claim-Check Pattern’ı ile Event Boyut Sınırlarının Üstesinden Gelmek

{:en}In today’s technological age, we typically build our application solutions on event-driven architecture in order…

2 ay ago

Containerized Uygulamaların Supply Chain’ini Güvence Altına Alarak Güvenlik Risklerini Azaltma (Güvenlik Taraması, SBOM’lar, Artifact’lerin İmzalanması ve Doğrulanması) – Bölüm 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

9 ay ago

Identity & Access Management İşlemlerini Azure AD B2C ile .NET Ortamında Gerçekleştirmek

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 yıl ago

Azure Service Bus Kullanarak Microservice’lerde Event’ler Nasıl Sıralanır (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 yıl ago

.NET Microservice’lerinde Outbox Pattern’ı ile Eventual Consistency için Atomicity Sağlama

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 yıl ago