DEV Community

Akash for MechCloud Academy

Posted on

Part 8: My Data! My Precious! Handling State with Persistent Volumes

In our journey so far, we've deployed an application, configured it with ConfigMaps and Secrets, and exposed it to the world. Our application is configurable and accessible. But it has a fatal flaw: it is stateless.

The Nginx container we're running doesn't need to save any data. But what if we were running a database, a blog, or a user profile service?

By default, the filesystem inside a container is ephemeral. When a Pod crashes or is restarted, it's replaced with a brand new one with a fresh, empty filesystem. Any data saved in the old Pod is gone forever. This is a dealbreaker for almost any real-world application.

To solve this, Kubernetes has a powerful storage model that allows data to live independently from the lifecycle of a Pod.

The Kubernetes Storage Abstraction

To untangle the complexities of storage, Kubernetes provides a clear separation of concerns between the cluster administrator who provisions storage and the developer who consumes it. This is managed by three key objects.

Let's use an analogy: The Cloud Vending Machine.

  1. PersistentVolume (PV):

    • What it is: A PersistentVolume is a piece of storage in the cluster that has been provisioned by an administrator. It is a cluster resource, just like a Node is a CPU/memory resource.
    • Analogy: The PVs are the actual items stocked inside the vending machine (e.g., a "10GB Fast SSD" bar, a "100GB Slow HDD" coil). They are the real supply of storage.
  2. PersistentVolumeClaim (PVC):

    • What it is: A PersistentVolumeClaim is a request for storage by a user. It's like a Pod consuming Node resources; a PVC consumes PV resources.
    • Analogy: The PVC is you making a selection on the vending machine. You press the button for "B4," which corresponds to the "10GB Fast SSD." You don't care which specific bar it is, just that it meets your request.
  3. StorageClass:

    • What it is: A StorageClass provides a way for administrators to describe the "classes" of storage they offer. This allows for the dynamic provisioning of volumes.
    • Analogy: The StorageClass is the catalog on the front of the vending machine that says, "All items in row 'B' are Fast SSDs" and "All items in row 'D' are Slow HDDs." When you request a "Fast SSD," it knows where to look.

The workflow is simple:

  • As a user, you create a PVC manifest asking for a certain size and type of storage.
  • Kubernetes reads your claim and finds a PV that can satisfy it.
  • It then "binds" the PV to your PVC, reserving it for you.
  • You can then mount this claim into your Pod as a volume.

Hands-On: Giving Our Pod a Memory

Let's give our Nginx Pod a persistent place to store its website files.

Step 1: Create the PersistentVolumeClaim

First, we need to request the storage. Create a file named pvc.yaml.

# pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
Enter fullscreen mode Exit fullscreen mode
  • accessModes: This defines how the volume can be mounted. ReadWriteOnce means it can be mounted as read-write by a single Node. This is the most common mode.
  • resources.requests.storage: We are requesting 1 Gibibyte of storage.

Step 2: Update the Deployment to Use the Claim

Now we need to tell our Pod to mount and use this claimed volume. We do this by adding two sections to our deployment.yaml: a volumes list for the Pod and a volumeMounts list for the container.

Here is the complete, updated deployment.yaml:

# deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      # This section defines a volume named 'nginx-storage' that comes from our PVC
      volumes:
      - name: nginx-storage
        persistentVolumeClaim:
          claimName: nginx-pvc
      containers:
      - name: nginx-web-server
        image: nginx
        # This section mounts the volume into the container at a specific path
        volumeMounts:
        - name: nginx-storage
          mountPath: /usr/share/nginx/html
        envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: app-secret
Enter fullscreen mode Exit fullscreen mode
  • spec.volumes: This creates a named volume for our Pod called nginx-storage and specifies that its source is the PVC named nginx-pvc.
  • spec.containers.volumeMounts: This tells the nginx-web-server container to take the volume named nginx-storage and mount it at the path /usr/share/nginx/html. This is the directory where Nginx serves its default files.

Step 3: Apply and Verify

Let's apply our changes.

# Create the claim first
kubectl apply -f pvc.yaml

# Update the deployment
kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check the status of your claim:

kubectl get pvc
Enter fullscreen mode Exit fullscreen mode

You should see that its STATUS is Bound. This means Kubernetes has successfully provisioned a PV and attached it to our claim.

The Proof of Persistence

Now for the magic trick. We will write a file to our mounted volume, delete the Pod, and see if the file survives.

  1. Get your Pod's name:
kubectl get pods
# Find the name of your running 'hello-nginx' pod
Enter fullscreen mode Exit fullscreen mode
  1. Write a file inside the Pod's mounted volume: (Replace <your-pod-name> with the correct name)
kubectl exec <your-pod-name> -- bash -c 'echo "Persistence Works!" > /usr/share/nginx/html/index.html'
Enter fullscreen mode Exit fullscreen mode

We just overwrote the default Nginx welcome page with our own message.

  1. Delete the Pod to simulate a crash:
kubectl delete pod <your-pod-name>
Enter fullscreen mode Exit fullscreen mode
  1. Wait for the new Pod to be created:
    Run kubectl get pods again. You'll see the old Pod Terminating and a new one Running. Get the name of this new Pod.

  2. Check for the file in the new Pod:

kubectl exec <new-pod-name> -- cat /usr/share/nginx/html/index.html
Enter fullscreen mode Exit fullscreen mode

The output should be:

Persistence Works!
Enter fullscreen mode Exit fullscreen mode

It worked! The Deployment created a completely new Pod, but because our PersistentVolumeClaim was mounted, the data survived the "crash." Our application is now stateful.

What's Next

Our application is now running, configured, exposed, and stateful. But is it healthy?

Right now, Kubernetes knows if a Pod's container is Running, but it has no idea if the application inside the container has crashed or is stuck in a loop. It might be running, but unable to serve traffic.

In the next part, we'll learn how to give Kubernetes deeper insight into our application's health using Health Probes. We'll teach it how to know the difference between "alive" and "ready to receive traffic."

Top comments (0)