Kubernetes Local PVC 丢失时的 Fake Device 解决方案
节点重启或 OS patching 时,本地磁盘可能短暂丢失,导致依赖 Local PV 的 Pod 启动失败——kubelet 找不到设备路径,mount 操作报错。本文记录一种通过 loop device 创建”假设备”(fake device)来解除 Pod 启动阻塞的工程方案,以及各类节点修复场景下的处理策略。
问题背景
节点重启或 OS patching 后,/dev/sdc 及对应的 /dev/disk/by-id/wwn-xxxxxx 路径可能不再存在。但 Kubernetes 中对应的 PV 仍处于 Bound 状态:
1 | spec: |
此时 kubelet 尝试挂载时,因找不到源设备而失败,Pod 无法启动。
方案思路
Local Volume Provisioner(下称 “provisioner”)在检测到 PV 路径不存在时,主动创建一个 loop device 作为假设备,让 kubelet 能够完成 mount 操作,使 Pod 正常启动。Pod 内的应用程序随后通过设备大小识别假设备并将其移除,再进入正常业务流程。
为什么不能用普通文件替代设备
直觉上,用 touch + bind mount 创建一个假文件映射到设备路径似乎可行:
1 | touch test_fake_file |
但 kubelet 会拒绝挂载非设备节点的路径:
1 | Warning Failed kubelet Error: failed to generate container spec: |
因此必须使用真正的块设备——通过 loop device 来实现。这要求 provisioner Pod 挂载宿主机的 /dev/ 路径(需要相应的安全策略支持)。
实现步骤
1. Provisioner 检测设备丢失
Provisioner 以 10 秒为周期轮询 PV 路径(wwn-xxx)是否存在。若不存在,读取节点 label 判断是否需要创建假设备:
1 | shouldCreateFakeDisk := ShouldCreateFakeDisk(config.Node.Labels[DedicatedLabelKey]) |
2. 创建 loop device
若 shouldCreateFakeDisk 为 true,provisioner 执行以下操作:
- 在本地临时目录创建一个 512 字节的镜像文件
- 从
loop101开始设置 loop device - 创建软链接:
/dev/loopX→/dev/disk/by-id/wwn-xxxxxx
创建后,kubelet 在下一次同步时即可成功完成 mount:
1 | Normal SuccessfulMountVolume kubelet |
3. Pod 内识别并移除假设备
假设备大小仅为 512 字节,写入超过 512B 会报错:
1 | root@pod:/# dd if=/dev/zero of=/dev/vda bs=1 count=1024 |
应用启动脚本通过读取设备大小识别假设备,读出 512B 的设备即为假设备,将其删除后再启动服务:
1 | for device in `ls /dev/data*`; do |
移除假设备后,服务正常启动。
完整流程
1 | 节点重启 |
注意事项
假设备不会被 provisioner 重新分配
假设备通过 loop device 创建,软链接指向 /dev/loopX 而非 /dev/sdc,不在 provisioner 的 volume 扫描列表中,因此不会被重新注册为可用 PV。
需要 /dev/ hostPath 权限
创建 loop device 需要 provisioner Pod 能访问宿主机 /dev/ 路径,须通过安全策略(如 PSP / SecurityContext)明确授权。/dev/disk/by-id/ 是已有的 hostPath,但 /dev/ 本身默认不挂载,需要额外的 PR 支持。
PVC/PV 生命周期
假设备期间 PVC 和 PV 不会被删除。若用户手动删除 PVC,PV 会被重新 provision 但大小变为 512B,新 PVC 无法绑定到该 PV。若需要在保留 PV 的前提下重建 PVC,可设置注解:
1 | storage.tess.io/reclaimpolicy.override = Recycle |
等待 PV 同步后再删除 PVC。
节点修复场景分析
| 场景 | 处理方式 |
|---|---|
| 节点重启 / OS patching | Provisioner 重启后自动走检测流程,假设备重建 |
| 节点重新 provision(新节点) | 全新节点,无历史状态,无需处理 |
| 替换磁盘(新 sdc + 新 wwn) | 新 wwn 不匹配旧 PV,用户需删除旧 PVC 并重建 |
| 磁盘恢复(原 sdc + 原 wwn) | Linux 自动重建软链接,kubelet 恢复正常挂载 |
| NPD 检测到 sdc 丢失 | 在 computenode 标记 sdc 为 lost,触发告警 |
| 节点 decommission | Pod 和 PVC 需要手动删除 |
用户侧影响场景
以下情况会触发本方案的运作:
- 节点重启:OS patching 等运维操作导致节点重启
- Pod 删除重建:用户主动删除并重建 Pod
- Provisioner Pod 重建:provisioner 自身重启
- 节点修复(单盘替换):仅更换一块磁盘
- 新节点 provision:节点首次加入集群
- 故障磁盘自动归还:之前被判定故障的磁盘恢复后自动接回