Kubernetes 1.10 makes storage lifecycle decisions explicit
CSI and local persistent volumes reach beta in Kubernetes 1.10. The storage changes also clarify the relationship between mounting, scheduling and deletion.
A pod can move while its data remains tied to a particular machine. Storage management has to account for that difference before a container starts, and again when its claim is deleted. The Kubernetes 1.10 announcement on March 26 brings local persistent volumes and the Container Storage Interface to beta. It also announces beta protection for claims in use and volumes bound to claims. These changes address separate decisions in a stateful application's lifecycle.
I find the storage work more instructive than a simple count of beta features. Kubernetes needs to express where data exists and when a reference to it can disappear. Reconciliation can maintain those relationships only when the API and the controllers represent them. Storage makes that requirement concrete because creating another pod does not reproduce the bytes on a failed disk.
CSI concerns the boundary between Kubernetes and storage implementations. The announcement describes independent delivery of external volume drivers, outside the core Kubernetes repository. Local persistent volumes concern a storage source whose location matters directly to the workload. Protection of storage objects concerns deletion ordering. A cluster can need all of these mechanisms without any one of them replacing the others.
What the local volume plugin actually mounts
The local volume implementation shipped in 1.10.0 supports a local source through a PersistentVolume. Its CanSupport method requires a PersistentVolume specification with the local field set. This is relevant to the meaning of persistence here. The plugin receives a separately represented volume with a path, and the pod's mount operation uses that path.
For filesystem mounts, NewMounter takes the path from the local volume source and stores it as the global path. Setup creates a bind mount from that source into the pod's volume directory. The implementation does not copy the source directory's contents to another machine as part of this operation. Its mount procedure makes an existing local path available at the location the pod expects.
That distinction explains why local persistence needs coordination with placement. A successful bind mount depends on the correct source path on the node where setup occurs. Creating the destination directory is only preparation for the bind mount. It does not create the storage contents which the source path is meant to provide. The mount implementation consequently solves a narrower problem than general storage availability.
Setup checks that the global path is nonempty and validates it against backsteps before mounting. It also checks whether the target is already mounted and returns successfully when no additional mount is needed. A lock keyed by the global path serializes setup work for that source within the plugin. These steps concern safe repeated operations on a particular local volume, including cases where several setup calls could otherwise touch the same source.
The plugin reports ReadWriteOnce as its access mode. Its source comment explains that access mode concerns the number of nodes which can attach, rather than the number of pods which can mount. That distinction matters when interpreting a local volume claim. A single-node access mode does not, by itself, express exclusive use by a single application process or provide application-level coordination.
Ownership is part of mount behavior
The local plugin examines existing mount references to the source when a filesystem group has been requested. If references already exist, it compares the requested group with the source's current group. A difference produces a warning event about the volume potentially being unshareable. This check connects pod security settings to the fact that several mounts may expose the same underlying filesystem contents.
For a writable mount, the plugin applies ownership only when there are no existing references. Changing ownership while another reference is active could affect an existing consumer of the same source. The implementation's conditional treatment of ownership recognizes that mounting a directory and preparing its permissions are related operations with different consequences for other users.
The bind-mount options start with bind and include ro when the mount is read-only. If mounting fails, the code checks the target's mount state, attempts an unmount when needed and checks again. A target that remains mounted causes an error return so a later synchronization can retry. Teardown delegates to the unmount helper with an extensive mount-point check. These details show how much local state remains relevant after storage has a declarative API object.
Binding can wait for the consumer
The 1.10.0 StorageClass API definitions name two volume binding modes. Immediate is the default when the field is unset. WaitForFirstConsumer defers provisioning and binding until a pod first references the claim, with those operations occurring during scheduling. The latter mode provides a way to consider the consumer when deciding the storage binding.
The relevant fragment of a StorageClass is small.
volumeBindingMode: WaitForFirstConsumer
This fragment describes the binding choice, not a complete local-volume installation. It does not specify a provisioner or supply an existing volume. The source also labels the field alpha-level and says servers honor it only with the VolumeScheduling feature enabled. That wording is a useful reminder to distinguish the status of a particular API field from the announcement that local persistent volumes have reached beta.
The practical difference between the two modes is when the binding decision acquires information about demand. Immediate binding can happen before a pod exists to describe a consumer. Deferred binding retains the claim until scheduling can consider that consumer. For storage whose location constrains execution, the timing of this decision affects whether a selected volume and the eventual pod placement can work together.
Deletion protection is an active controller
The 1.10.0 PVC protection controller explains how claim protection works beyond the announcement. It watches claims and pods, queues claim identifiers and processes them after its informer caches synchronize. Protection is therefore ongoing work performed against observed cluster state. It is not solely a validation check at the instant a user requests deletion.
A claim becomes a deletion candidate when it has both a deletion timestamp and the protection finalizer. The controller then checks whether the claim is in use. When it finds no qualifying consumer, it removes the finalizer by updating a deep copy of the claim. An error keeps the item eligible for rate-limited retry. The controller does not interpret an update failure as evidence that cleanup has completed.
The use check lists pods in the claim's namespace. It skips unscheduled pods and pods classified as terminated, then examines persistent volume claim references in the remaining pod specifications. A matching claim name means the claim remains in use. These are specific controller semantics. The implementation does not attempt to inspect an application's open file descriptors or infer whether a database has completed its own shutdown protocol.
That makes the protection's boundary understandable. Kubernetes delays deletion according to pod references and lifecycle state visible in the API. The mechanism can preserve storage object ordering while leaving application consistency to the application and storage system. A user requesting deletion has expressed intent, but the finalizer permits that intent to remain pending while the controller observes a consumer that still requires the claim.
The same controller also adds the finalizer to older claims which lack it and are not being deleted. Its source explains that admission normally adds the finalizer and that this path covers claims predating that admission behavior. This handles existing state as well as newly created objects. A protection mechanism introduced during an upgrade needs both paths because the API already contains claims created under earlier settings.
The controller also handles deleted pod notifications whose objects arrive through informer tombstones. It extracts the pod and queues the claims in that pod's volume list. This matters because a deletion notification can supply the information needed to reconsider protection even when the pod is no longer present in the ordinary cache.
I regard this as a useful example of Kubernetes' controller model applied to a precise operational problem. Pod lifecycle events enqueue the claims they reference when those events may allow protection to end. The worker then checks current claim and pod state before changing the finalizer. The decision survives beyond a single request handler and remains tied to observable dependencies. For a scheduled, nonterminated pod which still references the claim, the controller retains that protection.