Cloud-native olarak geliştirdiğimiz uygulamalarımızın zaman zaman bir storage üzerinde bir takım data’lara erişebilmeleri, paylaşabilmeleri veya depolayabilmeleri gibi farklı ihtiyaçları olabilmektedir.
Bildiğimiz gibi containerize olarak Kubernetes üzerinde host ettiğimiz uygulamalarımız, doğası gereği ephemeral(kısa ömürlü) ve stateless‘dır. Yani persistent bir storage çözümü kullanmadan container içerisinde depolayacağımız herhangi bir data, container silinene kadar hayatta kalacaktır.
Bu makale kapsamında ise Azure‘un bizlere fully managed olarak sunduğu File shares hizmeti ile bir çok container tarafından erişilebilir persistent bir volume nasıl oluşturabileceğimize ve container’larımıza bu volume’ü nasıl mount edebileceğimize kısaca değineceğiz. Ayrıca oluşturacak olduğumuz persistent volume’ün, dışarıdan gelebilecek potansiyel güvenlik tehdit’lerine karşı nasıl güvenli bir hale getirilebileceğinden de kısaca bahsedeceğiz.
Başlamadan
Bu makale için aşağıdaki ortam ve tool’lar gerekmektedir.
- Azure Kubernetes Service
- Azure CLI
- Kubectl
Kubernetes Volumes
Kubernetes data kaybı gibi bir çok problemleri çözen iyi bir volume abstraction model’ine ve farklı volume type’larını kullanabilmemize olanak sağlayan bir yapıya sahiptir. Bu esnek yapısı ile pod’lar içerisinde farklı type’lara sahip volume’leri aynı anda kullanabilmekteyiz.
Volume’leri Kubernetes içerisinde Ephemeral ve Persistent olarak kategorize edebiliriz. Ephemeral’ın yaşam döngüsü bir pod’a bağlıyken, persistent’ın yaşam döngüsü ise herhangi bir pod’a bağlı değildir. Ek olarak Kubernetes volume’leri oluşturulurken statically olarak bir cluster administrator’ı tarafından veya dynamically olarak Kubernetes API‘ı tarafından oluşturulabilmektedir.
Bu makale kapsamında ise PersistentVolume ve PersistentVolumeClaim resource’larını kullanarak statically olarak oluşturulmuş Azure File shares hizmetini bir pod’a nasıl mount edebileceğimize bakacağız.
Azure File Share’i Oluşturalım
Azure Files bizlere SMB veya NFS protokolleri üzerinden erişilebilen, fully managed bir file share hizmeti sunmaktadır. Özellikle bir cloud ortamına migration sürecinde isek, Azure Files‘ın on-prem ortamlarımızda da çalışabilme esnekliği veya SMB protokolü üzerinden uygulamalarımıza mount edilebilmesi sayesinde uygulamalarımızın kolay bir şekilde cloud ortamına migrate olabilme süreçlerini kolaylaştırmaktadır.
Azure File shares hizmeti için öncelikle bir storage account oluşturmamız gerekmektedir. Ardından File shares‘i oluşturabileceğiz. Bu işlemleri Azure CLI üzerinden aşağıdaki gibi gerçekleştirelim.
az storage account create -n STORAGE_ACCOUNT_NAME -g RESOURCE_GROUP -l LOCATION --sku Standard_LRS
export AZURE_STORAGE_CONNECTION_STRING=$(az storage account show-connection-string -n STORAGE_ACCOUNT_NAME -g RESOURCE_GROUP -o tsv) az storage share create -n STORAGE_ACCOUNT_NAME --connection-string $AZURE_STORAGE_CONNECTION_STRING
Microsoft Defender for Storage
Şimdi ise oluşturmuş olduğumuz File shares hizmetini, dışarıdan gelebilecek tehdit’lere, istenmeyen erişimlere karşı Microsoft Defender ile nasıl güvenli bir hale getirebileceğimize hızlıca bir bakalım.
Microsoft Defender, hem Azure-native hem de hybrid ortamlarımızdaki güvenlik tehditlerine karşı bir korumaya sahip olabileceğimiz gelişmiş bir tehdit koruma tool’udur. Biz oluşturmuş olduğumuz File shares hizmetini koruma altına alabilmek için ise, Microsoft Defender’ın storage için olan native akıllı güvenlik katmanından yararlanacağız.
az security atp storage update --resource-group RESOURCE_GROUP --storage-account STORAGE_ACCOUNT_NAME --is-enabled true
Zararlı içeriklere karşı koruma açısından hoşlanmadığım yönü ise, upload edilen her bir dosyayı tek tek taramamasıdır. Onun yerine Azure Blob Storage ve Azure Files hizmetleri tarafından üretilen telemetry verilerini analyze ederek, bilinen virüs veya trojan türleri için hash scanning yapmaktadır. Ayrıca SMB protokolü üzerinden yapılan upload işlemlerini de şimdilik desteklememektedir.
PersistentVolume ve PersistentVolumeClaim ile Çalışmak
PersistentVolume, normal bir volume’ün aksine herhangi bir pod’dan bağımsız olarak kendi lifecycle’ına sahip olan bir Kubernetes storage resource’udur. PersistentVolumeClaim ise bir pod’a PersistentVolume‘ün mount edilebilmesi için kullanılmaktadır.
Kısacası bir uygulamanın persistent bir storage’a ihtiyacı olduğunda, ilgili kullanıcı PersistentVolumeClaim‘i kullanarak arka plandaki storage hakkında detaylı bir bilgi sahibi olmadan ilgili uygulama için bir storage request edebilmektedir.
STORAGE_KEY=$(az storage account keys list --resource-group RESOURCE_GROUP --account-name mydocumentstrg --query "[0].value" -o tsv) kubectl create secret generic storage-account --from-literal=azurestorageaccountname=mydocumentstrg --from-literal=azurestorageaccountkey=$STORAGE_KEY
Şimdi aşağıdaki içeriğe sahip “pv-pvc” adında bir yaml dosyası hazırlayalım.
apiVersion: v1 kind: PersistentVolume metadata: name: azurefile spec: capacity: storage: 5Gi accessModes: - ReadWriteMany azureFile: secretName: storage-account shareName: mydocumentstrg readOnly: false persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile spec: accessModes: - ReadWriteMany storageClassName: "" resources: requests: storage: 5Gi
Yukarıda kısaca neler yaptığımıza bir bakalım.
- PersistentVolume‘ün örnek olarak 5Gi lık bir kapasiteye sahip olduğunu belirttik.
- Read-write olarak birden fazla node tarafından PersistentVolume‘ün mount edilebilmesi için “accessModes” seçeneğini ise “ReadWriteMany” olarak ayarladık.
- Storage plugin’i olarak önceden oluşturmuş olduğumuz Azure Files hizmetini kullanacağımız için, File shares hizmetinin “shareName” ve “secretName” bilgilerini de referans olarak gösterdik.
- Ayrıca “persistentVolumeReclaimPolicy” seçeneğini ise “Retain” olarak belirledik. Böylece PersistentVolumeClaim silindiğinde PersistentVolume ilgili data’lar ile beraber silinmemiş olacak.
Kısacası PersistentVolume ile cluster içerisinde 5Gi lık kapasiteye sahip bir storage resource’u oluşturmuş olacağız.
PersistentVolumeClaim‘i ise bir developer olarak istediğimiz bir pod spec’inde referans olarak göstererek, 5Gi kapasiteye sahip bir persistent storage request etmek için kullanacağız. Ayrıca “storageClassName” satırını boş set ederek, storage’ın dynamically olarak oluşturulmamasını da sağlamış olduk.
Eğer storage account’un static olarak değilde Kubernetes tarafından dynamically olarak oluşturulmasını isteseydik, PersistentVolume resource’u yerine istediğimiz kriterlere sahip bir StorageClass resource’u oluşturmamız ve bu resource’u kullanacak bir PersistentVolumeClaim tanımlamamız gerekecekti.
NOT: Eğer PersistentVolumeClaim‘in önceden oluşturmuş olduğumuz specific bir PersistentVolume ile bind olmasını istiyorsak, “volumeName” attribute’ünü set ederek bu işlemi gerçekleştirebiliriz. Örneğin farklı scalability ve performance tier’larına sahip olan hizmetlere sahip olabiliriz.
Şimdi aşağıdaki komutları terminal üzerinden execute edelim ve oluşturulan resource’lara bir göz atalım.
kubectl apply -f pv-pvc.yaml
kubectl get pv azurefile kubectl get pvc azurefile kubectl describe pvc azurefile
Yukarıda da gördüğümüz gibi azurefile PersistentVolumeClaim‘i, azurefile PersistentVolume‘üne başarılı bir şekilde “bound” oldu. Eğer “Used By” kısmına dikkat edersek, henüz herhangi bir pod tarafından referans edilmediği için “none” olduğunu görebiliriz.
Şimdi örnek uygulamamızı deploy etme aşamasına geçebiliriz.
Ben örnek olması açısından .NET 6 ile oluşturmuş olduğum aşağıdaki basit console uygulamasını Kubernetes üzerine deploy edeceğim.
using (StreamWriter writer = File.CreateText("/mnt/azure/mytext.txt")) { await writer.WriteLineAsync("hello"); } await Task.Delay(TimeSpan.FromHours(1));
Top-level statements ile hızlı bir şekilde küçük ve basitleştirilmiş uygulamalar geliştirebilmek, ne kadar güzel değil mi? Bu basit uygulama ile, “/mnt/azure/” path’i altında basit bir txt dosyası oluşturacağız. Bu path’e ise birazdan File shares hizmeti mount edeceğiz.
Dockerfile dosyası ise aşağıdaki gibidir.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyDocumentConsoleApp/MyDocumentConsoleApp.csproj", "MyDocumentConsoleApp/"] RUN dotnet restore "MyDocumentConsoleApp/MyDocumentConsoleApp.csproj" COPY . . WORKDIR "/src/MyDocumentConsoleApp" RUN dotnet build "MyDocumentConsoleApp.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyDocumentConsoleApp.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyDocumentConsoleApp.dll"]
Öncelikle aşağıdaki gibi basit bir deployment spec’i tanımlayalım.
apiVersion: apps/v1 kind: Deployment metadata: name: mydocumentapp-deployment labels: app: mydocumentapp spec: replicas: 1 selector: matchLabels: app: mydocumentapp template: metadata: labels: app: mydocumentapp spec: containers: - name: mydocumentapp image: YOUR_ACR.azurecr.io/mydocumentapp:v1 volumeMounts: - name: azurefileshare mountPath: /mnt/azure volumes: - name: azurefileshare persistentVolumeClaim: claimName: azurefile
Örnek uygulamamızı deploy edebilmek için tanımlamış olduğumuz bu basit spec’de, “volumes” bölümü altında daha önce oluşturmuş olduğumuz “azurefile” isimli PersistentVolumeClaim‘i kullanarak bir volume request ediyoruz. Ayrıca bu volume’ü container içerisinde mount etmek istediğimiz path’i de belirtiyoruz.
Böylece Kubernetes control plane, bu pod için PersistentVolumeClaim içerisinde tanımlamış olduğumuz kriterlere sahip bir PersistentVolume bulmaya çalışacaktır. Başarıyla uygun bir PersistentVolume bulduğunda ise, PersistentVolumeClaim‘i ilgili volume’a bind edecektir. Aksi bir durumda ise uygun bir PersistentVolume bulunamayacağı için ilgili pod schedule edilemeyecektir.
Şimdi aşağıdaki gibi uygulamayı deploy edelim ve container içerisindeki “/mnt/azure/mytext.txt” dosyanın içeriğini kontrol edelim.
kubectl apply -f ./mydocumentapp-deployment.yaml kubectl exec -it $(kubectl get pods -l=app=mydocumentapp --output=jsonpath={.items..metadata.name}) -- cat /mnt/azure/mytext.txt
Gördüğümüz gibi uygulamamız mount ettiğimiz File shares hizmeti üzerinde tanımladığımız “mytext.txt” dosyasını başarıyla oluşturmuş. Artık oluşturulan bu dosya, persistent bir şekilde Azure File shares hizmeti üzerinde barındırılmaktadır. İlgili uygulamalar tekrardan schedule veya scale out/down edilse dahi, aynı File shares erişilebilir durumda kalacaktır.
Şimdi oluşturmuş olduğumuz PersistentVolumeClaim‘i aşağıdaki gibi tekrardan describe edelim.
kubectl describe pvc azurefile
“Used By” kısmına şimdi baktığımızda ise, bu claim’in oluşturmuş olduğumuz pod tarafından kullanıldığını görebiliriz.
Toparlayalım
Kubernetes içerisindeki pod’ların, doğası gereği kolay silinebilen yani kısa ömürlü olduklarını söyledik. Pod’lar herhangi bir sebepden dolayı içlerindeki local data’lar ile birlikte silinebilir, tekrardan schedule edilebilirler. Bu sebeple pod’lar arasında paylaşılacak veya persistent olarak barındıralacak data’lar için, persistent volume’ler kullanmamız gerekmektedir.
Bu makale kapsamında persistent bir storage’a ihtiyaç duyan uygulamalarımız için, managed olarak önceden oluşturmuş olduğumuz Azure File shares hizmetini PersistentVolume ve PersistentVolumeClaim yaklaşımıyla uygulamalarımıza nasıl mount edebileceğimize kısaca bir göz attık.
PersistentVolume cluster administrator’ı tarafından cluster içerisine bir storage resource’u eklemek için kullanılırken, PersistentVolumeClaim ise bir developer’ın arkaplanda ne olup bittiğini bilmeden soyut bir şekilde uygulamasına persistent bir storage request edebilmesi için kullanılmaktadır.
Referanslar
https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction
https://docs.microsoft.com/en-us/azure/defender-for-cloud/defender-for-storage-introduction
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
thanks for sharing
Thanks.
There are articles and articles and then there is that that just does the job!!!
Thank you so much!!!!