跳转至

NFS 存储(k8s_ins)运行与使用手册 RUNBOOK

适用对象:接手 k8s_ins 集群 NFS 存储的日常运维与值班人员 读者假设:没接触过这套存储,凌晨被叫起来也能照着做完 关联变更档案:docs/SOP.md(2026-09-19 挂载参数优化)、nfs-storage/SOP.md(2026-09-17 存储类交付) 最近更新:2026-09-19

目录


1. 服务概览

干什么用的:为 k8s_ins 集群提供可 RWX、可动态供给、支持快照的共享存储。 集群里的 minio、mariadb、Prometheus、Grafana、Alertmanager、openldap 都跑在它上面。

由哪些组件构成

组件 位置 作用
NFS 服务端(nfs-kernel-server) 宿主机 dell-R620(192.168.31.249) 导出三个目录,是本服务唯一的存储后端
后端存储 两个 loop 镜像文件 + 一个 HDD 目录 见下表「三个后端」
CSI 驱动(csi-driver-nfs 4.13.4) 集群 kube-system csi-nfs-controller(1 副本,管创建/删除/快照)+ csi-nfs-node(DaemonSet,4 节点,管挂载/卸载)
快照控制器(snapshot-controller) 集群 kube-system 处理 VolumeSnapshot / VolumeSnapshotContent 对象
StorageClass 集群级 nfs-csi(默认,走 SSD 后端)、nfs-csi-hdd(HDD 后端)

三个后端(不要混)

后端路径 承载 底层 当前水位
/srv/nfs/k8s SSD 卷数据(SC nfs-csi) loop 镜像 /kvm/nfs/k8s-120g.img(上限 120GiB,位于系统盘 sda3) 212M / 118G(1%)
/srv/nfs/k8s-hdd HDD 卷数据(SC nfs-csi-hdd) loop 镜像 /data/nfs/k8s-hdd-500g.img(上限 500GiB,位于 /data) 1.1G / 492G(1%)
/data/nfs-k8s-snap 快照归档(tar.gz),刻意与卷数据分开 /data 目录 见第 10 章

/etc/exports 里还有一条 /data/nfs/slurm-state,不属于本服务(更早的 Slurm 交付),不要去动它。

依赖谁:宿主机的 sdb1(/data)与系统盘;宿主机上的 4 台 k8s VM 反过来又跑在 /data 上 ——存储与计算同宿主,无冗余。

异常时的业务影响

异常 影响
宿主机宕机 存储与全部计算节点同时中断,所有 Pod 的卷挂载失败,直至宿主恢复
nfs-server 停 已挂载的 Pod 因 hard 挂载而 I/O 阻塞(不是报错),表现为服务挂起;新 Pod 无法挂载
HDD 后端变慢 nfs-csi-hdd 上的 Prometheus、openldap 明显变慢(该盘与 VM 的 qcow2 争抢)
系统盘写满 nfs-csi(SSD 卷)与宿主自身、etcd 一起受影响,风险最高

重要性等级:高。它是集群所有有状态业务的底座,且单点无冗余。

2. 接入与凭据

地址与端口

项 值
NFS 服务端地址 192.168.31.249(宿主 dell-R620)
NFS 端口 2049/tcp(NFSv4);20048(mountd)、111(rpcbind,v3 兼容用)
可访问网段 192.168.31.0/24(在 /etc/exports 中按网段授权)
协议与认证 NFSv4.2,sec=sys(AUTH_SYS,无 Kerberos、无账号密码)

凭据

用途 存放位置 取用命令
集群操作 ~/.kube/config(宿主机 rain 用户) kubectl config current-context → 预期 kubernetes-admin@kubernetes
NFS 挂载 无凭据,按 IP 网段授权(no_root_squash) 无需取用
Grafana 登录 Secret monitoring/grafana-admin kubectl -n monitoring get secret grafana-admin -o jsonpath='{.data.admin-user}' \| base64 -d(密码把 admin-user 换成 admin-password)
宿主机提权 sudo 免密(用于改 /etc/exports、/etc/fstab、/proc/fs/nfsd/threads) sudo -n true && echo ok → 预期 ok

