SubPath In Kubernetes Volumes

Understanding Subpath with Kubernetes Volumes

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

  1. 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.
  2. 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 labels app=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 inside volumeMounts
# 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 say nginx.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.

Join Our Newsletter

Share this article:

Table of Contents