Kubernetes Persistent Volumes and Claims
Kubernetes Persistent Volumes and Claimes
PersistentVolume
Create a directory called /mnt/data on the k8s node. Within that directory, create index.html file with some text.
pradeep@learnk8s$ minikube ssh -p k8s
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
$ sudo mkdir /mnt/data
$ sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/indext.html"
$ cat /mnt/data/indext.html
Hello from Kubernetes storage
$ exit
logout
Now create a PersistentVolume named my-pv-volume of 1Gi using the hostPath pointing to the newly created directory. There are three types of accessModes, for this example use, RWO.
| RWO | ReadWriteOnce |
|---|---|
| RWX | ReadWriteMany |
| ROX | ReadyOnlyMany |
pradeep@learnk8s$ cat persistent-volume-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
Create the PV using this YAML file.
pradeep@learnk8s$ kubectl create -f persistent-volume-demo.yaml
persistentvolume/my-pv-volume created
Verify the available PVs.
pradeep@learnk8s$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-pv-volume 1Gi RWO Retain Available manual 4s
Describe the PersistentVolume for addtional details.
pradeep@learnk8s$ kubectl describe pv
Name: my-pv-volume
Labels: type=local
Annotations: <none>
Finalizers: [kubernetes.io/pv-protection]
StorageClass: manual
Status: Available
Claim:
Reclaim Policy: Retain
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /mnt/data
HostPathType:
Events: <none>
PersistentVolumeClaim
pradeep@learnk8s$ cat persistent-volume-claim-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 300Mi
pradeep@learnk8s$ kubectl create -f persistent-volume-claim-demo.yaml
persistentvolumeclaim/my-pv-claim created
pradeep@learnk8s$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pv-claim Bound my-pv-volume 1Gi RWO manual 40s
pradeep@learnk8s$ kubectl describe pvc
Name: my-pv-claim
Namespace: default
StorageClass: manual
Status: Bound
Volume: my-pv-volume
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: <none>
Events: <none>
pradeep@learnk8s$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-pv-volume 1Gi RWO Retain Bound default/my-pv-claim manual 12m
pradeep@learnk8s$ kubectl describe pv
Name: my-pv-volume
Labels: type=local
Annotations: pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pv-protection]
StorageClass: manual
Status: Bound
Claim: default/my-pv-claim
Reclaim Policy: Retain
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /mnt/data
HostPathType:
Events: <none>
The next step is to create a Pod that uses this PersistentVolumeClaim as a volume.
Here is the configuration file for the Pod:
pradeep@learnk8s$ cat my-pv-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-pv-pod
spec:
volumes:
- name: my-pv-storage
persistentVolumeClaim:
claimName: my-pv-claim
containers:
- name: my-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: my-pv-storage
pradeep@learnk8s$ kubectl create -f my-pv-pod.yaml
pod/my-pv-pod created
The scheduler has placed this Pod on k8s-m02 node.
pradeep@learnk8s$ kubectl get pods my-pv-pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-pv-pod 1/1 Running 0 10s 10.244.1.2 k8s-m02 <none> <none>
Let us describe the pod and look at the Volumes section.
pradeep@learnk8s$ kubectl describe pods my-pv-pod
Name: my-pv-pod
Namespace: default
Priority: 0
Node: k8s-m02/192.168.177.30
Start Time: Sun, 27 Feb 2022 22:35:45 +0530
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.244.1.2
IPs:
IP: 10.244.1.2
Containers:
my-pv-container:
Container ID: docker://9dc92c5c1037ad4d534fe6440275f62a68f521e3bd22fe47b57c06a8b3414e24
Image: nginx
Image ID: docker-pullable://nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 27 Feb 2022 22:35:49 +0530
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/usr/share/nginx/html from my-pv-storage (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xk6kh (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
my-pv-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: my-pv-claim
ReadOnly: false
kube-api-access-xk6kh:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 19s default-scheduler Successfully assigned default/my-pv-pod to k8s-m02
Normal Pulling 18s kubelet Pulling image "nginx"
Normal Pulled 15s kubelet Successfully pulled image "nginx" in 2.858388422s
Normal Created 15s kubelet Created container my-pv-container
Normal Started 15s kubelet Started container my-pv-container
It looks like no issues, but let us verify the nginx application by logging into the pod and issuing the curl localhost command.
pradeep@learnk8s$ kubectl exec -it my-pv-pod -- /bin/sh
# curl localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>
# exit
Hmm! This seems to be not working. It is forbidden. But this is expected right, we did not have the volume (hostPath) on this k8s-m02 node. We created it on k8s node only.
To solve this, we can add a nodeName spec to the Pod definition.
Delete this pod and after adding the nodeName recreate the Pod.
pradeep@learnk8s$ kubectl delete pod my-pv-pod
pod "my-pv-pod" deleted
pradeep@learnk8s$ vi my-pv-pod.yaml
pradeep@learnk8s$ cat my-pv-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-pv-pod
spec:
nodeName: k8s
volumes:
- name: my-pv-storage
persistentVolumeClaim:
claimName: my-pv-claim
containers:
- name: my-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: my-pv-storage
pradeep@learnk8s$ kubectl create -f my-pv-pod.yaml
pod/my-pv-pod created
There seems to be some issue with this. Even after scheduling this pod on the node k8s, nginx container is reporting Forbidden. There are couple of issues already reported, but could not find any resolution to this problem yet.
https://github.com/kubernetes/website/issues/9523