Use Ephemeral and Persistent Volumes in Pods

KubernetesBeginner
Practice Now

Introduction

Containers have writable filesystems, but that storage is tied to the container. Kubernetes volumes give Pods a clearer way to handle files. Some volumes are ephemeral and are meant for temporary data inside one Pod. Other volumes are persistent and can keep data available when a Pod is deleted and recreated.

In this lab, you will use an emptyDir volume for Pod-local scratch data, then create a PersistentVolumeClaim and mount it into two different Pods to prove that the data survives Pod replacement.

Prepare a Volume Practice Namespace

In this step, you will prepare a clean namespace and confirm that the cluster has the storage pieces needed for this lab.

Start in /home/labex/project. You will write Pod and PVC manifests in this directory.

mkdir -p /home/labex/project
cd /home/labex/project

Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.

kubectl get nodes --request-timeout=5s

Expected output includes:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Wait until the cluster networking Pod is Ready:

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

Expected output:

pod/calico-node-... condition met

This lab uses a PersistentVolumeClaim later. Confirm that the default standard StorageClass exists, then wait for the minikube storage provisioner Pod.

kubectl get storageclass standard
kubectl wait --namespace kube-system --for=condition=Ready pod/storage-provisioner --timeout=120s

Expected output includes:

NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ...
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           ...
pod/storage-provisioner condition met

Create a namespace named ckad-prep-volumes:

kubectl create namespace ckad-prep-volumes

Set this namespace as the current context namespace:

kubectl config set-context --current --namespace=ckad-prep-volumes

Confirm the current namespace:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

Expected output:

ckad-prep-volumes

Use emptyDir for Pod-Local Scratch Data

In this step, you will create a Pod that uses an emptyDir volume.

An emptyDir volume starts empty when the Pod is created. Containers in the same Pod can use it as shared scratch space, cache space, or a temporary working directory. It is not meant to preserve data after the Pod is removed.

Create emptydir-pod.yaml:

cat > emptydir-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: scratch-worker
  labels:
    app: scratch-worker
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: scratch-space
    emptyDir: {}
  containers:
  - name: worker
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo scratch-ready > /scratch/status.txt; sleep 3600'
    volumeMounts:
    - name: scratch-space
      mountPath: /scratch
EOF

Apply the manifest:

kubectl apply -f emptydir-pod.yaml

Expected output:

pod/scratch-worker created

Wait until the Pod is Ready:

kubectl wait --for=condition=Ready pod/scratch-worker --timeout=120s

Expected output:

pod/scratch-worker condition met

Read the file from the mounted emptyDir volume:

kubectl exec scratch-worker -c worker -- cat /scratch/status.txt

Expected output:

scratch-ready

Inspect the Pod's volume type. The jsonpath expression prints the volume name and confirms that it is an emptyDir.

kubectl get pod scratch-worker -o 'jsonpath={.spec.volumes[0].name}{" emptyDir\n"}'

Expected output:

scratch-space emptyDir

Write Data to a PersistentVolumeClaim

In this step, you will create a PersistentVolumeClaim and mount it into a writer Pod.

A PersistentVolumeClaim, or PVC, is an application request for storage. In this lab, the standard StorageClass dynamically provides storage for the claim. Your Pod mounts the claim at /data and writes a file there.

Create notes-pvc.yaml:

cat > notes-pvc.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: notes-pvc
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 1Mi
EOF

Apply the PVC:

kubectl apply -f notes-pvc.yaml

Expected output:

persistentvolumeclaim/notes-pvc created

Wait until the PVC is Bound:

kubectl wait --for=jsonpath='{.status.phase}'=Bound pvc/notes-pvc --timeout=120s

Expected output:

persistentvolumeclaim/notes-pvc condition met

Create a writer Pod that mounts the PVC:

cat > pvc-writer-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pvc-writer
  labels:
    app: pvc-writer
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: notes-storage
    persistentVolumeClaim:
      claimName: notes-pvc
  containers:
  - name: writer
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo persistent-note > /data/note.txt; sleep 3600'
    volumeMounts:
    - name: notes-storage
      mountPath: /data
EOF

Apply the writer Pod and wait until it is Ready:

kubectl apply -f pvc-writer-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-writer --timeout=120s

Expected output:

pod/pvc-writer created
pod/pvc-writer condition met

Read the file from the PVC mount:

kubectl exec pvc-writer -c writer -- cat /data/note.txt

Expected output:

persistent-note

Reuse the PVC from a New Pod

In this step, you will delete the writer Pod, create a new reader Pod that mounts the same PVC, and confirm the file is still there.

This is the practical difference between Pod-local scratch data and persistent storage. The writer Pod can be removed, but the PVC remains. A new Pod can mount the same claim and read the data.

Delete the writer Pod:

kubectl delete pod pvc-writer

Expected output:

pod "pvc-writer" deleted

Create a reader Pod that mounts the same PVC:

cat > pvc-reader-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pvc-reader
  labels:
    app: pvc-reader
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: notes-storage
    persistentVolumeClaim:
      claimName: notes-pvc
  containers:
  - name: reader
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'cat /data/note.txt; sleep 3600'
    volumeMounts:
    - name: notes-storage
      mountPath: /data
EOF

Apply the reader Pod and wait until it is Ready:

kubectl apply -f pvc-reader-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-reader --timeout=120s

Expected output:

pod/pvc-reader created
pod/pvc-reader condition met

Read the file from the new Pod:

kubectl exec pvc-reader -c reader -- cat /data/note.txt

Expected output:

persistent-note

Inspect the PVC. It should still be Bound, even though the original writer Pod is gone.

kubectl get pvc notes-pvc

Expected output includes:

NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
notes-pvc   Bound    ...      1Mi        RWO            standard       <unset>                 ...

Summary

You used an emptyDir volume for Pod-local scratch data, created a PVC for persistent storage, wrote data through one Pod, deleted that Pod, and read the same data from a new Pod that mounted the same claim. You now know when to use ephemeral storage for temporary Pod data and when to request persistent storage for data that should survive Pod replacement.