土法炼钢 · 系统与基础设施

【Rook / CSI】挂载与 features:NodeStage、krbd 错位与 read affinity

文章导航

分类入口
storagekubernetes
标签入口
#rook#csi#rbd#krbd#imagefeatures#read-affinity#tentacle#network-fencing#v1.20.4

目录

PVC 已经 Bound,Pod 也调度到了节点,业务却读写错乱或偶发 I/O 错误——这类事故很少停在「CreateVolume 失败」上。RBD CSI 的挂载路径把 image features、内核 krbd 能力、map 选项与节点生命周期 叠在一起;错位时常不是干净的 FailedMount,而是「看起来挂上了,行为不对」。

本文是「Rook / CSI」系列第 7 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 6 篇 · RBD 供给 StorageClass → image
第 7 篇 · 挂载与 features NodeStage/Publish;krbd;read affinity
第 8 篇 · CephFS CSI subvolume / RWX

版本锚定:Rook v1.20.4;Ceph-CSI v3.17.0;K8s v1.31–v1.36。Ceph:Squid v19.2.x / Tentacle v20.2.1+v20.2.0 + read affinity 有已知损坏风险,标 Tentacle+ 警告)。krbd/features 语义外链 ceph/12


一、Node 侧两段 RPC

K8s 对块设备卷的典型顺序(CSI 规范 + kubelet):

  1. NodeStageVolume:在节点全局 staging 路径把 RBD image map 成块设备(默认 krbd → /dev/rbd*),必要时格式化/挂载到 staging。
  2. NodePublishVolume:把 staging 上的设备(或文件系统)bind-mount 进 Pod 可见路径。
sequenceDiagram
  participant Kubelet
  participant NodePlugin as CSI RBD node plugin
  participant Kernel as krbd / nbd
  participant Ceph as MON / OSD

  Kubelet->>NodePlugin: NodeStageVolume
  NodePlugin->>Kernel: rbd map (features check)
  Kernel->>Ceph: libceph msgr
  NodePlugin-->>Kubelet: staging path ready
  Kubelet->>NodePlugin: NodePublishVolume
  NodePlugin-->>Kubelet: target path in Pod

Controller 侧的 CreateVolume 已在第 6 篇完成。本篇失败模式几乎都落在 NodeStage 的 map 与后续 I/O 语义上。


二、map 路径:krbd 默认,nbd 兜底

Ceph-CSI RBD 默认走 krbd(内核 drivers/block/rbd.c + libceph)。StorageClass 可设:

异构集群(部分节点内核旧)是经典陷阱:CreateVolume 在「新内核节点」视角合法,Pod 却调度到「旧内核节点」——NodeStage 才暴露问题。


三、features 与 krbd:为何「挂上但怪」

3.1 能力谈判发生在何时

时机 行为 局限
CreateVolume 按 SC 的 imageFeatures 创建 image 不知道将来哪个节点会 map
NodeStage 读 image features,对照本机 krbd 支持集 检测失败才报错;部分组合历史上行为不一

ceph-csi 在 NodeStage 会检查 krbd 是否支持所需特性;不支持且未允许其他 mounter 时,返回 Internal(日志常见 unsupported krbd Feature)。这是干净失败路径。

ceph/18 点名、本系列要展开的摩擦是另一类:

机制背景(不重写):Exclusive Lock、object-map 依赖链与 krbd map 闸门见 ceph/12 · features 与 krbd。编排层结论只有一条:StorageClass 的 features 上界 = 集群中最弱可调度节点的 krbd 上界(除非强制 nbd 并接受其运维面)。

3.2 工程核对顺序

  1. rbd info {pool}/{image} 看实际 features(toolbox)。
  2. 对照节点内核版本与 Rook 文档分档(≥5.4 才考虑完整特性集示例)。
  3. 看 node plugin 日志中的 map 参数与 feature 检查结果。
  4. 确认是否误开 journaling 等仅部分路径支持的特性。

不要先怀疑 BlueStore:挂载错位是客户端能力问题,不是 PG 降级问题。


四、read affinity:性能旋钮与 Tentacle 雷区

4.1 做什么

Rook 允许在 CephCluster 上打开 CSI read affinity(默认关闭):

spec:
  csi:
    readAffinity:
      enabled: true

