ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

K8S NFS动态供给实战:StorageClass与Provisioner部署指南

K8S NFS动态供给实战:StorageClass与Provisioner部署指南

1. 为什么要在 K8S 里折腾 NFS 动态供给

1.1 从一次存储告警说起

事情的起因很简单:某天凌晨收到一条告警,某个有状态服务的 Pod 因为 PVC 一直处于 Pending 状态起不来,业务直接中断。排查下来发现,之前团队用的是静态 PV 的方式——每上一个新服务,运维就得手动创建一个 PV、指定 NFS 路径、配置容量,然后开发再写一个 PVC 去绑定。服务少的时候还能忍,一旦微服务数量上到几十个,这套流程就成了灾难:PV 命名混乱、容量分配靠拍脑袋、NFS 目录权限经常对不上,最要命的是每次扩容都要人工介入。

后来我把整套存储方案换成了 NFS 动态供给(Dynamic Provisioning),配合 StorageClass 和 PVC 自动绑定,新服务上线只需要写一个 PVC 声明,底层 PV 和 NFS 目录自动创建,容量、回收策略全部按预设规则走。这篇文章就把这套方案的完整落地过程拆开讲清楚,包括 NFS 服务端怎么配、Provisioner 怎么选、StorageClass 参数怎么调、PVC 怎么动态挂载,以及我在实际踩坑中总结出来的排查经验。

这套内容适合谁看?如果你正在用 K8S 跑有状态服务(数据库、日志、文件服务、CI 缓存等),并且希望存储这块能自动化、少人工干预,那这篇基本可以直接抄作业。如果你只是刚接触 K8S 存储概念,也能从里面搞明白 PV、PVC、StorageClass、Provisioner 这几个东西到底是怎么串起来的。

1.2 静态供给和动态供给的本质区别

先把概念捋清楚,不然后面配置容易懵。

静态供给(Static Provisioning)的逻辑是:管理员先手动创建好 PV,PV 里写死 NFS 服务器地址、共享路径、容量大小;然后用户创建 PVC,K8S 的绑定控制器去找一个满足条件的 PV 进行绑定。整个过程 PV 是"预先存在"的,PVC 只是去"认领"。

动态供给(Dynamic Provisioning)的逻辑是:管理员只创建一个 StorageClass,里面声明用哪个 Provisioner、用什么参数;用户创建 PVC 时指定这个 StorageClass,Provisioner 收到请求后自动在 NFS 服务端创建对应目录,并生成一个 PV 与之绑定。PV 是"按需生成"的,用户完全不用关心底层路径。

打个生活化的比方:静态供给就像你去酒店,房间是提前开好的,你只能挑一间现成的住;动态供给则是你报个需求(几人间、住几天),前台当场给你开一间新房。前者房间数量固定、管理麻烦,后者按需分配、弹性好。

从运维角度看,动态供给带来的直接收益有三个:一是省人工,不用再手动建 PV;二是容量可控,每个 PVC 申请多少就分配多少;三是回收自动化,PVC 删除后 PV 和 NFS 目录能按策略自动清理。

1.3 整体架构长什么样

在动手之前,先把整个链路在脑子里过一遍,这样配置的时候每一步在干什么就清楚了。

整个架构分三层:

  • 存储层:一台(或多台)NFS 服务器,提供共享目录,所有 K8S 节点都能挂载。
  • 控制层:运行在 K8S 集群里的 NFS Provisioner(也叫 NFS Subdir External Provisioner),它监听 PVC 的创建事件,负责在 NFS 上建目录、建 PV。
  • 使用层:业务 Pod 通过 PVC 声明存储需求,PVC 通过 StorageClass 触发 Provisioner,最终 Pod 挂载到具体的 NFS 子目录。

数据流向是这样的:用户创建 PVC → StorageClass 匹配 → Provisioner 在 NFS 根目录下创建${namespace}-${pvcName}-${pvName}这样的子目录 → 生成 PV 对象 → PVC 与 PV 绑定 → Pod 挂载 PVC → 容器内看到的就是那个 NFS 子目录。

理解了这个链路,后面遇到 PVC 一直 Pending 的时候,你就知道该从哪一环开始查了。

2. NFS 服务端与 Provisioner 的选型与准备

2.1 NFS 服务端的基础配置

