Saturday, April 1, 2023

Azure Kubernetes Service: Using Azure Files as a Persistent Volume

In this post, let's see how we can use Azure Files as a Persistent Volume in Azure Kubernetes Service (AKS).

Preparation

In Azure, I already have an AKS and a Storage Account created. In the Storage Account, I have created a File Share named k8-file-share.

File Share
And then I have created a simple ASP.NET Core Minimal API with just 2 endpoints, and using those I can read and write to application storage.
using System.Text;
using System.Text.Json;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

WebApplication app = builder.Build();

app.UseHttpsRedirection();

var dataPath = Path.Combine("files""azure""customers.json");

app.MapGet("/customers", () =>
{
    List<Customer>? customers = GetCustomers();

    return customers;
});

app.MapPost("/customers", (Customer customer) =>
{
    List<Customer>? customers = GetCustomers();
    customers.Add(customer);

    string jsonString = JsonSerializer.Serialize(customers, new JsonSerializerOptions()
    {
        WriteIndented = true
    });

    using StreamWriter streamWriter = new(dataPath);
    streamWriter.Write(jsonString);

    return customer;
});

app.Run();

List<Customer> GetCustomers()
{
    if (!File.Exists(dataPath))
    {
        using FileStream fileStream = File.Create(dataPath);
        byte[] info = new UTF8Encoding(true).GetBytes("[ ]");
        fileStream.Write(info, 0, info.Length);
    }

    using StreamReader streamReader = new(dataPath);
    string json = streamReader.ReadToEnd();
    return JsonSerializer.Deserialize<List<Customer>>(json)!;
}

internal record Customer(string FirstNamestring LastName) { }

I have containerized this and have it available as a Docker image.

Now let's run the above application in AKS and use the Azure File Share (k8-file-share) as its Persistent Volume.

Start

First, let's start by creating a K8s namespace for the demo.

apiVersion: v1
kind: Namespace
metadata:
  name: demo-file-share

Now I am creating a K8s secret to maintain our Storage Account name and its key. I am naming it azure-storage-account-secret.

apiVersion: v1
kind: Secret
metadata:
  name: azure-storage-account-secret
  namespace: demo-file-share
stringData:
  azurestorageaccountname: <storage-account-name>
  azurestorageaccountkey: <storage-account-key>

Now I am creating a K8s PersistentVolume (PV).

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-file-share
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: azurefile-csi
  csi:
    driver: file.csi.azure.com
    readOnly: false
    volumeHandle: pv-file-share-001  # make sure this volumeid is unique for every identical share in the cluster
    volumeAttributes:
      resourceGroup: rg-k8s-storage-demo  # optional, only set this when storage account is not in the same resource group as node
      shareName: k8-file-share
    nodeStageSecretRef:
      name: azure-storage-account-secret # secret name and it's namespace
      namespace: demo-file-share
  mountOptions:
    - dir_mode=0777
    - file_mode=0777
    - uid=0
    - gid=0
    - mfsymlinks
    - cache=strict
    - nosharesock
    - nobrl

Here make sure to update resourceGroup, shareName, and nodeStageSecretRef.name/namespace as necessary. Something to note here, PVs are cluster-scoped resources.

Now let's create a K8s PersistentVolumeClaim (PVC) to use the above PV.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-file-share
  namespace: demo-file-share
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: azurefile-csi
  volumeName: pv-file-share
  resources:
    requests:
      storage: 1Gi

Here volumeName should be the name of the PersistentVolume.

And finally, I have the following deployment, with a spec that uses the above PVC for my application.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-bridge-file-share
  namespace: demo-file-share
spec:
  selector:
    matchLabels:
      app: api-bridge-file-share
  template:
    metadata:
      labels:
        app: api-bridge-file-share
    spec:
      containers:
        - name: api-bridge-file-share
          image: informaacr.azurecr.io/demo/azure-file-share/api:dev
          imagePullPolicy: Always
          volumeMounts:
            - name: vol-file-share
              mountPath: /app/files/azure
      volumes:
        - name: vol-file-share
          persistentVolumeClaim:
            claimName: pvc-file-share

Here, I have defined a Volume pointing to PVC. And inside the container, I am mounting the PVC under my desired location (/app/files/azure, the location my API is using).

And that's about it. Now I can make a deployment to AKS. I am going to expose my pod via Service so I can access the API, but I am not sharing it here.

Once deployed, let's describe the pod and see whether the volume mounts are all good.
kubectl describe pod
Now let's try the endpoints out. First I am doing an HTTP GET to /customers endpoint. And I can see the file is getting created.
File Share
Now let's write some data by doing an HTTP POST to /customers endpoint. And I can see the data is being written out.
customers.json
Hope this helps. You can find the complete code sample here,
   https://github.com/jaliyaudagedara/aks-examples/tree/main/storages/azure-file-share

More read,
   Create and use a volume with Azure Files in Azure Kubernetes Service (AKS)

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment