Lesson 5.1: Volumes and Persistent Volumes (PV and PVC)
In Kubernetes, a Volume is a way to persist data across container restarts and share data between containers in a pod. Unlike container filesystems, which are ephemeral and tied to the lifecycle of the container, volumes provide a mechanism to store data outside the container. This is particularly useful for stateful applications like databases, caches, and file storage systems.
emptyDir Volume
In the redis.yml file, an emptyDir volume is used to store Redis data:
[root@master kubernetes]# cat redis.yml
apiVersion: v1
kind: Pod
metadata:
name: redis-pod
spec:
containers:
- image: redis
name: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
[root@master kubernetes]# kubectl apply -f redis.yml
pod/redis-pod created
- Volume Name: redis-storage
- Mount Path: /data/redis
- Type: emptyDir
- Behavior: The data stored in /data/redis is temporary and will be deleted when the pod is removed.
[root@master kubernetes]# kubectl exec -it redis-pod -- sh
# pwd
/data/redis
# cat hello.txt
Hello Kubernetes !
#
# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
redis 1 0.4 0.1 138496 3328 ? Ssl 22:16 0:25 redis-server *:6379
root 224 0.0 0.0 2316 512 pts/1 Ss 23:56 0:00 sh
root 235 0.0 0.2 8012 3456 pts/1 R+ 23:57 0:00 ps -aux
#
# kill -9 1
#
# cat hello.txt
Hello Kubernetes !
#
# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
redis 1 0.4 0.1 138496 3072 ? Ssl 22:16 0:25 redis-server *:6379
root 224 0.0 0.0 2316 640 pts/1 Ss 23:56 0:00 sh
root 237 0.0 0.2 8012 3584 pts/1 R+ 23:58 0:00 ps -aux
PersistentVolume (PV) and PersistentVolumeClaim (PVC)
In the pv.yml, pvc.yml, and pod.yml files, a PersistentVolume and PersistentVolumeClaim are used to provide persistent storage to an Nginx pod:
PersistentVolume (PV):
[root@master kubernetes]# cat pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: standard
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/home/kubernetes/storage-drive"
- Volume Name: task-pv-volume
- Storage Class: standard
- Capacity: 1Gi
- Access Mode: ReadWriteOnce (RWO)
- Type: hostPath (local storage on the node)
PersistentVolumeClaim (PVC):
[root@master kubernetes]# cat pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
- Claim Name: task-pv-claim
- Storage Class: standard
- Requested Storage: 500Mi
- Access Mode: ReadWriteOnce
Pod Using PVC:
[root@master kubernetes]# cat pod.yml
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
- Volume Name: task-pv-storage
- Mount Path: /usr/share/nginx/html
- PVC: task-pv-claim
[root@master kubernetes]# kubectl apply -f pv.yml
persistentvolume/task-pv-volume created
[root@master kubernetes]# kubectl apply -f pvc.yml
persistentvolumeclaim/task-pv-claim created
[root@master kubernetes]# kubectl apply -f pod.yml
pod/task-pv-pod created
[root@master kubernetes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
task-pv-volume 1Gi RWO Retain Bound default/task-pv-claim standard <unset> 11s
[root@master kubernetes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
task-pv-claim Bound task-pv-volume 1Gi RWO standard <unset> 10s
[root@master kubernetes]# kubectl get pod
NAME READY STATUS RESTARTS AGE
task-pv-pod 1/1 Running 0 9s
Key Points from the Example
-
emptyDir:
- Used in the redis-pod to store Redis data temporarily.
- Data is lost when the pod is deleted.
-
PersistentVolume (PV) and PersistentVolumeClaim (PVC):
- Used in the task-pv-pod to provide persistent storage for Nginx.
- The PV (task-pv-volume) is backed by a hostPath on the node.
- The PVC (task-pv-claim) requests 500Mi of storage from the PV.
- The pod mounts the PVC at /usr/share/nginx/html.
-
Binding:
- The PVC (task-pv-claim) is bound to the PV (task-pv-volume) because they share the same storageClassName (standard).
-
Persistence:
- Data written to /usr/share/nginx/html in the task-pv-pod will persist even if the pod is deleted, as long as the PV and PVC exist.
Example of PV and PVC with local-storage
# Create an index.html file on your Node
[root@master pv]# docker exec -it dev-worker bash
root@dev-worker:/# cat /mnt/disks/ssd1/index.html
<h1>Welcome to SSD DISK 1 </h1>
[root@master pv]# cat pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- dev-worker
[root@master pv]# cat pvc1.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-name
spec:
storageClassName: local-storage
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
[root@master pv]# cat pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: pvc-name
[root@master pv]# cat svc.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
[root@master pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
example-pv 1Gi RWO Retain Bound default/pvc-name local-storage <unset> 25m
[root@master pv]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-name Bound example-pv 1Gi RWO local-storage <unset> 25m
[root@master pv]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
nginx-service NodePort 10.96.134.181 <none> 80:32483/TCP 16m
[root@master pv]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 17m
[root@master pv]# kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
dev-control-plane Ready control-plane 10d v1.32.2 172.18.0.5 <none> Debian GNU/Linux 12 (bookworm) 5.14.0-390.el9.aarch64 containerd://2.0.2
dev-worker Ready <none> 10d v1.32.2 172.18.0.3 <none> Debian GNU/Linux 12 (bookworm) 5.14.0-390.el9.aarch64 containerd://2.0.2
dev-worker2 Ready <none> 10d v1.32.2 172.18.0.2 <none> Debian GNU/Linux 12 (bookworm) 5.14.0-390.el9.aarch64 containerd://2.0.2
[root@master pv]# curl 172.18.0.3:32483
<h1>Welcome to SSD DISK 1 </h1>