NFS 服务端这块,我假设你用的是一台独立的 Linux 主机(CentOS 或 Ubuntu 都行),IP 假设为192.168.1.100,共享目录定为/data/nfs/k8s。以下命令在服务端执行。

先装包。CentOS 系用yum install -y nfs-utils rpcbind,Ubuntu 系用apt install -y nfs-kernel-server。装完之后创建共享目录并设置权限:

mkdir -p /data/nfs/k8s chmod 777 /data/nfs/k8s

这里权限给 777 是图省事,生产环境建议根据实际 UID/GID 精细化控制,后面注意事项里会细说。

然后编辑/etc/exports,写入共享规则:

/data/nfs/k8s 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

几个参数的含义必须搞清楚,不然容易出权限问题:

  • rw:读写权限,只读的话用ro。
  • sync:同步写入,数据先落盘再返回,安全但慢;async快但有丢数据风险,生产建议sync。
  • no_subtree_check:关闭子树检查,提升兼容性和性能,现代 NFS 基本都建议关。
  • no_root_squash:允许客户端的 root 用户以 root 身份操作共享目录。这个参数很关键,因为很多容器默认以 root 运行,如果开了root_squash,root 会被映射成匿名用户,写文件就会 Permission denied。

配置完执行生效命令:

exportfs -r systemctl enable --now nfs-server rpcbind

验证一下共享是否生效,在服务端执行showmount -e localhost,应该能看到/data/nfs/k8s这一行。然后在任意一个 K8S 节点上测试挂载:

mount -t nfs 192.168.1.100:/data/nfs/k8s /mnt

能挂上并且能读写,说明服务端没问题。这一步千万别跳过,我见过太多人 Provisioner 配了半天,最后发现是 NFS 服务端防火墙没放行 2049 端口。

2.2 Provisioner 的选型对比

NFS 动态供给的核心是 Provisioner,市面上有几个选择,我列个表对比一下,方便你按场景选。

方案维护状态是否需额外组件适用场景我的评价
nfs-subdir-external-provisioner活跃否,直接连现有 NFS已有 NFS,想快速上动态供给首选,轻量稳定
nfs-client-provisioner(旧版)已归档否老集群兼容不推荐新项目用
CSI NFS Driver活跃需部署 CSI追求标准化、多协议复杂但规范
Rook CephFS活跃需整套 Ceph大规模、高性能重,小集群杀鸡用牛刀

对于绝大多数中小集群,nfs-subdir-external-provisioner是最优解。它不需要你改造现有 NFS,直接复用,部署就是一个 Deployment,维护成本极低。它会在 NFS 根目录下按namespace-pvcname-pvname的规则建子目录,每个 PVC 一个独立目录,隔离性够用。

CSI NFS Driver 更"正统",支持快照、扩容等高级特性,但部署复杂度高一个量级,如果你没有这些高级需求,没必要上。Rook CephFS 适合有专门存储团队的大规模场景,个人和小团队玩不转。

我选 nfs-subdir-external-provisioner 的核心理由是:它把"动态供给"这件事做得足够简单,而简单意味着故障点少。存储这东西,稳定压倒一切。

2.3 部署前的 RBAC 与命名空间准备

Provisioner 要能监听 PVC、创建 PV,就必须有对应的 RBAC 权限。这部分官方 Helm Chart 会自动创建,但如果你像我一样喜欢手动部署、把每个对象都看清楚,那就得自己写。

先建命名空间:

kubectl create namespace nfs-provisioner

然后是 ServiceAccount、ClusterRole、ClusterRoleBinding 三件套。ClusterRole 需要授予的权限包括:对persistentvolumes的 get/list/watch/create/delete,对persistentvolumeclaims的 get/list/watch/update,对events的 create/update/patch,以及对storageclasses的 get/list/watch。

这里有个经验点:权限宁可精确也不要图省事给 cluster-admin。我见过有人直接绑 cluster-admin 图快,结果 Provisioner 出 bug 时误删了别的资源。存储组件权限收紧一点,出问题影响面小。

RBAC 配置好之后,Provisioner 的 Deployment 里要指定serviceAccountName: nfs-provisioner,否则它连 PVC 都读不到,日志里会一直报 forbidden。

3. StorageClass 与 Provisioner 的完整部署实操

3.1 Provisioner Deployment 的关键参数

Provisioner 的 Deployment 是整个方案的心脏,我把关键配置逐项拆开讲。核心是它的启动参数和环境变量。

启动参数里最重要的是--provisioner,这个名字必须和后面 StorageClass 里的provisioner字段完全一致,否则 StorageClass 找不到它。我一般命名为k8s-sigs.io/nfs-subdir-external-provisioner。

环境变量有四个必填:

  • PROVISIONER_NAME:和启动参数保持一致。
  • NFS_SERVER:NFS 服务端 IP,比如192.168.1.100。
  • NFS_PATH:NFS 共享根路径,比如/data/nfs/k8s。
  • POD_NAMESPACE:Provisioner 自己所在的命名空间,用 fieldRef 自动注入。

还有一个可选但强烈建议配的:ENABLE_LEADER_ELECTION,多副本部署时开启选主,避免多个实例同时处理同一个 PVC 导致重复建目录。

Deployment 的 volumes 部分要挂载 NFS,这样 Provisioner 容器内才能操作 NFS 目录:

volumes: - name: nfs-client-root nfs: server: 192.168.1.100 path: /data/nfs/k8s

容器内挂载点设为/persistentvolumes,Provisioner 就是在这个目录下创建子目录的。这个路径和 StorageClass 里的archiveOnDelete等参数配合工作,别改乱。

3.2 StorageClass 参数逐项拆解

StorageClass 是用户和 Provisioner 之间的"契约",用户只认它。下面是我生产在用的配置,逐行解释。

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-dynamic annotations: storageclass.kubernetes.io/is-default-class: "false" provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: archiveOnDelete: "true" pathPattern: "${.PVC.namespace}/${.PVC.name}" reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true

provisioner字段必须和 Provisioner 的PROVISIONER_NAME一致,这是绑定的唯一凭据。

archiveOnDelete: "true"是个非常实用的参数。默认情况下 PVC 删除后,NFS 上的目录也会被删掉,数据就没了。设为 true 后,删除 PVC 时目录会被重命名为archived-xxx保留下来,相当于给了一次"后悔药"。生产环境我强烈建议开这个,误删 PVC 的事故我见过不止一次。

pathPattern控制目录命名规则。默认是${namespace}-${pvcName}-${pvName},我改成${.PVC.namespace}/${.PVC.name},好处是目录结构按命名空间分层,一眼就能看出哪个目录属于哪个服务,排查问题时特别方便。

reclaimPolicy: Delete表示 PVC 删除后 PV 也删除。配合archiveOnDelete使用,实际效果是"PV 删了但数据目录归档保留"。如果你希望数据永久保留,可以设为Retain,但那样 PV 会一直占着,需要手动清理。

volumeBindingMode: Immediate表示 PVC 一创建就立即绑定。对于 NFS 这种网络存储,Immediate 没问题。如果是本地盘那种有节点亲和性的存储,才需要用WaitForFirstConsumer。

allowVolumeExpansion: true允许后续扩容 PVC。注意 NFS 本身没有容量限制,这个参数更多是让 K8S 层面允许修改 PVC 的 requests,实际容量还是看 NFS 服务端磁盘。

3.3 部署验证与状态检查

所有 YAML 应用完之后,按这个顺序验证,一步都不能少。

第一步,看 Provisioner Pod 是否 Running:

kubectl get pods -n nfs-provisioner

如果 CrashLoopBackOff,八成是 RBAC 或 NFS 挂载问题,看日志:

kubectl logs -n nfs-provisioner deploy/nfs-client-provisioner

第二步,看 StorageClass 是否创建成功:

kubectl get sc

确认nfs-dynamic在列表里,PROVISIONER 列显示的名字和配置一致。

第三步,创建一个测试 PVC 验证动态供给:

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-pvc spec: storageClassName: nfs-dynamic accessModes: - ReadWriteMany resources: requests: storage: 1Gi

应用后执行kubectl get pvc,正常应该几秒内从 Pending 变成 Bound。同时kubectl get pv能看到自动生成的 PV。再去 NFS 服务端ls /data/nfs/k8s,应该能看到default/test-pvc这样的目录。

这三步全过,说明动态供给链路完全打通。任何一步卡住,对照第 5 章的排查表处理。

4. PVC 动态挂载到 Pod 的实战

4.1 一个完整的 Deployment 挂载示例

