- Understand PersistentVolumeClaims for Storage
kubernetes.io > Documentation > Tasks > Configure Pods and Containers > Configure a Pod to Use a Volume for Storage
kubernetes.io > Documentation > Tasks > Configure Pods and Containers > Configure a Pod to Use a PersistentVolume for Storage
Create busybox pod with two containers, each one will have the image busybox and will run the 'sleep 3600' command. Make both containers mount an emptyDir at '/etc/foo'. Connect to the second busybox, write the first column of '/etc/passwd' file to '/etc/foo/passwd'. Connect to the first busybox and write '/etc/foo/passwd' file to standard output. Delete pod.
show
This question is probably a better fit for the 'Multi-container-pods' section but I'm keeping it here as it will help you get acquainted with state
Easiest way to do this is to create a template pod with:
kubectl run busybox --image=busybox --restart=Never -o yaml --dry-run=client -- /bin/sh -c 'sleep 3600' > pod.yaml
vi pod.yaml
Copy paste the container definition and type the lines that have a comment in the end:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: busybox
name: busybox
spec:
dnsPolicy: ClusterFirst
restartPolicy: Never
containers:
- args:
- /bin/sh
- -c
- sleep 3600
image: busybox
imagePullPolicy: IfNotPresent
name: busybox
resources: {}
volumeMounts: #
- name: myvolume #
mountPath: /etc/foo #
- args:
- /bin/sh
- -c
- sleep 3600
image: busybox
name: busybox2 # don't forget to change the name during copy paste, must be different from the first container's name!
volumeMounts: #
- name: myvolume #
mountPath: /etc/foo #
volumes: #
- name: myvolume #
emptyDir: {} #
Connect to the second container:
kubectl exec -it busybox -c busybox2 -- /bin/sh
cat /etc/passwd | cut -f 1 -d ':' > /etc/foo/passwd
cat /etc/foo/passwd # confirm that stuff has been written successfully
exit
Connect to the first container:
kubectl exec -it busybox -c busybox -- /bin/sh
mount | grep foo # confirm the mounting
cat /etc/foo/passwd
exit
kubectl delete po busybox
Create a PersistentVolume of 10Gi, called 'myvolume'. Make it have accessMode of 'ReadWriteOnce' and 'ReadWriteMany', storageClassName 'normal', mounted on hostPath '/etc/foo'. Save it on pv.yaml, add it to the cluster. Show the PersistentVolumes that exist on the cluster
show
vi pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
name: myvolume
spec:
storageClassName: normal
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
hostPath:
path: /etc/foo
Show the PersistentVolumes:
kubectl create -f pv.yaml
# will have status 'Available'
kubectl get pv
Create a PersistentVolumeClaim for this storage class, called mypvc, a request of 4Gi and an accessMode of ReadWriteOnce, with the storageClassName of normal, and save it on pvc.yaml. Create it on the cluster. Show the PersistentVolumeClaims of the cluster. Show the PersistentVolumes of the cluster
show
vi pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mypvc
spec:
storageClassName: normal
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
Create it on the cluster:
kubectl create -f pvc.yaml
Show the PersistentVolumeClaims and PersistentVolumes:
kubectl get pvc # will show as 'Bound'
kubectl get pv # will show as 'Bound' as well
Create a busybox pod with command 'sleep 3600', save it on pod.yaml. Mount the PersistentVolumeClaim to '/etc/foo'. Connect to the 'busybox' pod, and copy the '/etc/passwd' file to '/etc/foo/passwd'
show
Create a skeleton pod:
kubectl run busybox --image=busybox --restart=Never -o yaml --dry-run=client -- /bin/sh -c 'sleep 3600' > pod.yaml
vi pod.yaml
Add the lines that finish with a comment:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: busybox
name: busybox
spec:
containers:
- args:
- /bin/sh
- -c
- sleep 3600
image: busybox
imagePullPolicy: IfNotPresent
name: busybox
resources: {}
volumeMounts: #
- name: myvolume #
mountPath: /etc/foo #
dnsPolicy: ClusterFirst
restartPolicy: Never
volumes: #
- name: myvolume #
persistentVolumeClaim: #
claimName: mypvc #
status: {}
Create the pod:
kubectl create -f pod.yaml
Connect to the pod and copy '/etc/passwd' to '/etc/foo/passwd':
kubectl exec busybox -it -- cp /etc/passwd /etc/foo/passwd
Create a second pod which is identical with the one you just created (you can easily do it by changing the 'name' property on pod.yaml). Connect to it and verify that '/etc/foo' contains the 'passwd' file. Delete pods to cleanup. Note: If you can't see the file from the second pod, can you figure out why? What would you do to fix that?
show
Create the second pod, called busybox2:
vim pod.yaml
# change 'metadata.name: busybox' to 'metadata.name: busybox2'
kubectl create -f pod.yaml
kubectl exec busybox2 -- ls /etc/foo # will show 'passwd'
# cleanup
kubectl delete po busybox busybox2
If the file doesn't show on the second pod but it shows on the first, it has most likely been scheduled on a different node.
# check which nodes the pods are on
kubectl get po busybox -o wide
kubectl get po busybox2 -o wide
If they are on different nodes, you won't see the file, because we used the hostPath
volume type.
If you need to access the same files in a multi-node cluster, you need a volume type that is independent of a specific node.
There are lots of different types per cloud provider (see here), a general solution could be to use NFS.
Create a busybox pod with 'sleep 3600' as arguments. Copy '/etc/passwd' from the pod to your local folder
show
kubectl run busybox --image=busybox --restart=Never -- sleep 3600
kubectl cp busybox:etc/passwd ./passwd # kubectl cp command
# previous command might report an error, feel free to ignore it since copy command works
cat passwd
List Persistent Volumes in the cluster
kubectl get pv
Create a hostPath PersistentVolume named task-pv-volume with storage 10Gi, access modes ReadWriteOnce, storageClassName manual, and volume at /mnt/data and verify
// task-pv-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
kubectl create -f task-pv-volume.yaml
kubectl get pv
Create a PersistentVolumeClaim of at least 3Gi storage and access mode ReadWriteOnce and verify status is Bound
// task-pv-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
kubectl create -f task-pv-claim.yaml
kubectl get pvc
Delete persistent volume and PersistentVolumeClaim we just created
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume
Create a Pod with an image Redis and configure a volume that lasts for the lifetime of the Pod
// emptyDir is the volume that lasts for the life of the pod
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
kubectl create -f redis-storage.yaml
Exec into the above pod and create a file named file.txt with the text ‘This is called the file’ in the path /data/redis and open another tab and exec again with the same pod and verifies file exist in the same path.
// first terminal
kubectl exec -it redis-storage /bin/sh
cd /data/redis
echo 'This is called the file' > file.txt
//open another tab
kubectl exec -it redis-storage /bin/sh
cat /data/redis/file.txt
Delete the above pod and create again from the same yaml file and verifies there is no file.txt in the path /data/redis.
kubectl delete pod redis
kubectl create -f redis-storage.yaml
kubectl exec -it redis-storage /bin/sh
cat /data/redis/file.txt // file doesn't exist
Create PersistentVolume named task-pv-volume with storage 10Gi, access modes ReadWriteOnce, storageClassName manual, and volume at /mnt/data and Create a PersistentVolumeClaim of at least 3Gi storage and access mode ReadWriteOnce and verify status is Bound
kubectl create -f task-pv-volume.yaml
kubectl create -f task-pv-claim.yaml
kubectl get pv
kubectl get pvc
Create an nginx pod with containerPort 80 and with a PersistentVolumeClaim task-pv-claim and has a mouth path "/usr/share/nginx/html"
// task-pv-pod.yaml
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
kubectl create -f task-pv-pod.yaml
- Create a Persistent Volume named
pv
, access modeReadWriteMany
, storage class nameshared
, 512MB of storage capacity and the host path/data/config
. - Create a Persistent Volume Claim named
pvc
that requests the Persistent Volume in step 1. The claim should request 256MB. Ensure that the Persistent Volume Claim is properly bound after its creation. - Mount the Persistent Volume Claim from a new Pod named
app
with the path/var/app/config
. The Pod uses the imagenginx
. - Check the events of the Pod after starting it to ensure that the Persistent Volume was mounted properly.
Show Solution
Create a YAML file for the Persistent Volume and create it with the command kubectl create
command.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv
spec:
capacity:
storage: 512m
accessModes:
- ReadWriteMany
storageClassName: shared
hostPath:
path: /data/config
You will see that the Persistent Volume has been created but and is available to be claimed.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv 512m RWX Retain Available shared 4s
Create a YAML file for the Persistent Volume Claim and create it with the command kubectl create
command.
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 256m
storageClassName: shared
You will see that the Persisten Volume Claim has been created and has been bound to the Persisten Volume.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc Bound pv 512m RWX shared 2s
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv 512m RWX Retain Bound default/pvc shared 1m
Create a YAML file for the Pod and create it with the command kubectl create
command.
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: app
name: app
spec:
containers:
- image: nginx
name: app
volumeMounts:
- mountPath: "/var/app/config"
name: configpvc
resources: {}
volumes:
- name: configpvc
persistentVolumeClaim:
claimName: pvc
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
You can check the events of a Pod with the kubectl describe
command. You should see an entry that indicates the successful mount.
$ kubectl describe pod app
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 16s default-scheduler Successfully assigned app to docker-for-desktop
Normal SuccessfulMountVolume 16s kubelet, docker-for-desktop MountVolume.SetUp succeeded for volume "pv"
Normal SuccessfulMountVolume 16s kubelet, docker-for-desktop MountVolume.SetUp succeeded for volume "default-token-fsmmp"
Normal Pulling 15s kubelet, docker-for-desktop pulling image "nginx"
Normal Pulled 14s kubelet, docker-for-desktop Successfully pulled image "nginx"
Normal Created 14s kubelet, docker-for-desktop Created container
Normal Started 13s kubelet, docker-for-desktop Started container