NFS 存储服务 运行手册(RUNBOOK)¶
交付目录:
nfs-storage/适用对象:接手日常运维与值班的人(假设你没接触过本服务,凌晨被叫起来也要能照着做完) 配套文档:变更实施与回滚见nfs-storage/SOP.md;挂载参数调优见nfs-mount-tuning/docs/SOP.md最后核对时间:2026-09-19(文中所有水位与状态均以该时刻实测为准)
目录¶
1. 服务概览¶
用途:为 k8s_ins 集群提供动态供给、支持 RWX 与快照的持久存储,供有状态应用挂载。
组成
| 层 | 组件 | 位置 | 作用 |
|---|---|---|---|
| 服务端 | nfs-server(nfs-kernel-server 1:2.6.4-3ubuntu5.1)+ rpcbind |
宿主 dell-R620 192.168.31.249 |
提供三处导出(NFSv4.2) |
| 服务端后端 | SSD 120GiB loop 镜像 → /srv/nfs/k8s |
宿主 sda3 |
nfs-csi 存储类的数据目录 |
| 服务端后端 | HDD 500GiB loop 镜像 → /srv/nfs/k8s-hdd |
宿主 /data |
nfs-csi-hdd 存储类的数据目录 |
| 服务端后端 | HDD 裸目录 /data/nfs-k8s-snap |
宿主 /data |
卷快照归档(.tar.gz) |
| 集群控制面 | csi-nfs-controller(5 容器 Deployment) |
kube-system,当前在 k8s-node-02 |
CreateVolume / DeleteVolume / 快照编排 |
| 集群数据面 | csi-nfs-node(3 容器 DaemonSet,4 副本) |
kube-system,全部 4 节点 |
节点侧挂载/卸载 |
| 集群快照 | snapshot-controller(1 容器 Deployment) |
kube-system |
处理 VolumeSnapshot 对象 |
| 集群对象 | StorageClass nfs-csi(默认) / nfs-csi-hdd、CSIDriver nfs.csi.k8s.io、3 个 snapshot CRD、VolumeSnapshotClass csi-nfs-snapclass、Secret nfs-mount-options |
集群级 / kube-system |
供应用引用 |
依赖
- 宿主
dell-R620在线、nfs-server运行;/data(sdb1)与宿主根分区(sda3)可读写。 - 集群节点可经
192.168.31.0/24访问宿主 2049 端口。 - 宿主上还有 Slurm 的导出
/data/nfs/slurm-state共用同一个nfs-server服务与/etc/exports(见第 9 章)。
异常时的业务影响
服务不可用时,下列业务会I/O 阻塞(hard 挂载特性,不是报错而是卡住):
| 命名空间 | 业务 | 卷 |
|---|---|---|
| server | MariaDB(数据库)、MinIO(GitLab 对象存储) | storage-mariadb-0(20Gi)、minio-data(50Gi) |
| server / ldap | OpenLDAP ×2 套(conf/data/backups) | *-openldap-0(1/8/8Gi ×2) |
| monitoring | Prometheus(100Gi)、Grafana(10Gi)、Alertmanager(10Gi) | 见 SOP 第 4 章 |
| 宿主机 | Slurm 集群 | /data/nfs/slurm-state |
重要性等级:高。理由:承载数据库与监控栈;服务端与全部计算节点同宿主、无冗余,宿主故障即存储与计算同时中断。
2. 接入与凭据¶
地址与端口
| 项 | 值 |
|---|---|
| NFS 服务端 | 192.168.31.249:2049(NFSv4.2,TCP) |
| rpcbind | 192.168.31.249:111 |
| 允许客户端网段 | 192.168.31.0/24(/etc/exports 限定) |
| 导出路径 | /srv/nfs/k8s、/srv/nfs/k8s-hdd、/data/nfs-k8s-snap |
账号与凭据存放(本文不记录任何口令/密钥)
| 用途 | 账号 | 凭据从哪取 |
|---|---|---|
| 宿主机运维 | rain(在 sudo 组,sudo -n 免密) |
已有 SSH 会话;无需口令 |
| 集群操作 | kubeconfig 的 kubernetes-admin |
默认 ~/.kube/config(执行 kubectl config view --minify 查看) |
| 节点排障 | root(免密公钥登录) |
ssh root@<节点IP> 直接登录 |
客户端接入示例
宿主机或同网段机器手工挂载(与集群 Pod 看到的是同一份数据,勿同时写):
# 挂载整个导出(推荐用与 StorageClass 一致的参数)
mount -t nfs -o vers=4.2,nconnect=4,hard,timeo=600,retrans=3,noatime \
192.168.31.249:/srv/nfs/k8s /mnt
# 只挂某个 PVC 的子目录(路径取自 PV 的 volumeHandle 第三段)
mount -t nfs -o vers=4.2 192.168.31.249:/srv/nfs/k8s/pvc-204be3a9-1adf-4123-8c04-94fa0da2f27f /mnt
# 用完记得卸载
umount /mnt
集群内应用引用(PVC 片段):
spec:
storageClassName: nfs-csi # 或 nfs-csi-hdd;不写则用默认的 nfs-csi
accessModes: [ReadWriteMany]
resources:
requests:
storage: 10Gi
3. 日常速查表¶
| # | 目的 | 命令 |
|---|---|---|
| 1 | 看整体是否正常 | kubectl -n kube-system get pods \| grep -E 'csi-nfs\|snapshot' |
| 2 | 看存储类 | kubectl get sc |
| 3 | 看卷与业务绑定 | kubectl get pvc -A \| grep -E 'nfs-csi\|NAMESPACE' |
| 4 | 看宿主服务端状态 | systemctl is-active nfs-server && sudo exportfs -v && nfsconf --get nfsd threads |
| 5 | 看容量水位 | df -h /srv/nfs/k8s /srv/nfs/k8s-hdd /data / |
| 6 | 看各卷占用 | sudo du -sh /srv/nfs/k8s/* /srv/nfs/k8s-hdd/* 2>/dev/null |
| 7 | 看 CSI 日志 | kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=100 --prefix |
| 8 | 看服务端日志 | sudo journalctl -u nfs-server --since "30 min ago" \| tail -50 |
| 9 | 看节点真实挂载参数 | ssh root@192.168.31.13 'findmnt -t nfs4 -o TARGET,OPTIONS' |
| 10 | 驱动升级后必做 | kubectl apply -f nfs-storage/manifests/02-volumesnapshotclass.yaml |
4. 日常操作¶
4.1 状态检查¶
# 集群侧:期望 csi-nfs-controller 5/5、csi-nfs-node 4 个各 3/3、snapshot-controller 1/1
kubectl -n kube-system get pods -o wide | grep -E 'csi-nfs|snapshot'
预期输出(示例):
csi-nfs-controller-f54d5775d-9c98g 5/5 Running 0 2d15h 192.168.31.14 k8s-node-02
csi-nfs-node-9j8md 3/3 Running 0 2d15h 192.168.31.14 k8s-node-02
csi-nfs-node-t57sm 3/3 Running 0 2d15h 192.168.31.13 k8s-node-01
csi-nfs-node-vp5tm 3/3 Running 0 2d15h 192.168.31.10 k8s-master-01
csi-nfs-node-xlh2d 3/3 Running 0 2d15h 192.168.31.15 k8s-node-03
snapshot-controller-887465679-85j2g 1/1 Running 0 2d15h 10.244.44.194 k8s-node-02
不符时:Running 但 READY 不足 → 看该 Pod 日志(4.4);Pending → kubectl -n kube-system describe pod <名> 看事件。
# 服务端侧:期望 active / 四处导出(含 Slurm 的)+ threads 32
systemctl is-active nfs-server
sudo exportfs -v
nfsconf --get nfsd threads
sudo ss -lntp | grep -E '2049|111' # 期望 2049 与 111 均在 LISTEN
4.2 启动 / 停止 / 重启¶
# 服务端:查看状态(安全)
systemctl status nfs-server --no-pager
# 谨慎:重载导出配置(不会中断已挂载的客户端)
sudo exportfs -ra && sudo exportfs -v
# 高危:重启 nfs-server —— 会同时中断集群全部 NFS 业务与 Slurm
# 仅在确认两侧均可中断时执行,执行后按第 8 章场景 4 验证
sudo systemctl restart nfs-server
不要在故障排查中随手重启 nfs-server:客户端是 hard 挂载,服务端短暂不可用期间业务会卡在 I/O 上,
重启后虽能自愈,但会拉长业务受影响时间。优先查日志与导出状态。
4.3 扩容与缩容¶
- 应用侧申请新卷:直接创建 PVC 即可,无需人工干预。
- 已有卷改请求容量:
kubectl patch pvc <名> -n <ns> -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'。 注意:NFS 后端不强制容量,改请求值只是记账(详见第 10 章)。 - 后端总体扩容:见第 10 章"扩容操作"。
4.4 日志查看¶
# CSI 驱动(控制面:创建/删除卷、快照;数据面:挂载/卸载)
kubectl -n kube-system logs -l app=csi-nfs-controller -c nfs --tail=200
kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=200 --prefix
# 快照控制器
kubectl -n kube-system logs deploy/snapshot-controller --tail=100
# 服务端
sudo journalctl -u nfs-server --since "1 hour ago" | tail -100
sudo journalctl -u nfs-server -p err --since today # 只看错误级别
# 单次操作的完整上下文(排查挂载失败必看)
kubectl -n <ns> describe pod <pod名> | sed -n '/Events/,$p'
4.5 进入宿主机或节点排障¶
# 宿主机就是本机(dell-R620),直接执行;带 sudo 的命令已授权免密
hostname -I && sudo exportfs -v
# 进入某个 k8s 节点(root 免密)
ssh root@192.168.31.13 # k8s-node-01
ssh root@192.168.31.14 # k8s-node-02
ssh root@192.168.31.15 # k8s-node-03
ssh root@192.168.31.10 # k8s-master-01
# 在节点上查看某卷的真实挂载点与参数
findmnt -t nfs4 -o TARGET,SOURCE,OPTIONS
# 查看 kubelet 侧的 CSI 挂载目录
ls -l /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~csi/ # <pod-uid> 来自 kubectl get pod -o jsonpath='{.metadata.uid}'
5. 配置管理¶
配置项位置
| 层 | 文件 | 内容 |
|---|---|---|
| 集群(客户端参数、两个 SC、快照开关) | nfs-storage/values-nfs-csi.yaml |
核心实现体,改这里 |
| 集群(删除卷所需挂载参数) | nfs-storage/manifests/00-mount-options-secret.yaml |
必须与 SC 的 mountOptions 一致 |
| 集群(快照 CRD) | nfs-storage/manifests/01-snapshot-crds.yaml |
独立于 Helm 管理 |
| 集群(快照落盘位置) | nfs-storage/manifests/02-volumesnapshotclass.yaml |
每次 helm 升级后都要重新 apply |
| 服务端(导出与权限) | 宿主 /etc/exports |
三处本方案导出 + Slurm 导出共存 |
| 服务端(nfsd 线程等) | 宿主 /etc/nfs.conf |
[nfsd] threads = 32 |
| 服务端(loop 挂载持久化) | 宿主 /etc/fstab |
两个镜像的 loop 挂载行 |
修改流程
# 1) 改客户端参数 / SC(示例:改 mountOptions)
vi nfs-storage/values-nfs-csi.yaml
# 2) 同步 Secret(内容必须与 SC 一致)
vi nfs-storage/manifests/00-mount-options-secret.yaml
kubectl apply -f nfs-storage/manifests/00-mount-options-secret.yaml
# 3) 应用
helm upgrade --install nfs-csi nfs-storage/charts/csi-driver-nfs -n kube-system \
-f nfs-storage/values-nfs-csi.yaml --wait --timeout 6m
# 4) 必做:恢复快照落盘分流(helm 会重置该对象)
kubectl apply -f nfs-storage/manifests/02-volumesnapshotclass.yaml
# 5) 若改的是挂载参数,已有 PV 不会自动生效,按 SOP 第 5.3 节逐个 patch 并重启使用方
# 改服务端导出(示例:把某处导出改回 sync)
sudo cp -a /etc/exports "/etc/exports.bak.$(date +%Y%m%d-%H%M%S)"
sudo vi /etc/exports
sudo exportfs -ra && sudo exportfs -v
易错点(踩过或极易踩)
helm upgrade会重置VolumeSnapshotClass,快照会静默回落到 SSD,可能撑满才发现 → 每次升级后必做第 4 步。- 改
mountOptions只对新建 PV 生效;已有 PV 需逐个kubectl patch pv并重启使用方。 /etc/exports与 Slurm 共用:编辑时不要整文件重写,改完用exportfs -ra而不是exportfs -ua。/etc/nfs.conf的threads必须用sudo nfsconf --set nfsd threads 32维护(文件末尾是[svcgssd]段,tee -a追加会写错段)。- 两个 SC 只能有一个带默认注解;当前默认是
nfs-csi。
6. 备份与恢复¶
备份策略
| 对象 | 方式 | 位置 |
|---|---|---|
| 卷数据(业务数据本体) | 后端 loop 镜像文件,天然整体可复制 | /kvm/nfs/k8s-120g.img(SSD 后端)、/data/nfs/k8s-hdd-500g.img(HDD 后端) |
| 卷快照 | 走 VolumeSnapshot 对象(tar 归档) |
/data/nfs-k8s-snap/snapshot-*/ |
| 服务端配置 | 变更前 cp -a 带时间戳备份 |
原位 /etc/*.bak.*,台账见 nfs-storage/backup/README.md |
| 集群配置 | values 与 manifests 纳入版本管理 | nfs-storage/、nfs-mount-tuning/ |
手工备份命令
# 服务端配置备份(高危操作前必做)
sudo cp -a /etc/exports "/etc/exports.bak.$(date +%Y%m%d-%H%M%S)"
sudo cp -a /etc/fstab "/etc/fstab.bak.$(date +%Y%m%d-%H%M%S)"
sudo cp -a /etc/nfs.conf "/etc/nfs.conf.bak.$(date +%Y%m%d-%H%M%S)"
# 卷数据备份(示例:把 SSD 后端某个 PVC 的目录打包到 HDD;先确认该 PVC 无写入高峰)
PVC_DIR=/srv/nfs/k8s/pvc-204be3a9-1adf-4123-8c04-94fa0da2f27f # 取自 kubectl get pv -o jsonpath='{.spec.csi.volumeHandle}' 的第三段
sudo tar czf "/data/backup-$(basename "$PVC_DIR")-$(date +%Y%m%d-%H%M%S).tar.gz" -C "$PVC_DIR" .
sudo ls -lh /data/backup-*.tar.gz
# 镜像整体备份(慎用,占用同等空间;确认目标盘余量充足)
# sudo cp --sparse=always /data/nfs/k8s-hdd-500g.img /data/k8s-hdd-500g.img.bak
恢复步骤
# 场景一:误删了某个 PVC 的目录内容 → 从 tar 恢复
sudo tar xzf /data/backup-pvc-xxxx-<时间戳>.tar.gz -C /srv/nfs/k8s/pvc-xxxx/
# 恢复后在业务侧校验(示例见第 8 章场景 2 的验证段)
# 场景二:从卷快照恢复 → 用 PVC 的 dataSource(参考 nfs-storage/examples/03-snapshot-and-restore.yaml)
kubectl apply -f nfs-storage/examples/03-snapshot-and-restore.yaml
kubectl get pvc demo-data-restored # 预期 Bound
kubectl logs pvc-restore-check # 预期打印恢复出来的文件内容
# 场景三:服务端配置被改坏 → 从时间戳备份还原
sudo cp -a /etc/exports.bak.<时间戳> /etc/exports && sudo exportfs -ra && sudo exportfs -v
恢复后的验证:按第 8 章对应场景的"验证"段执行;确认业务 Pod 不再报 I/O 错误(kubectl -n <ns> get pod 均 Running 且无新增重启)。
演练记录:快照恢复路径已于交付时实测通过(见 SOP.md 第 6 章 V7);
整机恢复与镜像级回滚尚未演练,属待补项。
7. 监控与告警¶
查看入口
# Grafana(监控面板;端口转发后浏览器访问 http://localhost:3000)
kubectl -n monitoring port-forward svc/kube-prometheus-stack-grafana 3000:80
# Alertmanager
kubectl -n monitoring port-forward svc/kube-prometheus-stack-alertmanager 9093:9093
关键指标与查看方式
| 指标 | 怎么看 | 健康区间 |
|---|---|---|
| 后端空间使用率 | df -h /srv/nfs/k8s /srv/nfs/k8s-hdd |
< 80% |
| 宿主根分区使用率 | df -h / |
< 80%(当前 25%) |
/data 使用率 |
df -h /data |
< 80%(当前 7%) |
| HDD 争抢情况 | iostat -x 1(看 %util、await) |
%util 长期 < 70% |
| NFS 客户端 RPC 延迟 | 节点上 cat /proc/self/mountstats,Grafana 中 node-exporter 的 NFS 指标 |
OPEN/SETATTR 平均 < 5ms(SSD 预期 < 1ms) |
| CSI 组件存活 | kubectl -n kube-system get pods \| grep csi-nfs |
全部 Running 且重启计数不增长 |
| 卷挂载是否正常 | kubectl get pvc -A |
全部 Bound |
| 服务端 nfsd 线程 | nfsconf --get nfsd threads |
32 |
日志位置
| 来源 | 命令 |
|---|---|
| 服务端 | sudo journalctl -u nfs-server --since "1 hour ago" |
| CSI 控制面 | kubectl -n kube-system logs -l app=csi-nfs-controller -c nfs --tail=200 |
| CSI 数据面 | kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=200 --prefix |
| 业务侧现象 | kubectl -n <ns> describe pod <名>、业务自身日志 |
建议设置的告警(当前尚未配置,属待办)
| 告警 | 阈值 | 动作 |
|---|---|---|
| 后端空间不足 | /srv/nfs/k8s 或 /srv/nfs/k8s-hdd 使用率 > 80% |
见第 10 章扩容 |
| 宿主根分区不足 | / 使用率 > 80% |
清理或扩镜像容量 |
| CSI 组件异常 | csi-nfs-* Pod 非 Running 持续 2 分钟 |
见第 8 章场景 1、2 |
| 服务端不可达 | 2049 端口探测失败 | 见第 8 章场景 4 |
8. 故障处置¶
场景 1:PVC 一直 Pending,不 Bound¶
症状:kubectl get pvc -n <ns> 显示 Pending,相关 Pod 卡在 ContainerCreating 或 Pending。
诊断
# 1) 看 PVC 事件(最常见原因直接写在这里)
kubectl -n <ns> describe pvc <pvc名> | sed -n '/Events/,$p'
# 预期:正常应有 Provisioning → ProvisioningSucceeded;报错会给出原因
# 2) 供给器是否存活(Provisioning 由它执行)
kubectl -n kube-system get pod -l app=csi-nfs-controller # 预期 1 个 5/5 Running
kubectl -n kube-system logs -l app=csi-nfs-controller -c nfs --tail=100 # 预期无 "failed to create subdirectory"
# 3) 服务端是否还能写(controller 需挂载 NFS 建目录)
sudo exportfs -v | grep -E 'srv/nfs/k8s' # 预期导出存在且为 async
systemctl is-active nfs-server # 预期 active
处置
- 事件提示
failed to provision且日志显示权限/挂载错误 → 先修服务端(导出、服务状态),再删除该 PVC 重建:bash kubectl -n <ns> delete pvc <pvc名> # 高危:未 Bound 的 PVC 无数据,删除安全 kubectl apply -f <原清单> - 事件提示 storageclass 不存在或名字写错 →
kubectl get sc核对(应为nfs-csi或nfs-csi-hdd)。 - controller Pod 不健康 → 见场景 2 的组件排查;必要时重启组件:
bash kubectl -n kube-system rollout restart deploy/csi-nfs-controller
验证:kubectl get pvc -n <ns> <pvc名> 显示 Bound,且 kubectl get pv 中对应 PV 的 volumeHandle 指向正确后端。
场景 2:Pod 挂载失败或读写报 I/O error¶
症状:Pod 事件报 MountVolume.SetUp failed、failed to mount;或业务日志出现 Input/output error、卡住无响应。
诊断
# 1) 挂载失败的具体原因(node 插件才有挂载动作)
kubectl -n <ns> describe pod <pod名> | sed -n '/Events/,$p'
kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=150 --prefix
# 2) 该 Pod 调度到的节点上,服务端是否可达
NODE=$(kubectl -n <ns> get pod <pod名> -o jsonpath='{.spec.nodeName}')
NODE_IP=$(kubectl get node "$NODE" -o jsonpath='{.status.addresses[0].address}')
ssh root@"$NODE_IP" 'timeout 5 bash -c "</dev/tcp/192.168.31.249/2049" && echo REACHABLE || echo UNREACHABLE'
# 预期:REACHABLE
# 3) 节点上实际挂载情况
ssh root@"$NODE_IP" 'findmnt -t nfs4 -o TARGET,SOURCE,OPTIONS | head'
# 预期:能看到对应 PV 的挂载点,OPTIONS 含 vers=4.2,nconnect=4
处置
- 服务端不可达 → 见场景 4。
- 挂载点存在但业务报 I/O 错误、且服务端正常 → 让使用方重新挂载:
bash kubectl -n <ns> rollout restart statefulset/<名> # 高危:重启业务,逐个进行 - 生产事故中优先止血:确认服务端恢复后,重启受影响业务即可自愈(
hard挂载会随后续 I/O 恢复)。
验证:Pod Running 且无新增重启;进入容器读写测试:
kubectl -n <ns> exec <pod名> -- sh -c 'touch /data/.probe && ls -l /data/.probe && rm -f /data/.probe'
# 预期:无报错,能看到文件
场景 3:快照创建失败,或快照落到了 SSD 上¶
症状:kubectl get volumesnapshot -A 显示 READYTOUSE=false;或宿主机 /srv/nfs/k8s 下出现了 snapshot-* 目录(说明分流失效)。
诊断
# 1) 快照对象状态
kubectl get volumesnapshot -A
kubectl describe volumesnapshot <名> -n <ns> | sed -n '/Events/,$p'
kubectl get volumesnapshotcontent -o custom-columns='NAME:.metadata.name,READY:.status.readyToUse,ERR:.status.error.message'
# 2) 快照类是否仍指向 HDD(helm 升级后最易丢)
kubectl get volumesnapshotclass csi-nfs-snapclass -o jsonpath='{.parameters}{"\n"}'
# 预期:{"server":"192.168.31.249","share":"/data/nfs-k8s-snap"}
# 3) 归档实际位置
sudo find /data/nfs-k8s-snap -maxdepth 2 -name '*.tar.gz' | head
sudo ls -d /srv/nfs/k8s/snapshot-* 2>/dev/null && echo "警告:快照落到了 SSD"
处置
parameters为空或指向 SSD → 恢复分流配置后重试:bash kubectl apply -f nfs-storage/manifests/02-volumesnapshotclass.yaml kubectl delete volumesnapshot <名> -n <ns> # 高危:删除失败/错位的快照对象 kubectl apply -f <快照清单>- 已错落在 SSD 的归档 → 搬到 HDD 后清理(先确认无快照对象引用,否则直接删对象让它重建):
bash sudo mv /srv/nfs/k8s/snapshot-xxxx /data/nfs-k8s-snap/ # 高危:确认该目录不被其它快照引用 - 空间不足导致失败 → 见第 10 章。
验证:kubectl get volumesnapshot <名> -n <ns> 显示 READYTOUSE=true;sudo find /data/nfs-k8s-snap -maxdepth 2 -name '*.tar.gz' 能看到新归档;ls /srv/nfs/k8s 无 snapshot-*。
场景 4:宿主机重启后存储未恢复¶
症状:宿主 reboot 后,业务 Pod 卡在 I/O;exportfs -v 无导出,或 findmnt 看不到后端挂载。
诊断
# 1) loop 后端是否恢复挂载(fstab 带 nofail,挂载失败不会阻塞启动,需人工确认)
findmnt -o SOURCE,TARGET,OPTIONS -t ext4 | grep -E 'srv/nfs/k8s'
# 预期:两行,OPTIONS 含 noatime,prjquota
# 2) 服务是否起来
systemctl is-active nfs-server && nfsconf --get nfsd threads # 预期 active 与 32
sudo exportfs -v # 预期四处导出(含 Slurm)
# 3) 若镜像未挂载,看失败原因
sudo fdisk -l /kvm/nfs/k8s-120g.img 2>/dev/null | head -3
sudo journalctl -u nfs-server -b --since "10 min ago" | tail -30
处置
# 缺挂载 → 手工补齐(幂等)
sudo mount -a && findmnt -t ext4 | grep nfs
# 服务未起 → 启动(不影响已恢复的客户端)
sudo systemctl start nfs-server && sudo exportfs -ra && sudo exportfs -v
# 仍失败 → 按 SOP 第 5.1 步骤 3、4 逐条排查(fstab 行、镜像文件是否存在)
验证:四处导出可见、nfsconf --get nfsd threads 为 32;节点侧 findmnt -t nfs4 有挂载;业务 Pod 自动恢复(若不恢复则逐个重启);Slurm 侧状态目录可读写。
场景 5:HDD 后端读写明显变慢¶
症状:Prometheus/OpenLDAP 等 HDD 卷业务出现查询慢、写入超时;元数据操作 RTT 从亚毫秒升到几十毫秒。
诊断
# 1) 物理盘是否被抢(HDD 上有 VM qcow2、Slurm 状态、HDD 卷三方共用)
iostat -x 1 5 | grep -E 'Device|sdb'
# 预期:%util 长期 < 70%;await 明显升高说明排队严重
# 2) 谁在写
sudo iotop -o -b -n 3 2>/dev/null | head -20
# 3) 服务端线程是否被改回去
nfsconf --get nfsd threads && sudo cat /proc/fs/nfsd/threads # 预期均为 32
# 4) 导出是否仍是 async
sudo exportfs -v | grep srv/nfs/k8s-hdd # 预期含 async
处置
- 线程数回落 →
printf '32\n' | sudo tee /proc/fs/nfsd/threads && sudo nfsconf --set nfsd threads 32。 - 导出被改回
sync→ 评估后改回(注意 R2:async有掉电丢数据风险,改前确认业务可接受)。 - 确认是 VM/Slurm 抢盘 → 属于资源竞争,短期只能错峰;长期方案是把 HDD 卷迁移到独立盘。
- 需要临时缓解 → 把延迟不敏感的负载从 HDD 卷迁到 SSD 卷(改 PVC 的 SC 需重建 PVC,见第 10 章)。
验证:iostat -x 1 的 %util/await 回落;节点侧 cat /proc/self/mountstats 中 OPEN/SETATTR 平均 RTT 回到毫秒级以下。
场景 6:后端空间写满(ENOSPC)¶
症状:业务报 No space left on device;df -h /srv/nfs/k8s 或 /srv/nfs/k8s-hdd 显示 100%。
诊断
df -h /srv/nfs/k8s /srv/nfs/k8s-hdd /data /
sudo du -sh /srv/nfs/k8s/* /srv/nfs/k8s-hdd/* 2>/dev/null | sort -h | tail -10 # 谁占得最多
sudo du -sh /data/nfs-k8s-snap/* 2>/dev/null | sort -h | tail -5 # 快照占用
kubectl get pvc -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,REQ:.spec.resources.requests.storage' | grep nfs-csi
处置
# 1) 先清理无用快照(可回收大量空间)
kubectl get volumesnapshot -A # 找出不再需要的
kubectl delete volumesnapshot <名> -n <ns> # 高危:删除后归档立即回收
# 2) 清理僵尸卷目录(PVC 已删但目录残留的情况)——先确认无 PVC 引用再删
kubectl get pv -o jsonpath='{range .items[*]}{.spec.csi.volumeHandle}{"\n"}{end}' | grep /srv/nfs/k8s
sudo ls -d /srv/nfs/k8s/pvc-* # 与上一步结果比对,多出来的即为残留
sudo rmdir /srv/nfs/k8s/pvc-<确认无引用> # 高危:目录非空时不要强删
# 3) 扩容后端(详见第 10 章)
验证:df -h 使用率回落;业务写入恢复正常;kubectl get pvc -A | grep nfs-csi 全部 Bound。
9. 安全与权限¶
访问控制
| 项 | 现状 | 说明 |
|---|---|---|
| 导出范围 | 192.168.31.0/24 |
仅该网段客户端可挂载;改网段需同步 /etc/exports |
| 导出权限 | rw,async,no_subtree_check,no_root_squash |
no_root_squash 意味着该网段任意主机以 root 身份可读写全部数据,属内网信任模型 |
| 数据可见性 | 挂载整个导出即可看到所有 PVC 目录 | 无租户隔离;误删会影响对应业务 Pod |
| 集群侧权限 | kubernetes-admin(cluster-admin) |
无更细 RBAC 划分 |
| 节点登录 | root 免密公钥 |
密钥在 ~/.ssh/,勿外传 |
风险与改进方向
no_root_squash是fsGroup能正常工作的前提(改为root_squash会导致挂载失败),故不宜随意收紧; 如需收紧,应改为all_squash,anonuid/anongid并让 Pod 用runAsUser/runAsGroup对齐(属变更,走 SOP 流程)。- 建议限制宿主机 SSH 来源、并把
192.168.31.0/24内的客户端纳管清点。
证书与轮换:本服务不使用 TLS 证书;NFS 采用 sec=sys(AUTH_SYS,基于 UID/GID 信任)。
凭据轮换:无静态凭据需轮换;SSH 密钥与 kubeconfig 的轮换沿用集群既有制度。
审计:当前无独立审计日志;服务端操作痕迹在 journalctl -u nfs-server,集群侧在 CSI 组件日志与 API Server 审计(若开启)。
10. 容量与性能¶
当前水位基线(2026-09-19 实测)
| 对象 | 容量 | 已用 | 使用率 | 备注 |
|---|---|---|---|---|
/srv/nfs/k8s(SSD 后端) |
118G | 212M | 1% | loop 上限 120GiB |
/srv/nfs/k8s-hdd(HDD 后端) |
492G | 1.1G | 1% | loop 上限 500GiB |
/data/nfs-k8s-snap(快照) |
与 /data 共池 |
— | — | 弹性占用 |
/data(HDD 本体) |
1.8T | 116G | 7% | 含 VM qcow2、HDD 后端镜像、快照、Slurm 状态 |
/(宿主 SSD) |
220G | 52G | 25% | 含 SSD 后端镜像(按需增长)、3 个 etcd raw |
重要特性:容量不被强制
VCS 层面的请求值(PVC storage)在 NFS 后端不被强制,它只是记账:
# 看某个卷实际占了多少
PVC_DIR=$(kubectl get pv <pv名> -o jsonpath='{.spec.csi.volumeHandle}' | awk -F'#' '{print $2"/"$3}')
sudo du -sh "/${PVC_DIR}" # 注意 volumeHandle 里的 share 是以 / 开头形式的相对路径
后端已启用 prjquota(项目配额),需要硬限额时手工设置:
sudo setquota -P <projectID> <软块上限> <硬块上限> 0 0 /srv/nfs/k8s # projectID 与目录需先建立映射
sudo repquota -P /srv/nfs/k8s
扩容触发条件与操作
触发:任一后端使用率 > 80%,或 / > 80%,或业务报 ENOSPC。
# 以 HDD 后端扩到 800GiB 为例(高危:改动 loop 镜像与文件系统)
df -h /data # 先确认 /data 余量足够
sudo truncate -s 800G /data/nfs/k8s-hdd-500g.img # 稀疏扩展文件(按需增长,不会立即占满)
sudo losetup -c /dev/loop19 2>/dev/null || sudo blockdev --rereadpt /dev/loop19 # 让 loop 识别新大小
sudo resize2fs /dev/loop19 # 在线扩容文件系统(挂载状态下可执行)
findmnt -no SOURCE,SIZE /srv/nfs/k8s-hdd # 预期容量变大
# SSD 后端同理,注意同时受宿主根分区余量约束(当前可用约 157G)
注意:
StorageClass里的allowVolumeExpansion只影响能否修改 PVC 请求值, 与后端真实容量无关——扩容必须按上面步骤操作后端。
已知瓶颈与优化方向
| 瓶颈 | 说明 | 方向 |
|---|---|---|
| HDD 与 VM 抢盘 | HDD 卷、快照归档、Slurm 状态、4 台 VM 的 qcow2 共用一块物理盘 | 迁移到独立盘;或把 IO 敏感业务放 SSD 版 SC |
| loop 叠加 | 后端为文件 + loop,随机 IO 比裸设备差 | 生产环境考虑独立块设备 |
| 单点无冗余 | 服务端与全部计算同宿主 | 将 NFS 服务端迁至独立主机 |
| 容量不强制 | 单卷可写穿共享池,影响同后端其它卷 | 用 prjquota 对关键卷设限额 |
async 丢数据风险 |
宿主掉电可能丢最近写入 | 数据库所在导出可单独改回 sync(按导出粒度) |
11. 附录¶
术语表
| 术语 | 含义 |
|---|---|
| StorageClass (SC) | 存储类,PVC 通过它选择后端与参数 |
| PV / PVC | 持久卷 / 持久卷声明,PVC 是应用的申请,PV 是实际分配的对象 |
| CSI | 容器存储接口,K8s 与存储驱动之间的标准协议 |
volumeHandle |
PV 上的后端标识,格式 <server>#<share>#<subdir>,据此定位宿主上的目录 |
| provision | 动态供给:由驱动自动创建后端目录并绑定 PV |
| fsGroup | Pod 声明的属组,kubelet 会据此改卷内文件属组(本方案依赖导出 no_root_squash) |
| nfsd threads | 服务端处理 RPC 的线程数 |
nconnect |
同一挂载使用多条 TCP 连接并行 RPC |
相关文档与交付物
| 路径(相对项目根) | 说明 |
|---|---|
nfs-storage/RUNBOOK.md |
本文件 |
nfs-storage/SOP.md |
变更实施、验证与回滚 |
nfs-storage/backup/README.md |
备份台账(含原位 .bak 的位置与可删条件) |
nfs-storage/values-nfs-csi.yaml |
核心实现体:客户端参数与两个 StorageClass |
nfs-storage/manifests/ |
CRD、Secret、快照落盘分流清单 |
nfs-storage/examples/ |
四个验证用例(PVC / 跨节点 / 快照恢复 / HDD) |
nfs-mount-tuning/docs/SOP.md |
挂载参数调优的变更记录 |
修订记录
| 日期 | 修改人 | 变更内容 |
|---|---|---|
| 2026-09-19 | 运维 | 首版:基于 SOP.md 当前状态提炼运行手册,含 6 个故障场景、水位基线与建议告警 |
待补项
- 宿主
reboot后的自愈验证(对应 SOP 第 6 章 V11)尚未执行,需约定窗口。 - 监控告警规则尚未落地(第 7 章建议表)。
- 整机恢复与镜像级回滚尚未演练。
来源:
/home/rain/Documents/k8s_ins/nfs-storage/RUNBOOK.md| 最后更新:2026-09-19 16:28(由build/sync.py同步,请勿在站点侧修改)