subPath
do for you?I've always been confused as to what purpose subPath
serves. I've come across subPath
whenever a configmap was mounted: it looked like it was used as a way to control which keys in a configmap or secret are projected… but I was never sure.
In this page, I'll try to uncover what exactly is happening when using subPath
and finally get some firm understanding.
We will be using Kind to test things out. Let's create a kind cluster:
kind create cluster
We will need the tree
utility too:
docker exec kind-control-plane sh -c "apt update && apt install tree -y"
Let's consider the following configmap:
apiVersion: v1
kind: ConfigMap
metadata:
name: conf
data:
conf.yaml: "contents"
other.yaml: "other"
And the following pod manifest; pay close attention to volumes
and volumeMounts
:
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: example
image: busybox:1.28
command: ['sh', '-c', 'echo "Running!" && tail -f /dev/null']
volumeMounts:
- name: conf
mountPath: /etc/conf.yaml
subPath: conf.yaml
volumes:
- name: conf
configMap:
name: conf
Now, I'll need to inspect the rootfs of the example container above. I'll use the command podbash
defined as follows:
#!/bin/bash
#shellcheck disable=SC2093
exec docker exec -it kind-control-plane bash -c "$(grep -v 'exec docker' "$0")" podbash "$@"
CONTAINER_ID=$(crictl ps --name example -o json | jq -r ".containers[].id")
PID="$(ctr -n k8s.io t ls | grep "$CONTAINER_ID" | awk '{print $2}')"
cd "/proc/$PID" || exit 1
exec bash "$@"
EOF
With podbash
, I can inspect the container's rootfs as well as look at the mountinfo. For example:
# To inspect the container's filesystem:
podbash -c 'ls root/etc'
# To inspect the mount points:
podbash -c 'cat mountinfo' | grep configmap
It's interesting to look at the mount information to understand the role of subPath
. The output of mountinfo
looks like this:
4031 4028 259:5 /lib/kubelet/pods/84f85/volumes/kubernetes.io~configmap/conf/..2024_07_01_14_15_02.3624755718/conf.yaml /etc/conf.yaml ro,relatime - ext4 /dev/nvme0n1p3 rw,errors=remount-ro
To make this a little more readable, I've turned this into a table:
Host volume | Pod mount point |
---|---|
/lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/conf/..2024_07_01_14_15_02.3624755718/conf.yaml | /etc/conf.yaml |
Now, let's make the distinction between mountPath
and subPath
:
mountPath
is about the pod mount point.subPath
is about the host volume path.Without subPath
, the outcome would have been very different. Let's remove the subPath
from the manifest:
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: example
image: busybox:1.28
command: ['sh', '-c', 'echo "Running!" && tail -f /dev/null']
volumeMounts:
- name: conf
mountPath: /etc/conf.yaml
volumes:
- name: conf
configMap:
name: conf
The host "volume" (a directory, really) looks like this:
# From the host:
$ sudo tree /lib/kubelet/pods/X/volumes/kubernetes.io~configmap/conf
├── conf.yaml -> ..data/conf.yaml
└── other.yaml -> ..data/other.yaml
The pod mount point looks off:
$ podbash -c 'tree root/etc/conf.yaml'
root/etc/conf.yaml
├── conf.yaml -> ..data/conf.yaml
└── other.yaml -> ..data/other.yaml
The problem is that /etc/conf.yaml
is now a directory rather than a file…
That's where subPath
is useful: when you need to mount a single file rather than an entire folder.
More specifically, subPath
allows you to select which path under the host volume path /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/conf.yaml/
needs to be mounted.
Important learnings:
subPath
isn't found, what's mounted at mountPath
is a host-mounted volume that is made of an empty directory. The subPath
name doesn't matter in this case.subPath
corresponds to a file in the host volume (e.g., it matches a configmap key), that file alone is mounted to mountPath
.subPath
corresponds to a directory in the host-mounted volume (only happens when mounting a PV volume), then mountPath
is a directory that corresponds to the sub- directory subPath
in the host-mounted volume.If your concern is that you want to mount multiple configmap keys to a directory with existing files (such as /etc
), the following will cause /etc
to be replaced by the host-mounted volume:
volumeMounts:
- name: conf
mountPath: /etc
The subPath
is exactly what you want in this case. You will have to have one volume mount per file, though:
volumeMounts:
- name: conf
mountPath: /etc/conf.yaml
subPath: conf.yaml
- name: conf
mountPath: /etc/other.yaml
subPath: other.yaml
subPath
that doesn't exist in the host's volume?On the host, since baz
doesn't exist in the configmap myconf
, the host volume is an empty directory:
$ sudo ls -al /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/foo
drwxrwxrwx 2 root root 4096 Jul 1 15:56 .
On the host, when foo
exists in the configmap myconf
, it appears as a file in the host volume:
$ sudo ls -al /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/..2024_07_01_14_15_02.3624755718/foo
-rw-r--r-- 1 root root 8612 Jul 1 16:15 /lib/kubelet/pods/bc2bf/volumes/kubernetes.io~configmap/myconf/..2024_07_01_14_15_02.3624755718/foo