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=nginxon 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: 80kubectl 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: TCPkubectl 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.htmlpresent at root directory to/datadirectory
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
mountPathin volumeMounts to mount/datavolume 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
subPathas 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: DirectoryHere 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.volumeMountsspecify the name of the volume, mountPath, and subPath to mount one of the keys from configmap saynginx.confand 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.