PVC 建好之后,怎么挂到 Pod 里是最后一步。我拿一个典型的 Web 服务举例,它需要把上传的文件持久化到 NFS。

先建 PVC:

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: web-uploads-pvc namespace: default spec: storageClassName: nfs-dynamic accessModes: - ReadWriteMany resources: requests: storage: 5Gi

注意accessModes用的是ReadWriteMany,这是 NFS 相比块存储的最大优势——多个 Pod 可以同时读写同一个卷。如果你的服务是多副本部署且共享文件,必须用 RWX,用 RWO 的话第二个 Pod 会挂载失败。

然后是 Deployment:

apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: containers: - name: web image: nginx:1.25 volumeMounts: - name: uploads mountPath: /usr/share/nginx/html/uploads volumes: - name: uploads persistentVolumeClaim: claimName: web-uploads-pvc

三个副本共享同一个 PVC,写入的文件所有副本都能看到。这就是 NFS 动态供给在无状态多副本场景下的典型用法。

4.2 挂载权限与 UID 匹配的坑

挂载本身不难,难的是权限。这是 NFS 动态供给里最容易翻车的地方,我单独拎出来讲。

问题现象:Pod 起来了,但容器内往挂载目录写文件时报Permission denied。原因通常是容器内进程的 UID 和 NFS 目录的属主不匹配。

NFS 的权限模型是基于 UID/GID 的。Provisioner 创建目录时,默认属主是运行 Provisioner 的用户(通常是 root)。如果容器内进程以非 root 用户(比如 UID 1000)运行,就会写不进去。

解决办法有三个,按推荐程度排序:

方案一:在容器里指定 runAsUser 匹配目录属主。如果 NFS 目录属主是 root,就让容器也以 root 跑(securityContext.runAsUser: 0)。简单直接,但安全性差。

方案二:用 initContainer 改目录权限。启动一个 initContainer,以 root 身份执行chown -R 1000:1000 /mnt/data,把目录属主改成业务 UID。这是生产最常用的做法,兼顾安全和可用。

方案三:NFS 服务端配置 all_squash 加 anonuid。在/etc/exports里加all_squash,anonuid=1000,anongid=1000,把所有访问者都映射成 UID 1000。适合整个 NFS 只服务一类应用的场景。

我一般用方案二,灵活且不影响其他服务。initContainer 的配置大概是这样:

initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /mnt/data"] securityContext: runAsUser: 0 volumeMounts: - name: uploads mountPath: /mnt/data

4.3 多副本共享读写的注意事项

多副本共享 NFS 卷虽然方便,但有几个坑必须提前知道。

第一,文件锁问题。NFS 的文件锁(flock/fcntl)在旧版本协议下支持不完善,如果你的应用依赖文件锁做并发控制(比如某些数据库、某些队列),可能会出诡异问题。NFSv4 对锁的支持好很多,建议服务端强制用 v4。

第二,性能瓶颈。所有副本的 IO 都打到同一台 NFS 服务器,副本数一多,NFS 的网卡和磁盘就是瓶颈。我实测过,单台千兆网卡的 NFS,跑 5 个以上高频写副本就开始有明显延迟。这种场景要么升级万兆网卡,要么换分布式存储。

第三,缓存一致性。NFS 有客户端缓存,多个节点同时读写同一文件时,可能出现 A 节点写了、B 节点读到的还是旧内容。对一致性要求高的场景,挂载时加noac参数关闭属性缓存,但会牺牲性能。

所以我的建议是:NFS 动态供给适合"共享读、少量写"或"各副本写各自文件"的场景,比如静态资源、上传目录、日志归档。如果是高频并发写同一文件,老老实实上分布式存储。

5. 常见问题排查与避坑经验

5.1 PVC 一直 Pending 的排查路径

PVC Pending 是最常见的问题,排查要按链路顺序来,别乱试。

先看 PVC 的 Events:

kubectl describe pvc <pvc-name>

Events 里通常会直接告诉你原因。我整理了一张速查表:

报错信息根本原因解决方法
no persistent volumes availableStorageClass 名字写错或不存在检查 PVC 的 storageClassName 拼写
waiting for a volume to be createdProvisioner 没运行或没权限检查 Provisioner Pod 状态和日志
failed to provision volumeNFS 挂载失败或目录创建失败检查 NFS 连通性和服务端权限
storageclass not foundStorageClass 未创建kubectl get sc 确认
forbiddenRBAC 权限不足检查 ClusterRole 绑定

