Share the love

This post is a tutorial on deploying Apache web server on Azure Kubernetes Service using persistent volumes. To use web server with several fronts, content is required to be served by all the instances. Persistent volumes are the resolution to overcome this overhead. We will use Azure Files for persistent volumes so that these volumes can be shared between more than one pod.

Here are the steps to be followed.

Create AKS Cluster

  • Create AKS cluster. For creation purposes, we will use AZ CLI, however, it’s up to you to choose your method.
az group create -n <resource-group-name> -l <location>
az aks create -n <aks-name> -g <resource-group-name> --node-count 1 --generate-ssh-keys
  • Connect to the AKS cluster
az aks get-credentials -n <aks-name> -g <resource-group-name>
create aks cluster using az cli

Create YAML for Persistent Volume, Deployment and Service

We need three important components: Persistent Volume Claim, Deployment and Kubernetes service. We will go through all the required YAML files to create these components.

Persistent Volume Claim

PersistentVolumeCalim is the resource type used to generate storage account dynamically. This will allow us to indicate the capacity we want to reserve for the shared folder and what Storage Class we want to use. For this example, since AKS already creates for us two StorageClass that cover both Azure Files and Azure Disk, I have chosen the corresponding one for what we need – Azure Files.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: http-pvc
spec:
  resources:
    requests:
      storage: 10Gi
  volumeMode: Filesystem
  storageClassName: azurefile
  accessModes:
    - ReadWriteMany

Deployment

We will create the deployment file to define pods that will run our Apache web server. Following code will deploy:

  • Three replicas
  • Mount the volume to /usr/local/apache2/htdocs
  • Pod with apache container
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: apache
          image: httpd:2.4
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: /usr/local/apache2/htdocs/
              name: html
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: http-pvc

Service

We will create service with type LoadBalancer. We will expose the same service to access our application.

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80

I have created a single file, named cieaks.yaml, containing all above 3 codes. Run kubectl command to deploy all these components

kubectl create -f cieaks.yaml

You should get an output similar to:

deployment.apps/apache-server created
service/web created
persistentvolumeclaim/http-pvc created

If everything goes as per the plan, you should see a storage account in Azure portal. You should look into the “MC” resource group.

Storage account created after deploying the PVC.

If you explore this storage account, you should see the File Share.

File Share created after creating the PVC in the AKS cluster.

Post this, you can also check the PVC we created earlier using kubectl command.

kubectl describe pvc http-pvc

You should see an output similar to mine:

kubectl describe pvc output

Above output shows that the volume is being used by three different pods. Since we added three replicas.

Time to check the final output.

Create a sample html file similar to following one. Upload the file(s) to the Azure Files we came across in above steps.

<html lang="en">
<head>
<title>My Sample Apache Page</title>
<meta charset="utf-8">
</head>
<body>
    My Apache test.
</body>
</html>
Upload the html file to Azure File share.

To browse the website/application we need the IP of ther “web” service. Run command to fetch services from your cluster.

kubectl get svc

You should see output similar to following:

kubectl-get-svc

Notice the public IP in the column “EXTERNAL-IP”? This is the IP of our web service. Take it, open a browser and browse! But here’s a problem – see it?

Bug in Apache while using SMB

output after uploading our html file to file share.

Notice the content “: bytes Content-Length: 142 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html“, before our sample text from html body?

In some cases, or browsers, you may face a problem wherein a file named “download” is downloaded rather than opening the expected output. This is due to a bug present in Apache. It affects the Apache server when SMB is used to mount folders, in our case we did Azure Files. It is caused by EnableMMAP property. EnableMMAP controls if Apache server uses memory-mapping to read content from a file during delivery. Setting this property to “Off” should solve the issue.

Lets turn it off and save it. Run following command:

docker run --rm httpd:2.4 cat /usr/local/apache2/conf/httpd.conf > httpd.conf

This command downloads the httpd.conf file by running a docker image “httpd” version 2.4. After this, uncomment line “EnableMMAP off”

EnableMMAP off

After making the change, lets create a config-map to use the file to do so.

kubectl create configmap apacheconf --from-file=httpd.conf

Output should be similar to this:

configmap/apacheconf created

Finally, modify the deployment yaml file to mount the above configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: apache
          image: httpd:2.4
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: /usr/local/apache2/htdocs/
              name: html
            - mountPath: /usr/local/apache2/conf/httpd.conf
              name: apache-config
              subPath: httpd.conf
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: http-pvc
        - name: apache-config
          configMap:
            name: apacheconf

Run the updated deployment:

kubectl apply -f cieaksdep2.yaml

Now, the issue should be resolved. If not, please drop it in the comments and we will check it together.