客户端接入示例

宿主机或任何 192.168.31.0/24 内的机器手工挂载(演示用,不要挂到 /data 上):

sudo mkdir -p /mnt/nfs-test
sudo mount -t nfs -o vers=4.2,nconnect=4,hard,timeo=600,retrans=3,noatime \
  192.168.31.249:/srv/nfs/k8s /mnt/nfs-test
findmnt -no OPTIONS --target /mnt/nfs-test
# 预期:rw,noatime,vers=4.2,rsize=1048576,wsize=1048576,hard,proto=tcp,nconnect=4,timeo=600,retrans=3,sec=sys…
sudo umount /mnt/nfs-test

在集群里用(PVC 片段,可直接粘贴):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-data
  namespace: default
spec:
  storageClassName: nfs-csi        # 要 HDD 后端就写 nfs-csi-hdd
  accessModes: [ReadWriteMany]
  resources:
    requests:
      storage: 5Gi                 # 仅记账,实际容量不受限(见第 10 章)

3. 日常速查表

最高频的 10 条,先看这里。

# 1) 三处导出是否正常(含 sync/async 状态)
sudo exportfs -v

# 2) nfsd 工作线程数(当前应为 32)
sudo cat /proc/fs/nfsd/threads

# 3) 三个后端的容量水位
df -h /srv/nfs/k8s /srv/nfs/k8s-hdd /data

