Non graceful node shutdown¶
In Kubernetes, when a node becomes dysfunctional or is intentionally drained,
taints node.kubernetes.io/out-of-service may be applied to mark the node
as unavailable refer k8s doc.
This results in the forceful deletion of pods scheduled on the node and the
cleanup of associated VolumeAttachment objects. However, this forceful cleanup
sequence bypasses key CSI lifecycle calls most notably, the
NodeUnpublishVolume and NodeUnstageVolume entry points are not invoked
prior to ControllerUnpublishVolume call.
Problem¶
When ControllerUnpublishVolume is called without the node first having
cleaned up the volume (via NodeUnpublishVolume and NodeUnstageVolume),
the CSI driver has no opportunity to revoke the node's access to the volume.
The node may still hold active mounts, open file handles, or client sessions.
This can lead to data corruption as applications may still be running on the
broken node with active client sessions, even though the node is marked as out
of service.
Important Note¶
⚠️ WARNING: When a node becomes out of service, its mounts and device mappings will persist until the node undergoes a complete power lifecycle (includes shutdown and startup). To prevent data inconsistency or corruption, administrators MUST NOT remove the
node.kubernetes.io/out-of-servicetaint until the node has successfully completed a full power cycle. Removing the taint prematurely may leave stale device states, active client sessions, or lingering mounts, which can lead to serious data integrity issues.
Proposed solution¶
To ensure safe volume reuse and prevent stale client access during node
disruptions, the proposed solution is to track the client address during
the NodeStageVolume() operation and store it in the image or subvolume
metadata under the key: .[rbd|cephfs].csi.ceph.com/clientAddress/<NodeId>.
Note: This metadata should not be copied when creating clones, snapshots, or mirror images of the volume.
This stored address can then be used:
- In
ControllerUnpublishVolume()to blocklist or evict the client if the node has theout-of-servicetaints, ensuring it no longer has access to the volume.
Removal of blocklist can either be done manually or can be automated by enabling
CSI-Addons in Ceph-CSI deployment and creating of networkFenceClass. Ceph-CSI will
auto unblocklist itself during CSI-Addons GetFenceClient() call when pods are
rescheduled after node recovery.
Implementation Details¶
NodeStageVolume()¶
-
Applicable to: RBD and CephFS
-
Before volume staging:
-
Retrieve the client address:
-
Always set it in the image/subvolume metadata: (
NodeIdfrom the plugin container argument--nodeid) -
Continue with Volume staging
ControllerUnpublishVolume()¶
-
Retrieve the Node object using the provided
NodeIdfrom the request. -
If the node has the
out-of-servicetaint: -
Check for the metadata key:
-
If present, proceed to revoke client access:
- For RBD:
# Add client to blocklist with extended duration to prevent stale client access. # The blocklist must persist until we can confirm the node has gone through # a complete power cycle, as premature expiration could lead to data corruption ceph osd blocklist add <clientAddress> 157784760-
For CephFS:
- List active clients and match against
clientAddressto get theclientId.- Evict the client and blocklist the address:
ceph tell mds.* client evict id=<clientId> # Add client to blocklist with extended duration to prevent stale client access. # The blocklist must persist until we can confirm the node has gone through # a complete power cycle, as premature expiration could lead to data corruption ceph osd blocklist add <clientAddress> 157784760 - List active clients and match against
-
Remove the
.[rbd|cephfs].csi.ceph.com/clientAddress/<NodeId>metadata key -
If the node doesn't have the
out-of-servicetaint: -
Remove the
.[rbd|cephfs].csi.ceph.com/clientAddress/<NodeId>metadata key
Workaround for older PVs¶
Problem: ControllerPublishVolume()/ControllerUnpublishVolume() requires
controller-publish-secret. The secret needed to access the Ceph cluster may be
missing for older PVs if the following parameters were not specified in their
corresponding StorageClass at the time of provisioning:
csi.storage.k8s.io/controller-publish-secret-name
csi.storage.k8s.io/controller-publish-secret-namespace
Solution 1: Fallback to default secrets if available in csi-config-map ConfigMap.
[
{
"clusterID": "my-cluster",
"rbd": {
...
"controllerPublishSecretRef": {
"secretName": "publish-secret-name",
"secretNamespace": "publish-secret-namespace"
},
...
},
"cephfs": {
...
"controllerPublishSecretRef": {
"secretName": "publish-secret-name",
"secretNamespace": "publish-secret-namespace"
},
...
}
}
]