如果 Events 信息不够,直接看 Provisioner 日志,它会打印详细的失败原因:

kubectl logs -n nfs-provisioner deploy/nfs-client-provisioner --tail=100

日志里如果出现mount failed,基本就是 NFS 服务端的问题;出现create directory failed,是 NFS 目录权限问题;出现forbidden,是 RBAC 问题。

5.2 NFS 挂载超时与网络排查

Provisioner 日志里报mount: connection timed out或no route to host,说明 K8S 节点到 NFS 服务端的网络不通。按这个顺序查:

第一步,在节点上直接 ping NFS 服务端 IP,确认网络可达。

第二步,检查 NFS 端口是否放行。NFS 依赖 2049(nfsd)和 111(rpcbind)两个端口,防火墙必须放行:

firewall-cmd --add-service=nfs --permanent firewall-cmd --add-service=mountd --permanent firewall-cmd --add-service=rpc-bind --permanent firewall-cmd --reload

第三步,在节点上手动挂载测试:

mount -t nfs 192.168.1.100:/data/nfs/k8s /mnt

如果手动挂不上,那就是 NFS 服务端或网络的问题,跟 K8S 无关。如果手动能挂、Provisioner 挂不上,那多半是 Provisioner 的 volumes 配置写错了 IP 或路径。

这里有个隐蔽的坑:NFS 服务端重启后,客户端的挂载可能变成 stale(失效)状态,表现为Stale file handle错误。这时候需要重新挂载,K8S 里就是重启对应的 Pod。生产环境建议给 NFS 服务端做高可用,避免单点重启。

5.3 删除 PVC 后数据丢失的预防

这是最痛的坑,没有之一。默认配置下,删除 PVC 会连带删除 NFS 上的数据目录,如果业务方误删了 PVC,数据就没了。

预防措施有三层:

第一层,StorageClass 开archiveOnDelete: "true"。这样删除 PVC 时目录会被重命名为archived-<pvcname>-<pvname>保留,数据还在,只是 PV 没了。需要恢复时手动把目录改回名字,再建 PVC 绑定。

第二层,NFS 服务端做定期快照或备份。光靠 archive 不够,万一有人手贱把 archived 目录也删了呢?我一般用 rsync 每天增量备份 NFS 根目录到另一台机器,保留 7 天。

第三层,生产环境用reclaimPolicy: Retain。这样 PV 删除后数据目录完全不动,需要人工确认后再清理。代价是 PV 会一直占着,需要定期巡检清理。适合核心数据。

三层防护叠加,基本能杜绝误删事故。我个人的配置是:普通业务用 Delete + archiveOnDelete,核心数据用 Retain + 备份。

5.4 性能调优的几个实用参数

NFS 默认配置在 K8S 场景下不一定最优,几个可以调的参数:

挂载参数rsize和wsize:控制单次读写的数据块大小,默认 4K 或 64K,调到 1M(rsize=1048576,wsize=1048576)能显著提升大文件吞吐。但要注意,这个参数在 K8S 的 PV 里配置比较麻烦,因为动态供给生成的 PV 用的是 Provisioner 的默认挂载参数。要改的话得在 Provisioner 的 Deployment 里通过mountOptions传。

nconnect参数:NFSv4.1 以上支持多连接,nconnect=4能让单个挂载用 4 条 TCP 连接,吞吐翻倍。这个参数对高并发场景提升明显,但需要内核和 NFS 版本支持。

服务端asyncvssync:前面说过,sync安全但慢。如果业务能容忍少量数据丢失风险(比如缓存类),可以改async换性能。但数据库这类绝对不能改。

调优这事没有银弹,得根据实际业务压测。我的建议是先用默认配置跑起来,有性能问题再针对性调,别一上来就堆参数,那样出问题都不知道是哪个参数导致的。

6. 从单机 NFS 到生产可用的演进思路

6.1 单点 NFS 的风险与缓解

前面整套方案跑下来,你会发现有个绕不开的问题:NFS 服务端是单点。它一挂,所有挂载它的 Pod 全部 IO 阻塞,业务雪崩。

缓解单点风险,有几个层次的方案:

最轻量的是做 NFS 服务端高可用,用 keepalived + drbd 或者 corosync + pacemaker 做双机热备,VIP 漂移。配置复杂,但能扛住单机故障。