# 4) 每个 PVC 子目录的实际占用
sudo du -sh /srv/nfs/k8s/* /srv/nfs/k8s-hdd/* 2>/dev/null

# 5) 集群存储对象一览
kubectl get sc && kubectl get pv && kubectl get pvc -A

# 6) CSI 组件是否健康
kubectl -n kube-system get pods -l app=csi-nfs-node && kubectl -n kube-system get pods -l app=csi-nfs-controller

# 7) 服务端日志(先定位再过滤,不要整篇拉)
sudo journalctl -u nfs-server --since "1 hour ago" | tail -50

# 8) 重载导出(改完 /etc/exports 后执行,幂等)
sudo exportfs -ra && sudo exportfs -v

# 9) 看某节点上 NFS 挂载的**实际参数**(把 Pod 名换成该节点上的 csi-nfs-node)
kubectl -n kube-system exec csi-nfs-node-<后缀> -c nfs -- nsenter -t 1 -m findmnt -t nfs4 -o TARGET,OPTIONS --noheadings

# 10) 本工程:预演与幂等复核
cd /home/rain/Documents/k8s_ins/nfs-mount-tuning
.venv/bin/python main.py --config config/tune.yaml --dry-run          # 只看不改
.venv/bin/python main.py --config config/tune.yaml --skip-measure     # 当前状态应全部 skipped

4. 日常操作

4.1 状态检查

# 服务端进程与导出
systemctl is-active nfs-server                       # 预期:active
sudo exportfs -v                                     # 预期:三处导出各一行,含 async,wdelay,rw,no_root_squash

# CSI 组件(4 个 node 插件 + 1 controller + 1 snapshot-controller)
kubectl -n kube-system get pods | grep -E 'csi-nfs|snapshot'
# 预期:csi-nfs-controller-*  5/5 Running;csi-nfs-node-*  4 个 3/3 Running;snapshot-controller-* 1/1 Running

# helm release
helm -n kube-system list | grep nfs-csi              # 预期:STATUS=deployed,CHART=csi-driver-nfs-4.13.4

4.2 启动 / 停止 / 重启 NFS 服务

# 高危:停止或重启 nfs-server 会让所有客户端 I/O 阻塞(hard 挂载),务必先确认无写入高峰
sudo systemctl status nfs-server
sudo systemctl restart nfs-server                    # 预期:无输出;随后 systemctl is-active nfs-server 为 active
# 重启后必须复核,threads 由 /etc/nfs.conf 恢复为 32
sudo cat /proc/fs/nfsd/threads                       # 预期:32(若为 8,见 8.5)
sudo exportfs -v | grep -c async                     # 预期:4(三处本服务导出 + 1 条 slurm-state)

4.3 日志查看

sudo journalctl -u nfs-server --since "30 min ago" | tail -50                 # 服务端服务日志
sudo journalctl -k --since "30 min ago" | grep -i nfsd | tail -30             # 内核 nfsd 相关
kubectl -n kube-system logs -l app=csi-nfs-controller -c nfs --tail=100       # 供给与快照编排
kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=100             # 各节点挂载/卸载
tail -50 /home/rain/Documents/k8s_ins/nfs-mount-tuning/logs/tune.log          # 参数变更工程的日志

4.4 进入容器或主机排障

# 宿主机(就是本机)
ssh rain@192.168.31.249                              # 或直接在本机操作

# 在节点上查看所有 NFS 挂载(借用 node 插件的 hostPID/hostMount)
kubectl -n kube-system get pods -l app=csi-nfs-node -o wide                 # 找到目标节点对应的 Pod
kubectl -n kube-system exec csi-nfs-node-<后缀> -c nfs -- \
  nsenter -t 1 -m findmnt -t nfs4 -o TARGET,OPTIONS --noheadings

# 在业务 Pod 里看卷内容(示例:minio)
kubectl -n server exec minio-0 -- ls -l /data | head

4.5 扩缩容与卷操作

# 扩容 PVC(改了请求值,但 NFS 后端不强制容量,见第 10 章)
kubectl -n <ns> patch pvc <name> -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

# 新增一个导出供新的 StorageClass 使用(顺序不可颠倒)
sudo mkdir -p /srv/nfs/k8s-new
echo '/srv/nfs/k8s-new 192.168.31.0/24(rw,async,no_subtree_check,no_root_squash)' | sudo tee -a /etc/exports
sudo exportfs -ra && sudo exportfs -v
# 再在 nfs-storage/values-nfs-csi.yaml 的 storageClasses 列表里加一项并 helm upgrade

# 删除卷(SC 的 reclaimPolicy=Delete,会自动删后端子目录,高危)
kubectl -n <ns> delete pvc <name>                    # 预期:pv 与后端子目录随后被回收
sudo ls /srv/nfs/k8s                                 # 核对对应 pvc-<uuid> 目录已消失

5. 配置管理

5.1 配置项位置

层 文件 / 对象 关键内容
服务端导出 /etc/exports 三处导出的选项:rw,async,no_subtree_check,no_root_squash
服务端服务 /etc/nfs.conf 的 [nfsd] 段 threads = 32(重启后恢复线程数)
服务端挂载 /etc/fstab 两个 loop 镜像:loop,prjquota,noatime,nofail,x-systemd.device-timeout=10
客户端参数(权威来源) nfs-storage/values-nfs-csi.yaml 两个 StorageClass 的 mountOptions
删除卷所需参数 nfs-storage/manifests/00-mount-options-secret.yaml mountOptions 字符串,必须与上一条一致
快照落盘位置 nfs-storage/manifests/02-volumesnapshotclass.yaml 指向 HDD 的 parameters(每次 helm upgrade 后要重新 apply)
参数变更实现体 nfs-mount-tuning/config/tune.yaml 期望状态(导出选项、nfsd 线程、挂载参数、目标工作负载)

5.2 修改流程(改挂载参数)

# 1) 改期望状态
vi /home/rain/Documents/k8s_ins/nfs-mount-tuning/config/tune.yaml

# 2) 预演,确认将变更的内容
cd /home/rain/Documents/k8s_ins/nfs-mount-tuning
.venv/bin/python main.py --config config/tune.yaml --dry-run

# 3) 执行(会按需静默业务、备份、改配置、patch PV 并重新挂载)
.venv/bin/python main.py --config config/tune.yaml

# 4) 复核幂等
.venv/bin/python main.py --config config/tune.yaml --skip-measure    # 预期:全部 skipped

5.3 生效方式与易错点

易错点 说明
改了 SC 但旧卷没变 mountOptions 固化在 PV 上,只对新建 PV 生效;已有卷要 patch PV 后重启使用方(本工程会自动做)
nconnect 改了没效果 连接级参数只在重新挂载时生效,仅仅重启容器不够,必须让卷重新 stage(本工程通过重启控制器实现)
两处 Secret/values 不一致 删除卷时参数不一致会失败;改一处必须同步另一处(本工程自动同步)
helm upgrade 后快照落回 SSD Helm 会重置 VolumeSnapshotClass 的 parameters,每次升级后必须重新 kubectl apply -f nfs-storage/manifests/02-volumesnapshotclass.yaml,并用 kubectl get volumesnapshotclass csi-nfs-snapclass -o jsonpath='{.parameters}' 复核
改 /etc/exports 后忘记重载 必须 sudo exportfs -ra;只改文件不重载不生效
直接重启 nfs-server 会瞬时中断所有客户端,属高危;优先用 exportfs -ra(导出变更)与 echo N > /proc/fs/nfsd/threads(线程变更),二者都无需重启服务

6. 备份与恢复

6.1 备份策略与位置

对象 备份方式 位置
/etc/exports、/etc/nfs.conf、/etc/fstab 每次变更前由实现体自动 cp -a 并带时间戳 原位同目录 *.bak.<YYYYmmdd-HHmmss>(实现体 --rollback 依赖此约定)
nfs-storage/values-nfs-csi.yaml、00-mount-options-secret.yaml 同上 nfs-mount-tuning/backup/nfs-storage/…
台账 手工维护 nfs-mount-tuning/backup/README.md(每份备份的来源、回滚对象、可删时机)
卷数据本身 无自动备份 只有快照(tar.gz 归档到 /data/nfs-k8s-snap)

重要认知:NFS 后端没有副本、没有 RAID、没有定时备份;三处导出共用同一台宿主的磁盘。 真正意义上的数据保护只有「打快照」,且快照是 tar 归档、恢复耗时与数据量成正比。

6.2 手工备份命令

# 配置文件
sudo cp -a /etc/exports  "/etc/exports.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)"
sudo cp -a /etc/fstab    "/etc/fstab.bak.$(date +%Y%m%d-%H%M%S)"

# 打一份卷快照(示例:server/minio-data)
kubectl -n server get pvc minio-data                          # 先拿到 PVC 名与状态,预期 Bound
# 参考 nfs-storage/examples/03-snapshot-and-restore.yaml 的写法创建 VolumeSnapshot
kubectl get volumesnapshot -A                                  # 预期:READYTOUSE=true
sudo find /data/nfs-k8s-snap -maxdepth 2 -name '*.tar.gz' -exec ls -lh {} \;   # 归档落在 HDD

6.3 恢复步骤(从配置备份回滚)

cd /home/rain/Documents/k8s_ins/nfs-mount-tuning
.venv/bin/python main.py --config config/tune.yaml --rollback      # 预期:三行「已回滚 … <- …」,最后「回滚成功」
sudo exportfs -v && sudo cat /proc/fs/nfsd/threads                 # 复核:导出与线程数回到旧值

集群侧配置的恢复见 docs/SOP.md 第 7.2 节(含 values / Secret 的还原与 helm 升级)。

6.4 演练记录

日期 演练内容 结果
2026-09-19 变更前采集基线、变更后复测;四阶段幂等复跑 通过(详见 docs/SOP.md 第 6 章)
— 从快照恢复整卷到新 PVC 未演练,建议安排

7. 监控与告警

关键指标与查看入口

指标 查看方式 关注阈值
三处后端使用率 df -h /srv/nfs/k8s /srv/nfs/k8s-hdd /data /srv/nfs/k8s 使用率 > 80% 需处理(它与宿主 / 同盘)
单个 PVC 占用 sudo du -sh /srv/nfs/k8s/* /srv/nfs/k8s-hdd/* 单目录接近上限即需清理或迁移
nfsd 线程数 sudo cat /proc/fs/nfsd/threads 应为 32;变为 8 说明 /etc/nfs.conf 未生效或被改回
导出状态 sudo exportfs -v 应有三处本服务导出且含 async
元数据延迟 见下方「延迟自测」 同后端较基线劣化 > 20% 需排查
磁盘压力(HDD) iostat -x 1 观察 /data 所在盘的 %util、await %util 长期 > 90% 说明 HDD 是瓶颈
CSI 组件健康 kubectl -n kube-system get pods \| grep csi-nfs 有非 Running 或重启数持续增长

延迟自测(不装任何东西)

# 重新跑一次度量,与 logs/measure-before-*.json、measure-after-*.json 对比
cd /home/rain/Documents/k8s_ins/nfs-mount-tuning
.venv/bin/python main.py --config config/tune.yaml --stages measure-after
# 报告落在 logs/measure-after-*.json;看 fio 的 p50/p99 与 mountstats 的 avg_rtt_ms

日志位置:服务端 /var/log/syslog 与 journalctl -u nfs-server;集群侧见 4.3。

Grafana / Prometheus 入口(均为 ClusterIP,用端口转发访问)

kubectl -n monitoring port-forward svc/kube-prometheus-stack-grafana 3000:80        # 浏览器 http://localhost:3000
kubectl -n monitoring port-forward svc/kube-prometheus-stack-prometheus 9090:9090   # http://localhost:9090
kubectl -n monitoring port-forward svc/kube-prometheus-stack-alertmanager 9093:9093
kubectl -n kube-system port-forward svc/headlamp 8080:80                            # 集群面板

这套监控自身也跑在本存储上:Grafana/Alertmanager 在 nfs-csi,Prometheus 在 nfs-csi-hdd。 存储故障时监控很可能同时不可用,此时以宿主机命令行(本章与第 3 章)为主要手段。

8. 故障处置

场景 1:Pod 无法挂载 NFS 卷 / 容器写不进卷

症状:Pod 卡在 ContainerCreating;kubectl describe pod 里有 FailedMount; 或容器启动后报 Permission denied。

诊断

kubectl -n <ns> describe pod <pod> | grep -A5 -iE 'failedmount|mount failed'   # 预期:能看到具体挂载报错
kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=50 | grep -iE 'error|fail|denied'
sudo exportfs -v | grep <对应后端路径>                                          # 预期:导出存在且含 rw
sudo systemctl is-active nfs-server                                            # 预期:active

处置

  1. 导出缺失 → 检查 /etc/exports 是否有该路径一行,补上后 sudo exportfs -ra。
  2. 服务端不可达 → 从节点侧验证:kubectl -n kube-system exec csi-nfs-node-<后缀> -c nfs -- nsenter -t 1 -m timeout 5 bash -c 'cat < /dev/null > /dev/tcp/192.168.31.249/2049' && echo ok(预期 ok)。
  3. 写权限问题:本存储依赖导出带 no_root_squash,并由 kubelet 应用 Pod 的 fsGroup 改写 GID。 若改了导出参数(去掉 no_root_squash)会出现「挂载成功但写不进」,需改回并 exportfs -ra。
  4. 单个 Pod 异常而其它正常 → 删除该 Pod 让其重建:kubectl -n <ns> delete pod <pod>。

验证:kubectl -n <ns> get pod <pod> 为 Running,kubectl -n <ns> exec <pod> -- touch /<挂载点>/.write-test && echo ok 返回 ok。

场景 2:Pod 访问卷时「卡住不动」(I/O hang)

症状:容器内 ls / 读写命令长时间无返回;探针失败但 Pod 状态仍是 Running; 日志里没有明显报错。

诊断

systemctl is-active nfs-server                       # 预期:active(若是 failed/inactive 即找到原因)
sudo exportfs -v | wc -l                             # 预期:4 行(导出存在)
kubectl -n kube-system exec csi-nfs-node-<后缀> -c nfs -- \
  nsenter -t 1 -m findmnt -t nfs4 -o TARGET,OPTIONS --noheadings | head    # 能看到挂载,说明是服务端阻塞而非卸载
sudo journalctl -u nfs-server --since "10 min ago" | tail -30

处置

  1. 这是 hard 挂载的设计行为:服务端不可用时 I/O 阻塞而不是报错,以避免数据损坏。 因此优先恢复服务端(sudo systemctl restart nfs-server),而不是改客户端参数。
  2. 服务端恢复后 Pod 会自动继续(通常无需重启 Pod)。
  3. 若长时间无法恢复且业务必须中断,才考虑临时改 soft(不推荐,会带来数据损坏风险), 且必须在事后改回并重启使用方。

验证:容器内 ls 恢复正常;kubectl -n kube-system logs -l app=csi-nfs-node -c nfs --tail=20 无新报错。

场景 3:删除 PVC 后后端目录没回收 / 空间不下降

症状:kubectl delete pvc 成功,PV 已消失,但 df -h 没有变化,后端还留着 pvc-<uuid> 目录。

诊断

kubectl -n <ns> get pvc <name>                        # 预期:NotFound(已删)
kubectl get pv | grep pvc-<uuid>                      # 预期:无(PV 已回收)
sudo ls -ld /srv/nfs/k8s/pvc-<uuid>                   # 预期:目录不存在;若仍存在即为残留
kubectl -n kube-system logs -l app=csi-nfs-controller -c nfs --tail=100 | grep -iE 'delete|error'
sudo du -sh /srv/nfs/k8s/* | sort -h | tail -5        # 找出实际占空间的大目录

处置

  1. 先确认目录确实属于已删除的 PV(对照 kubectl get pv 已无该 uuid),不要凭猜删除。
  2. 残留通常来自删除时挂载参数不一致(SC 与 Secret 的 mountOptions 不同步)导致驱动无法挂载后端清理。 复核:kubectl -n kube-system get secret nfs-mount-options -o jsonpath='{.data.mountOptions}' | base64 -d 与 kubectl get sc nfs-csi -o jsonpath='{.mountOptions}' 是否一致。
  3. 确认无误后手工清理(高危,路径必须精确,禁止通配):
sudo ls -ld /srv/nfs/k8s/pvc-<uuid>          # 再确认一次目标
sudo rm -rf /srv/nfs/k8s/pvc-<uuid>          # 高危:删除后端卷目录,不可恢复
  1. 若删除的是快照归档,位置在 /data/nfs-k8s-snap/snapshot-<uid>/。

验证:sudo du -sh /srv/nfs/k8s 数值下降;df -h /srv/nfs/k8s 可用空间增加。

场景 4:nfs-csi-hdd 上的应用(Prometheus/openldap)极慢

症状:Prometheus 查询卡顿、抓取超时;openldap 认证偶发超时;nfs-csi 上的应用却正常。

诊断

findmnt -no SOURCE,OPTIONS --target /srv/nfs/k8s-hdd      # 确认后端与挂载参数(应含 noatime)
iostat -x 1 3 | grep -A1 '^Device' | tail -5              # 预期:/data 所在盘 %util 高、await 大
sudo du -sh /srv/nfs/k8s-hdd/* | sort -h | tail -5        # 找出占用大的卷
uptime                                                    # 宿主负载(4 台 VM 也在这台机器上)

处置

  1. 这是已知瓶颈:/data 那块 HDD 同时承载 4 台 k8s VM 的 qcow2 与 HDD 卷数据,机械盘随机 IO 天生差。
  2. 缓解手段(按优先级):把 IO 敏感的业务迁到 nfs-csi(SSD);降低 Prometheus 保留期/抓取频率; 确认已启用 noatime(本服务默认已启用)。
  3. 不要在 HDD 上跑数据库类小随机 IO。

验证:iostat -x 1 的 await、%util 回落;应用侧延迟恢复。

场景 5:快照落到了 SSD,撑满 120GiB 预算

症状:打了快照后 /srv/nfs/k8s 出现 snapshot-* 目录;df -h 显示 SSD 后端快速上涨。

诊断

kubectl get volumesnapshotclass csi-nfs-snapclass -o jsonpath='{.parameters}{"\n"}'   # 预期:含 server/share=/data/nfs-k8s-snap
sudo ls -l /srv/nfs/k8s | grep snapshot      # 预期:无输出(若有,说明 parameters 被重置)
sudo find /data/nfs-k8s-snap -maxdepth 2 | head

处置

kubectl apply -f /home/rain/Documents/k8s_ins/nfs-storage/manifests/02-volumesnapshotclass.yaml
kubectl get volumesnapshotclass csi-nfs-snapclass -o jsonpath='{.parameters}{"\n"}'   # 复核已指回 HDD

若 SSD 侧已有归档:确认新快照已分流后,手工清理 SSD 上的 snapshot-* 目录(高危,路径精确)。

验证:新打一个快照,确认归档出现在 /data/nfs-k8s-snap/snapshot-<uid>/ 而非 SSD。

场景 6:nfsd 线程数变成 8 / 导出变成 sync

症状:重启宿主机或他人改过配置后,sudo cat /proc/fs/nfsd/threads 显示 8; 或 sudo exportfs -v 里三处导出回到 sync。

诊断

grep -A3 '^\[nfsd\]' /etc/nfs.conf | grep -v '^#'       # 预期:threads = 32
grep -E '/srv/nfs/k8s|nfs-k8s-snap' /etc/exports        # 预期:三行都含 async
cd /home/rain/Documents/k8s_ins/nfs-mount-tuning && .venv/bin/python main.py --config config/tune.yaml --dry-run
# 预期:若有漂移,会列出「检测到状态漂移: …」

处置

cd /home/rain/Documents/k8s_ins/nfs-mount-tuning
.venv/bin/python main.py --config config/tune.yaml --stages server      # 收敛回期望状态(幂等)

验证:sudo cat /proc/fs/nfsd/threads 为 32;sudo exportfs -v 三处含 async; main.py --skip-measure 复跑全部 skipped。

9. 安全与权限

账号与权限模型

层 模型
NFS 导出授权 按 IP 网段 192.168.31.0/24 授权,sec=sys(AUTH_SYS)——无账号、无密码、无 Kerberos
no_root_squash 已启用:网段内任意主机的 root 可读写全部导出数据。这是 fsGroup 能生效的前提
集群 RBAC 使用 cluster-admin(kubeconfig /home/rain/.kube/config)
宿主提权 sudo 免密

风险与当前取舍

  • no_root_squash + 网段授权 = 内网横向风险:网段内任何机器都能以 root 读写所有卷。 当前环境为隔离测试网段,接受此取舍。若要收紧:改 /etc/exports 为 all_squash,anonuid=<uid>,anongid=<gid> 并为 Pod 设置 runAsUser/runAsGroup,改后需 sudo exportfs -ra 并重启使用方。
  • 无传输加密:sec=sys 下数据在网内明文。跨不可信网络必须改用 sec=krb5* 或走加密隧道。
  • async 导出:如第 10 章所述会放大掉电丢数据风险。
  • 凭据一律不写入本文档;Grafana 等凭据按第 2 章的取用命令从 Secret 读取。

证书与密钥:本服务不使用 TLS/证书(NFS 层无证书)。集群侧组件(Prometheus、Alertmanager)的 TLS Secret 与 monitoring 命名空间内其它 Secret 由 kube-prometheus-stack 管理,轮换遵循该 chart 的方式,与 NFS 无关。

10. 容量与性能

当前水位基线(2026-09-19 实测)

对象 大小 已用 可用
/srv/nfs/k8s(SSD 卷) 118G 212M(1%) 112G
/srv/nfs/k8s-hdd(HDD 卷) 492G 1.1G(1%) 485G
/data(宿主 HDD,含快照归档与 VM qcow2) 1.8T 116G(7%) 1.6T

各卷占用(2026-09-19 实测)

后端 目录 占用 归属
/srv/nfs/k8s pvc-4323db6c-… 159M mariadb
/srv/nfs/k8s pvc-5ca538f8-… 53M grafana
/srv/nfs/k8s pvc-467e1cc4-… 704K minio
/srv/nfs/k8s pvc-3fac44ea-… 8.0K alertmanager
/srv/nfs/k8s-hdd pvc-c44a8887-… 1.1G prometheus
/srv/nfs/k8s-hdd 其余 6 个 pvc-* 各 64K–164K openldap 系列

扩容触发条件与操作

  1. 触发:df -h /srv/nfs/k8s 使用率 > 80%(它与系统盘 sda3 同盘,同时影响宿主与 etcd); 或某卷目录占用接近上限。
  2. 操作:受 loop 镜像上限约束,扩容需要放大镜像文件: sudo truncate -s <新大小> /kvm/nfs/k8s-120g.img && sudo resize2fs /kvm/nfs/k8s-120g.img (高危,会短暂影响导出,须在窗口内执行并先备份/确认空间充足)。
  3. 也可把新业务直接指向 nfs-csi-hdd(500GiB 上限、空间充裕,代价是 IO 慢)。

已知瓶颈与优化方向

瓶颈 说明 方向
PVC 请求值不生效 后端无 CSI 层配额,PVC 的 5Gi 只是记账,可写满整个导出 需要真限额时用 ext4 prjquota 手工给子目录设配额;容量规划按「导出总量」而非「PVC 请求和」
HDD 后端 IO 差 与 4 台 VM 的 qcow2 争抢同一块盘,且叠加 loop 层 IO 敏感业务放 SSD;不要在 HDD 上跑数据库
单点无冗余 存储与计算同宿主,宿主故障即全断 学习环境可接受;要真冗余需把 NFS 服务端迁到独立主机
async 的持久化代价 服务端不落盘即确认,掉电可能丢最近写入 数据库类如需要强持久化,按导出粒度改回 sync 并 exportfs -ra
快照是 tar 归档 非 COW,创建/恢复耗时与数据量成正比 数据量大时可关闭压缩(--set controller.enableSnapshotCompression=false),换取更快但占更多空间

11. 附录

术语表

术语 含义
StorageClass(SC) 集群里的「存储类型」模板,PVC 通过它动态申请卷
PV / PVC 实际的卷对象(PV)与用户的申请(PVC)
RWX / RWO 多节点读写 / 单节点读写;NFS 原生支持 RWX
nconnect 同一挂载使用多条 TCP 连接并行发 RPC,提升并发元数据性能
vers / nfsvers 协议版本;配置里写 nfsvers=,内核回显为 vers=
hard 挂载 服务端不可用时 I/O 阻塞而非报错,优先数据一致性
sync / async 导出 服务端是否在回复前落盘;async 更快但有掉电丢数据风险
no_root_squash 客户端 root 保持 root 身份,是 fsGroup 生效的前提
loop 镜像 用文件当作块设备挂载(/kvm/nfs/k8s-120g.img 等),容量上限即文件大小
prjquota ext4 的目录级配额特性,本服务已启用但未强制

相关文档与交付物

路径(相对项目根) 内容
nfs-mount-tuning/docs/SOP.md 2026-09-19 挂载参数优化的变更档案(背景/目标/步骤/验证/回滚/风险)
nfs-mount-tuning/docs/INCIDENT-20260919-mount-option-judgement.md 变更期间一次判定误报的排障记录
nfs-mount-tuning/backup/README.md 备份台账
nfs-mount-tuning/config/tune.yaml 期望状态配置(日常调整改这里)
nfs-storage/SOP.md 2026-09-17 存储类交付档案(含初始参数与验证清单)
nfs-storage/examples/ PVC 供给、跨节点读写、快照恢复、HDD 卷的验证清单
nfs-mount-tuning/logs/ 参数变更运行日志与 before/after 度量报告

修订记录

日期 修改人 变更内容
2026-09-19 运维(AI 协助) 首次建立 RUNBOOK;覆盖概览、接入、速查、日常操作、配置、备份、监控、6 个故障场景、安全、容量与性能

来源:/home/rain/Documents/k8s_ins/nfs-mount-tuning/docs/RUNBOOK.md | 最后更新:2026-09-19 16:46(由 build/sync.py 同步,请勿在站点侧修改)