摘要

半夜 Pod 崩了,kubectl describe 一看全是“mount failed”,心态炸没炸?本文记一次生产级 K8s 数据库 PVC 挂载异常从挂载失败、权限不足到磁盘物理损坏的完整排障路径。文中给出了一键诊断脚本、fsGroup 权限配置、磁盘修复命令、故障自愈策略,所有配置直接复制进项目就能跑。根治 PVC 挂载问题,有这一篇就够了。

📌 政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦。


1. 故障现象速查表

现象关键词 直接原因 对应章节
FailedMountUnable to attach or mount 存储后端不可用、PV 未绑定、驱动异常 第3章
Permission deniedmkdir: cannot create directory 容器用户 UID/GID 无写权限 第4章
数据库启动报 I/O errorcorruptread-only filesystem 磁盘坏道、文件系统损坏、存储后端故障 第5章
Pod 一直 ContainerCreating Volume 挂载阻塞,kubelet 超时 第3、5章

✅ 无论哪种现象,先 kubectl describe pod <pod> + kubectl get events,日志直接告诉你死在哪个阶段。


2. 排查三步法总纲

🔹 第一步:定位挂载点阶段
看 Pod Events 是卡在 AttachVolume.AttachMountVolume.MountDevice 还是 MountVolume.SetUp。不同阶段对应不同组件:Attach 失败查存储后端和节点,Mount 失败查文件系统和权限。

🔹 第二步:对比底层存储状态
PVC、PV、StorageClass 是否正常绑定?后端存储(NFS/Ceph/本地盘)能否手动挂载到宿主机?用 mount -t <type> 测试绕开 K8s 直接验证。

🔹 第三步:进入容器内部核实
kubectl exec -it <pod> -- df -h 检查挂载点,ls -ld /data 看权限和属主。很多权限问题只有在容器视角才能复现。

下面按三类典型场景拆解。


3. 第一类:PVC 挂载失败

3.1 现象

  • Pod Events: Warning FailedMount ... Unable to attach or mount volumes

  • PVC 状态 Pending 或 Bound 但挂载报错

  • mount 命令报 wrong fs type, bad option, bad superblock

3.2 排查步骤与命令

① 检查 PVC/PV 绑定

bash

kubectl get pvc -n <ns>
kubectl describe pvc <pvc-name>

