Mount Namespaces and Propagation
How does a mount made inside the CSI node plugin container reach the host and then the app? Each container has its own mount namespace, and the plugin volumeMounts set mountPropagation Bidirectional on the Kubelet directories, so every mount it makes there is repeated in the host list, where a bind mount is the same files at a second path. The app container gets its own bind at /data, private by default, so later host mounts do not reach it.
Play the animation 5 steps, animated, best on a wide screen
Mount Namespaces and Propagation, step by step
-
The CSI node plugin runs in a container, so how can a mount it makes reach the host, and then Pod A? Each container gets its own mount namespace, a private list of mounts, and by default a mount call in a container changes only that list. Two lists matter so far: the plugin and the host, where Kubelet and the runtime run. Pod A gets one when its container starts.
Play from step 1 -
The plugin volumeMounts give /var/lib/kubelet/plugins and /var/lib/kubelet/pods mountPropagation: Bidirectional, allowed only in a privileged container. The runtime binds each from the host mount holding them, shared on a systemd host or made shared by Kubelet at start, so all three sit in one peer group, shared:1. The staging mount the plugin itself makes for /dev/nvme1n1 lands in its own list, and the kernel repeats it in the host list.
Play from step 2 -
Publishing the volume to Pod A usually adds a bind mount of that staged directory under /var/lib/kubelet/pods, and it reaches the host list the same way. A bind is not a copy: both entries are one filesystem on /dev/nvme1n1, the same files and the same inodes. That is why findmnt on Node-1 lists /dev/nvme1n1 twice.
Play from step 3 -
Now the runtime starts the app container. It creates a new mount namespace for the container and binds the host entry of that Pod directory at /data, a third view of the same files. The volumeMount leaves mountPropagation at None, which is rprivate by default, so a mount the host adds under that directory later does not reach /data.
Play from step 4 -
If instead the plugin volumeMounts used None, its mounts would stay in its own list. The host list would get neither entry, so Kubelet could not see the mount, and Pod A would get the empty host directory at /data, on the Node disk. Bidirectional is the one mode that sends mounts back to the host, and HostToContainer only receives them. The docs advise propagation only on hostPath or memory-backed emptyDir volumes, which the plugin directories are.
Play from step 5