中等方案是换分布式存储,比如 Ceph、GlusterFS、Longhorn。这些方案本身有副本机制,单节点故障不影响。代价是部署和运维复杂度上一个台阶,需要专门的存储知识。

最省事的是接受单点,但做好监控和快速恢复。NFS 服务端加磁盘监控、进程监控,出问题第一时间告警;同时准备好备用机器,故障时能快速切换。适合对可用性要求没那么极致的内部系统。

我的实际选择是:核心业务上分布式存储,边缘业务用单机 NFS + 监控。不是所有服务都值得为高可用付出那么大成本,按业务重要性分级处理更务实。

6.2 什么场景该换掉 NFS

NFS 动态供给不是万能的,以下几种情况建议尽早换方案:

  • 需要高 IOPS 的数据库:NFS 的网络延迟决定了它不适合跑 MySQL、PostgreSQL 这类对 IO 延迟敏感的服务。这类应该用本地 SSD 或高性能块存储。
  • 需要快照和克隆的场景:NFS Provisioner 不支持快照,如果你的 CI/CD 需要快速克隆环境,得用支持 VolumeSnapshot 的 CSI 驱动。
  • 超大规模集群:几千个 PVC 时,单台 NFS 的目录数量和并发连接会成为瓶颈,需要分布式存储。
  • 需要精细 QoS 的场景:NFS 没法限制单个卷的 IOPS 和带宽,多租户场景下容易互相影响。

判断标准很简单:如果你的存储需求已经超出了"共享文件"这个范畴,就该考虑换方案了。NFS 的定位就是简单共享文件,别让它干超出能力范围的活。

6.3 我踩过的三个真实坑

最后分享三个我在实际部署中踩过的坑,都是文档里不会写的。

坑一:Provisioner 副本数设成 2 但没开选主。结果两个实例同时处理同一个 PVC,在 NFS 上创建了两个目录,PV 却只绑定了一个,另一个成了孤儿目录。后来开了ENABLE_LEADER_ELECTION才解决。多副本部署 Provisioner 必须开选主,这是硬性要求。

坑二:NFS 服务端 exports 用了通配符网段,但集群节点跨网段。配置写的是192.168.1.0/24,结果有个节点在192.168.2.0/24,那个节点上的 Pod 全部挂载失败。排查了半天才发现是网段没覆盖全。exports 的网段一定要覆盖所有可能调度 Pod 的节点,或者干脆用具体 IP 列表。

坑三:PVC 扩容后 Pod 没重启,容量没生效。NFS 的扩容其实只是改了 K8S 层面的 requests,底层 NFS 目录容量本来就是共享磁盘的,不存在"扩容"一说。但如果你用的是支持扩容的 CSI 驱动,扩容后需要重启 Pod 才能让文件系统感知新容量。这个坑让我一度以为扩容失败了。

这三个坑的共同点是:都跟"想当然"有关。存储这东西,每个环节都得实际验证,不能凭经验假设。

6.4 一套可直接复用的部署清单

把整套流程浓缩成一份清单,照着做基本不会漏:

  1. NFS 服务端装包、建目录、配 exports、放行防火墙、验证挂载。
  2. K8S 集群所有节点装 nfs-utils,确保能挂 NFS。
  3. 创建 nfs-provisioner 命名空间和 RBAC。
  4. 部署 Provisioner Deployment,配好 NFS_SERVER、NFS_PATH、PROVISIONER_NAME。
  5. 创建 StorageClass,配 archiveOnDelete、pathPattern、reclaimPolicy。
  6. 建测试 PVC 验证 Bound 状态。
  7. 建测试 Pod 验证挂载和读写。
  8. 配置监控告警(Provisioner 状态、NFS 服务端磁盘和进程)。
  9. 配置 NFS 数据定期备份。
  10. 文档化 PVC 命名规范和容量申请流程。

这套清单我在三个不同规模的集群上跑过,基本一次成型。唯一需要根据环境调整的是网段、路径和权限配置,其余都是通用的。

存储这块,我的核心体会是:动态供给解决的是"自动化"问题,但"可靠性"问题得靠架构和运维兜底。Provisioner 让 PVC 创建变得无感,但 NFS 单点、数据备份、权限管理这些事,一个都省不了。把自动化做好的同时,别忘了给数据上保险。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进