如果 PVC 一直 Pending,查看 Events,常见原因:

  • 没有匹配的 StorageClass(kubectl get sc

  • PV 资源耗尽(kubectl get pv

  • 存储后端不可用(如 NFS Server 宕机)

② 验证后端存储可用性
对于 NFS:

bash

# 在 Pod 所在节点手动挂载测试
mount -t nfs 192.168.1.100:/data /mnt/test
ls /mnt/test
umount /mnt/test

如果手动挂载也失败,直接检查存储端。

③ 检查 CSI 驱动 / provisioner 状态

bash

kubectl get pods -n kube-system | grep csi
kubectl logs <csi-provisioner-pod> -n kube-system --tail=100

3.3 常见根因与修复

根因 修复方法
StorageClass provisioner 未部署或报错 重新部署 CSI 驱动,确保 provisioner Pod Running
NFS Server 导出路径权限不匹配 在 NFS 上设置 *(rw,sync,no_root_squash) 或特定网段
PV 容量不足或访问模式冲突 调整 accessModes(RWO/ROX/RWX),PVC 请求的 size 必须 ≤ PV
节点未安装对应文件系统工具 在节点上安装 nfs-commonceph-common 等

快速修复示例:重绑 PVC

yaml

# 删除PVC重新创建,或者手动创建PV绑定
kubectl delete pvc data-pvc -n dm
kubectl apply -f pvc.yaml

🛑 避坑: StatefulSet 生成的 PVC 名称固定(<volumeClaimTemplate>-<pod>-0),不能直接改 PVC 名,需要在模板中调整 volumeClaimTemplates 的 storageClassName 后重建 StatefulSet(会丢失数据,务必先备份)。


4. 第二类:权限不足

这是最隐蔽、最高发的一类,达梦、人大金仓等数据库容器经常因为 UID/GID 导致写失败。

4.1 现象

  • Pod Running 但数据库初始化失败,日志:Permission deniedcannot create directory '/dm/data/DAMENG'

  • df -h 看到挂载成功,但写入文件报错

  • 手动 touch /data/test 成功,但数据库服务启动失败(因为服务进程用的不是 root 用户)

4.2 原因分析

达梦官方镜像默认用 dmdba 用户(UID=1000)运行,人大金仓用 kingbase 用户。PVC 挂载后目录属主是 root:root,非 root 容器进程无写权限。

K8s 安全上下文中如果未指定 fsGroup,Volume 挂载后保持存储后端的原始权限。

4.3 根治方案

方案一:Pod 安全上下文设置 fsGroup(推荐)

yaml

apiVersion: apps/v1
kind: StatefulSet
spec:
  template:
    spec:
      securityContext:
        fsGroup: 1000          # 指定卷挂载后所属组
        runAsUser: 1000        # 可选,与镜像内用户ID一致
        runAsGroup: 1000
      containers:
      - name: dm-primary
        image: dm8:datawatch
        volumeMounts:
        - name: data
          mountPath: /dm/data

👉 效果: Kubelet 挂载卷时自动 chown 目录组为 1000,并设置 setgid 权限,后续创建的文件均继承组 1000,避免权限问题。这是最简单的根治方式。

方案二:InitContainer 预置权限(兼容非 root 场景)

yaml

initContainers:
- name: fix-permissions
  image: busybox
  command: ['sh', '-c', 'chown -R 1000:1000 /dm/data && chmod 750 /dm/data']
  volumeMounts:
  - name: data
    mountPath: /dm/data

方案三:修改 Dockerfile 用户或使用 rootless 镜像
不推荐,侵入性强。

4.4 验证

bash

kubectl exec -it dm-primary-0 -- id dmdba
kubectl exec -it dm-primary-0 -- ls -ld /dm/data

输出应显示属主为 dmdba 或 UID 1000。

✅ 使用 fsGroup 后,同一 Pod 内多个容器只要 group 一致,均可共享读写。


5. 第三类:磁盘损坏与数据恢复

5.1 现象

  • 数据库错误日志:Input/output errorBuffer I/O error on device

  • 操作系统日志:kernel: blk_update_request: I/O error 或 EXT4-fs error

  • dmesg 输出磁盘坏道信息

  • PVC 挂载成功但读取卡死,ls /data 无响应

5.2 紧急排查

① 查看系统日志

bash

# 在节点上执行
dmesg | grep -i "error\|fail\|corrupt"
journalctl -k | grep -i "I/O error"

② 检查文件系统一致性

bash

# 先卸载挂载点(停止Pod)
umount /var/lib/kubelet/pods/<pod-uid>/volumes/.../mount
fsck -y /dev/sdb1           # 对本地磁盘,如果是网络存储则在存储端操作

③ 检查磁盘坏道

bash

badblocks -v /dev/sdb1 > badblocks.txt

5.3 数据恢复步骤

⚠️ 先全量备份,再操作!

对于达梦/人大金仓:

bash

# 1. 尽可能导出数据(如果还能读)
dmfldr USERID=SYSDBA/SYSDBA@localhost:5236 MODE='OUT' SQL='SELECT * FROM important_table' DATA='/safe/backup.txt'

# 2. 全量物理备份(如果文件系统尚可访问)
tar -czf /safe/dm_data_backup.tar.gz /dm/data

# 3. 如果文件系统已损坏,尝试 dd 镜像
dd if=/dev/sdb1 of=/safe/disk_image.img bs=4M conv=noerror,sync

修复后,基于备份重建 PVC:

  • 新建一个 PVC 和临时 Pod,将备份数据导入

  • 再将数据库 Pod 指向新 PVC

✅ 如果使用了 StorageClass 快照功能,直接恢复快照(需提前配置 VolumeSnapshot):

yaml

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: dm-data-snap
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: data-dm-primary-0

恢复:

yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-dm-primary-0-restore
spec:
  dataSource:
    name: dm-data-snap
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi

5.4 防止磁盘损坏诱发数据丢失

  • 配置节点亲和性,数据库 Pod 绑定到 SSD/NVMe 高性能节点

  • 启用 StorageClass volumeBindingMode: WaitForFirstConsumer 避免预分配异常

  • 设置备份 CronJob,每小时/天备份到对象存储


6. 根治方案:监控、备份与自愈

6.1 监控指标

层面 监控项 工具
K8s 事件 FailedMountFailedAttachVolume 事件告警 Prometheus AlertManager + kube-state-metrics
节点磁盘 磁盘使用率、I/O 延迟、坏道 SMART 信息 node-exporter + smartctl
数据库日志 关键词 I/O errorcorruptpermission denied ELK/Loki 日志告警
PVC 状态 容量超过 80% 告警、Pending 状态 kube-state-metrics

6.2 自动修复策略

  • 挂载失败重试: 配合 Liveness Probe,Pod 启动失败自动重启,StatefulSet 控制器会尝试重新挂载。

  • 磁盘损坏自愈: 使用本地 PV 时配置节点反亲和,Pod 调度到健康节点;使用 Ceph/Rook 等分布式存储实现自动修复。

  • 权限问题阻断: 在 CI/CD 中校验 StatefulSet YAML 必须包含 securityContext.fsGroup 字段,否则拦截发布。

6.3 备份不可绕过

数据库 PVC 一定要配置定时备份:

yaml

apiVersion: batch/v1
kind: CronJob
metadata:
  name: dm-backup
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: dm8:tools
            command: ["/bin/sh", "-c"]
            args:
              - >
                dmrman CTLSTMT="BACKUP DATABASE '/dm/data/DAMENG/dm.ini' FULL BACKUPSET '/dm/backup/$(date +%Y%m%d)'";
            volumeMounts:
            - name: data
              mountPath: /dm/data
            - name: backup
              mountPath: /dm/backup
          restartPolicy: OnFailure

📢 总结

PVC 挂载异常是 K8s 数据库场景的核心大坑,从挂载失败、权限不足到磁盘损坏,一旦出问题直接影响业务连续性。这篇复盘把我们团队踩过的所有坑都沉底了:一键诊断脚本、fsGroup 权限一刀切、磁盘损坏的恢复路径、监控自愈方案,每一个配置都是生产里跑过的。

别等半夜 Pod 崩了再搜解决方案,先收藏这篇,按章节排查,问题基本都能在三步内定位。

更多推荐