语义(Rook「Enable Read affinity」+ ceph-csi RBD 文档):按节点 topology 标签推导 CRUSH 位置,map 时附加 krbd 选项(如 read_from_replica=localizecrush_location=...),让读尽量打到「更近」的 OSD 副本。需要节点 topology 标签与内核 ≥ 5.8(Rook 文档门槛)。

这是延迟优化,不是正确性特性。未理解 CRUSH 拓扑与标签一致性时,收益不稳定。

4.2 Tentacle v20.2.0:官方损坏警告(Tentacle+)

Rook Ceph Upgrades(v1.20)写明:

站内纪律:凡 Tentacle 特有风险,正文标 Tentacle+,不把「默认关着所以没事」写成生产豁免——有人会打开它。


五、节点丢失与 network fencing(简述)

RBD 卷在节点宕机后不能「自动安全」地挂到另一节点:旧节点若仍持有 map/会话,双挂会损坏。

Rook v1.20 文档路径:

  1. 确认节点不可用后打上 node.kubernetes.io/out-of-service=nodeshutdown 的 NoExecute/NoSchedule taint。
  2. 若启用 network fencing(CSI-Addons + Driver 配置,默认不启用):驱动从 image 元数据取客户端地址,对 Ceph blocklist,阻止旧会话。
  3. 在节点完整断电重启循环之前,不得提前去掉 taint——文档警告:过早摘 taint 可能留下陈旧 map/会话,造成严重一致性问题。
  4. 恢复后经冷却期,CSI-Addons 可自动解除 blocklist;亦可手工处理。

详细租户与安全面见第 15 篇;本篇只建立坐标:挂载生命周期包含 fencing,不只包含 mount 成功码。


六、排障时把「怪」拆成可证伪假设

遇到「卷已挂载但应用不对」时,按下面顺序收窄,避免一上来打 OSD:

  1. 确认挂载栈:节点上是否存在预期的 /dev/rbd* 或 nbd 设备;Pod 内挂载选项是否来自本次 NodePublish。
  2. 确认 features 实际值:toolbox rbd info 与 StorageClass 声明是否一致;有无被人手工 rbd feature enable 过。
  3. 确认节点内核与模块rbd/libceph 是否加载;内核是否低于 SC 设计基线。
  4. 确认 lock / blocklist:是否刚做过节点迁移或 fencing;旧客户端是否仍在 blocklist 上。
  5. 确认 read affinity:若刚升 Tentacle 或刚打开 localize 读,优先排除第 4 节雷区,再谈性能。

只有前四项都干净,才把问题推到 PG 降级、磁盘延迟或应用逻辑。第 13 篇会把这些收成排障坐标系;本篇先建立「怪 ≠ 存储慢」的默认怀疑顺序。

异构内核滚动升级(部分节点已升、部分未升)会放大第 2–3 项。平台若不能一次性升级所有可调度节点的内核,就应冻结 StorageClass features,而不是「先开 object-map 再慢慢升内核」。


七、与 librbd 路径的边界(容器默认不走这条)

ceph/12 花了整节讲 librbd / QEMU。Rook 上绝大多数应用容器走的是 kubelet → CSI node plugin → krbd → 文件系统 → bind 进 Pod,不是 Pod 内链 librbd。例外包括:虚拟化负载用 kubevirt 一类再挂 RBD、或特权实验直接 map。

因此:把 QEMU rbd_cache 争论原样搬到「普通 Deployment 挂 PVC」会误导。容器路径的正确性争论更集中在 krbd features、exclusive-lock 与节点 fencing。librbd 的缓存语义仍重要,但不在本篇默认事故面。

若强制 mounter: rbd-nbd,运维面变成用户态守护与 nbd 模块——排障日志与性能轮廓都变,需要单独 runbook,不能与 krbd 共用一份「看 dmesg」手册。


八、谱系、争论与开放问题

谱系:块设备挂载从本地 SCSI 语义外推到网络块时,能力协商问题反复出现(iSCSI 特性、NVMe 可选特性、RBD features)。CSI 把「供给」与「节点附着」拆开,放大了异构节点上的延迟暴露。krbd 与 librbd 双路径(ceph/12)是 Ceph 长期分叉;容器场景默认偏 krbd。网络 fencing 则是把经典「STONITH / 客户端 blocklist」接到 K8s 的 out-of-service taint 上——设计提案在 ceph-csi 的 non-graceful node shutdown 文档中可核对。

