A pod in Kubernetes is the smallest deployable unit that has one or more containers in it and these containers include volumes that are used for storage purposes like to share files between containers, to store files from root filesystems, to use Network Attached File System such as NFS, EBS and many more. These volumes use volume mount which directs where to mount files.
But when working with volumes, we often encounter some problems like
- As we mount a Kubernetes volume at a mount-point and if we already have some content in it, then it would be hidden by our mount. But in some cases, we would not want to hide the existing content at the mount-point but want to add additional files/directories in parallel.
- While working with multiple configmaps/secrets, if one wants to mount multiple keys from different configmaps/secrets at the same or other location but cannot do it as it gets overwritten.
To solve such types of problems, a subPath in volume comes to the rescue.
Basically, a volume is a directory, possibly with some data in it, which is accessible to the containers in a pod and the contents of it are determined by the particular volume type used.
To use a volume in the pod it is specified with .spec.volumes
and volume mount for containers with .spec.containers[*].volumeMounts
... ... spec: containers: - image: busybox name: test-container volumeMounts: - mountPath: /mnt/data name: volume1 volumes: - name: volume1 emptyDir: {}
For each container defined within a Pod, you must independently specify where to mount each volume that the container uses with inside volumeMounts.mountPath
These volumes cannot be mounted within other volumes but can be shared for multiple uses in a pod which avoids creating multiple volume mounts and this is done with subpath in volume mounts.
The volumeMounts.subPath
property specifies a sub-path inside the referenced volume instead of its root.
To make it more clear, in the below code snippet which has both mountPath
and subPath
inside volumeMounts
name volume1
.
volumeMounts: - mountPath: /mnt/data/dataset1 name: volume1 subPath: dataset1 volumes: - name: volume1 emptyDir: {}
As we know the path of volume on the host is created under the following path /var/lib/kubelet/pods/<pod uid>/volumes/<volume type>/<volume name>
And for the above example, İf we’re not using subPath (only mountPath) then at the host, volume is created at /var/lib/kubelet/pods/<pod uid>/volumes/kubernetes.io~empty-dir/volume1
On the other hand, if we’re using subPath as dataset1 then at the host, its path looks like /var/lib/kubelet/pods/<pod uid>/volume-subpaths/volume1
From this we came to know, subPath will append the data to the existing volume mount point by creating a new directory and will not overwrite the existing content which makes the same volume to be mounted multiple times.
This is mostly useful when you want to mount a configuration file from a config map or want to mount credentials from secret but not to mount it as a volume. So, it can be done with the subpath.
Pre-requisites
Doing hands-on is important to follow and learn as it helps us to understand the concepts easily. It is recommended to do hands-on as you proceed further.
- Ensure you have access to a Kubernetes cluster with admin privileges on your local machine; if not, create one using Minikube or Kind.
- Make sure you have installed kubectl on your machine.
Lab for SubPath
In this hands-on lab, we will be seeing two examples for subpath in which the first one will give us a better understanding of subpath and help us to compare it with mountpath.
In the second example, we will be mounting keys from configmap through the subpath.
Example One
To see the difference between the mountPath and subPath more precisely, create an Nginx pod and deploy it using a service.
- Create an Nginx pod with labels
app=nginx
on it.
# nginx-app.yaml apiVersion: v1 kind: Pod metadata: name: nginx-app labels: app : nginx spec: containers: - name: nginx-container image: teamcloudyuga/nginx ports: - containerPort: 80
kubectl apply -f nginx-app.yaml
kubectl get pods
- Create a NodePort service for the same and access the app through
port-30000
# service.yaml apiVersion: v1 kind: Service metadata: name: nginx-svc labels: app: nginx spec: selector: app: nginx type: NodePort ports: - name: http port: 80 nodePort: 30000 protocol: TCP
kubectl apply -f service.yaml
kubectl get svc
On accessing the application through http://localhost:30000 you will see the welcomes page of Nginx
Now let’s see the difference between mountpath and subpath by creating a new Nginx pod with mountpath and subpath specified in it.
- Delete the existing Nginx pod
kubectl delete pod nginx-app
- Create a directory at the host which will be used as a volume to be mounted
mkdir /data
- And move the
hello.html
present at root directory to/data
directory
mv hello.html /data/
<!--hello.html--> <html> <title> SubPath</title> <body> <h1> Hello, This is a SubPath volume demo </h1> </body> </html>
- Create another Nginx pod and use
mountPath
in volumeMounts to mount/data
volume along with labelsapp=nginx
# pod1.yaml apiVersion: v1 kind: Pod metadata: name: nginx-pod1 labels: app : nginx spec: containers: - image: teamcloudyuga/nginx name: nginx-container ports: - containerPort: 80 volumeMounts: - mountPath: /usr/share/nginx/html/ name: test-volume volumes: - name: test-volume hostPath: path: /data type: Directory
- Now create the pod to expose it with the NodePort service.
kubectl apply -f pod1.yaml
kubectl get pods
On accessing the application again, will find that the Nginx welcome page is not there because in volumeMounts
mountPath
is /usr/share/nginx/html/
and the existing contents of this location has been overwritten by hello.html
This can be verified with the help of the following command.
kubectl exec -it nginx-pod1 -- ls /usr/share/nginx/html
You can also run the curl localhost
command to verify it.
kubectl exec nginx-pod1 -- curl localhost
- Again delete the existing Nginx pod
kubectl delete pod nginx-pod1
- Create another Nginx pod and use
subPath
as well insidevolumeMounts
# pod2.yaml apiVersion: v1 kind: Pod metadata: name: nginx-pod2 labels: app : nginx spec: containers: - image: teamcloudyuga/nginx name: nginx-container2 ports: - containerPort: 80 volumeMounts: - mountPath: /usr/share/nginx/html/hello.html subPath: hello.html name: test-volume volumes: - name: test-volume hostPath: path: /data type: Directory
Here subPath
is hello.html
and mountPath
is /usr/share/nginx/html/hello.html
This will make the subPath value to be appended with the mountPath and will not overwrite the existing contents.
- Now create the pod to expose it with the NodePort service.
kubectl apply -f pod2.yaml
kubectl get pods
On accessing the application again, will find that the Nginx welcome page comes there now because in volumeMounts
subPath
is also used along with mountPath
which appends new content along with the existing content at the location /usr/share/nginx/html/
This can be verified with the help of the following command.
kubectl exec -it nginx-pod2 -- ls /usr/share/nginx/html
You can also run the curl localhost
command to verify it.
kubectl exec nginx-pod2 -- curl localhost
Example Two
- Create a config map with multiple key files in it and then will mount the keys from configmap inside Nginx pod using subpath at different locations.
# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: nginx-config data: nginx.conf : | { user: nginx; } virtualhost.conf: | { server: localhost; port: 80 }
- Apply the configmap
kubectl apply -f configmap.yaml
kubectl get configmaps
- Create an Nginx pod and inside
spec.containers.volumeMounts
specify the name of the volume, mountPath, and subPath to mount one of the keys from configmap saynginx.conf
and use configmap as the volume inside.spec.volumes
# config-volume.yaml apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx-container image: teamcloudyuga/nginx volumeMounts: - name: config-volume mountPath: /tmp/config/nginx.conf subPath: nginx.conf - name: config-volume mountPath: /mnt/virtualhost.conf subPath: virtualhost.conf volumes: - name: config-volume configMap: name: nginx-config
- Now create the pod and exec into the pod for the keys.
kubectl apply -f config-volume.yaml
kubectl get pods
kubectl exec -it nginx -- cat /tmp/config/nginx.conf
kubectl exec -it nginx -- cat /mnt/virtualhost.conf
Here, we will see that nginx.conf
and virtualhost.conf
key from configmap, both are mounted at /tmp/config
and /mnt
location respectively and if only mountPath
is used then we will not be able to use the same volume mount to mount at different locations at the same time.
NOTE : SubPaths are not automatically updated when a ConfigMap is modified. Changes to a ConfigMap will need a new deployment which would result in the pods being recreated with the updated ConfigMap content.
Conclusion
In this hands-on lab, we have seen about subpath in Kubernetes volumes and saw what problems it can solve if being used.