Create and validate persistent storage

In this section, you’ll create a PersistentVolumeClaim (PVC), deploy a stateful NGINX application, and validate persistent storage behavior using OpenEBS LocalPV.

You’ll verify that data persists on the single-node cluster even after the application pod is deleted and recreated.

Create a PersistentVolumeClaim

Create a PersistentVolumeClaim (PVC) manifest:

    

        
        
cat > pvc.yaml <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: openebs-pvc
spec:
  storageClassName: openebs-hostpath
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
EOF

    

Apply the PVC:

    

        
        
kubectl apply -f pvc.yaml

    

The output is similar to:

    

        
        persistentvolumeclaim/openebs-pvc created

        
    

Verify that the PVC was created successfully:

    

        
        
kubectl get pvc

    

The output is similar to:

    

        
        NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       VOLUMEATTRIBUTESCLASS   AGE
openebs-pvc   Bound    pvc-4784909a-837e-457d-90aa-0aa6867f26de   5Gi        RWO            openebs-hostpath   <unset>                 134m

        
    

The PVC is dynamically provisioned by OpenEBS LocalPV.

Deploy a stateful NGINX application

Create the deployment manifest:

    

        
        
cat > nginx-openebs.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-openebs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-openebs
  template:
    metadata:
      labels:
        app: nginx-openebs
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: openebs-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: openebs-storage
        persistentVolumeClaim:
          claimName: openebs-pvc
EOF

    

Deploy the application:

    

        
        
kubectl apply -f nginx-openebs.yaml

    

The output is similar to:

    

        
        deployment.apps/nginx-openebs created

        
    

Verify Kubernetes resources

Check the pod status:

    

        
        
kubectl get pods

    

The output is similar to:

    

        
        NAME                             READY   STATUS    RESTARTS   AGE
nginx-openebs-84d6bfddd4-6rf5v   1/1     Running   0          133m

        
    

Check the PVC:

    

        
        
kubectl get pvc

    

The output is similar to:

    

        
        NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       VOLUMEATTRIBUTESCLASS   AGE
openebs-pvc   Bound    pvc-4784909a-837e-457d-90aa-0aa6867f26de   5Gi        RWO            openebs-hostpath   <unset>                 136m

        
    

Check the persistent volume:

    

        
        
kubectl get pv

    

The output is similar to:

    

        
        NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS       VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-4784909a-837e-457d-90aa-0aa6867f26de   5Gi        RWO            Delete           Bound    default/openebs-pvc   openebs-hostpath   <unset>                          134m

        
    

The output confirms that the persistent volume has been dynamically created and attached.

Write persistent data

Get the pod name:

    

        
        
POD=$(kubectl get pod -l app=nginx-openebs -o jsonpath='{.items[0].metadata.name}')

    

Write test data into the mounted volume:

    

        
        
kubectl exec -it $POD -- sh -c 'echo "OpenEBS on Azure Cobalt D4ps Arm64" > /usr/share/nginx/html/index.html'

    

Verify the data:

    

        
        
kubectl exec -it $POD -- cat /usr/share/nginx/html/index.html

    

The output is similar to:

    

        
        OpenEBS on Azure Cobalt D4ps Arm64

        
    

Validate persistence after pod recreation

Delete the NGINX pod:

    

        
        
kubectl delete pod -l app=nginx-openebs

    

Wait for Kubernetes to recreate the pod:

    

        
        
kubectl get pods -w

    

Press Ctrl + C after the new pod reaches the Running state.

Get the new pod name:

    

        
        
NEW_POD=$(kubectl get pod -l app=nginx-openebs -o jsonpath='{.items[0].metadata.name}')

    

Verify the data again:

    

        
        
kubectl exec -it $NEW_POD -- cat /usr/share/nginx/html/index.html

    

The output is similar to:

    

        
        OpenEBS on Azure Cobalt D4ps Arm64

        
    

This response confirms that the persistent volume retains data on the single-node cluster even after the pod is deleted and recreated.

Expose the NGINX application

Create a NodePort service to expose the NGINX application externally:

    

        
        
kubectl expose deployment nginx-openebs \
  --type NodePort \
  --port 80

    

Verify the service and note the NodePort assigned:

    

        
        
kubectl get svc

    

The output is similar to:

    

        
        NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.x.x.x       <none>        443/TCP        143m
nginx-openebs   NodePort    10.x.x.x       <none>        80:31635/TCP   7s

        
    

Note the NodePort value (in this example 31635). You’ll need it to create the Azure firewall rule in the next section. Your value might differ because Kubernetes assigns NodePorts dynamically.

What you’ve accomplished and what’s next

You’ve now successfully created dynamically provisioned persistent volumes using OpenEBS LocalPV on a single-node Kubernetes cluster running on an Arm-based Azure VM.

You’ve also validated persistent storage functionality by recreating application pods while preserving data across restarts, and exposed the application as a Kubernetes NodePort service.

Next, you’ll open the NodePort in the Azure Network Security Group so the application is reachable from your browser.

Back
Next