争论:要不要在 CreateVolume 时把 features 压到「集群最小内核」?保守派减少静默失败;激进派用新特性换 object-map 读放大收益,并依赖 tryOtherMounters。没有单一正确解——有内核矩阵与 SC 冻结流程的团队可以激进;多发行版混部的平台应保守。另一争论是 read affinity:副本局部读在纸面上降低读延迟,却引入拓扑标签正确性与 Tentacle 修复版本依赖;默认关闭是务实默认,不是「功能不重要」。

开放问题

  1. 调度器能否把「节点 krbd 能力」做成可扩展资源,使 PVC 与节点在绑定前对齐?
  2. read affinity 在副本局部读与 PG 降级/scrub 叠加时,尾延迟是否可预测?(需对照实验,本站无伪造数字。)
  3. fencing 冷却期与应用 RTO 之间如何给出可测试上限,而不是「大约五分钟」口语?

九、本篇收束

挂载成功码只证明 NodePublish 返回了 OK。features 错位、Tentacle+ read affinity 雷区、以及未 fencing 的双挂风险,都会在 OK 之后伤害数据。下一篇换到 CephFS:RWX 的税在 MDS/caps,不在 krbd。


十、把 Tentacle 与 Squid 的挂载假设写进变更单

站内机制叙述默认钉 Squid;进入 Tentacle 时,挂载相关变更单至少多写三行:

  1. 目标 Ceph 精确版本(禁止口头「升到 20」);
  2. 当前是否启用 csi.readAffinity,计划保持还是关闭;
  3. 若曾经过 20.2.0,是否已完成「关特性 + 应用重启」闭环。

缺任一行,就把 Tentacle+ 风险从「已知文档」降成「口头传说」。Rook 升级文档把 20.2.0 标成不推荐,不是排版装饰。

对只读副本 localize 的性能期望,也应写成可证伪句:例如「在某拓扑标签齐全的节点上,读延迟分布是否相对关闭 affinity 时左移」。没有拓扑标签却打开开关,等于给 map 选项喂空约束,收益与风险不对称。

网络 fencing 变更单则要写清:谁有权限打 out-of-service taint、谁确认断电循环、谁负责解 blocklist。权限不清时,最常见的操作事故是「业务已迁走、旧节点未 fencing」或「未断电就摘 taint」。二者都比「忘记开 features」更接近数据损坏。

十一、NodePublish 之后还有谁能动这块盘

NodePublish 完成不代表只有应用容器在访问 image。节点上的 CSI 插件、可能的 CSI-Addons sidecar、kubelet 的挂载管理,以及运维在 toolbox 里对同一 image 的误操作,都会进入同一条一致性故事。Exclusive Lock 的协作与 blocklist 破锁(ceph/12)在「同一 image 被两个客户端路径打开」时被触发;容器场景下第二客户端常常是未 fencing 干净的旧节点,而不是第二个 Pod(RWO 已由 K8s 附着限制)。

因此排障「数据被写坏」时,时间线要拉到:节点 NotReady 时刻、taint 时刻、卷在新节点 Stage 时刻、旧节点是否完成断电。缺时间线就无法区分应用 bug 与双挂。第 15 篇会再谈多租户下谁有权触发 fencing;本篇要求值班至少会问这四个时间戳。

关于「静默失败」的可观测缺口:kubelet 事件擅长报告 MountFailed,不擅长报告「mount 成功但 feature 降级/行为降级」。若 ceph-csi 回退到 nbd 或跳过某条检查,需要从插件日志确认,而不是从 kubectl describe pod 的成功事件确认。把 node plugin 日志纳入默认采集,是挂载篇对第 12 篇的具体订货。

性能问题上,mapOptions 里的队列深度一类旋钮可以改善延迟轮廓,但也会掩盖后端过载:把队列加长后 P99 表面变好,OSD 端却堆积更多飞行中请求。改 mapOptions 应与 OSD 延迟、慢请求计数对照,禁止只看应用侧均值。

参考资料

官方文档 / issue(A/B)

站内

上一篇:RBD 供给 · 系列目录 · 下一篇:CephFS CSI

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-15 · storage / kubernetes

【Rook / CSI】编排全景:五轴坐标系与 16 篇路线

相对 ceph/ RADOS 内核与 storage/ 云块选型,钉清 Rook/CSI 编排层缺口;定义 CSI RPC、sidecar、Operator/CRD、数据面归属、升级闸门五条坐标系,给出 16 篇阅读路线与 PVC Bound 之后 I/O 仍走 krbd→PG→BlueStore 的边界。


By .