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.
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.
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.
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.
Ö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 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
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
Ö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'
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.
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
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
Ö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.
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
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.
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
{:en}In today’s technological age, we typically build our application solutions on event-driven architecture in order…
{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…
{: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.…
{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…
{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…
{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…