8.0 KiB
03-06-k3s 使用 NFS 存储
本文只讲 K3s 集群侧如何使用已安装好的 NFS。
TL;DR
- 自动化验收:
./scripts/verify.sh run 03-06 - 关键前置:按本文「前置条件」准备环境变量/Secret/入口 IP
- 成功判据:达到本文「预期」且 playbook 断言通过
- 排障:见本文「排障」
前置条件
- 已完成
01-05-armv7-nfs服务安装.md - 可从 K3s 节点访问 NFS 服务器与导出目录
方式对比(从简单到复杂)
| 项 | 方式 1:Pod 直接挂 NFS | 方式 2:静态 NFS(PV + PVC) | 方式 3:动态 NFS(选装 provisioner) |
|---|---|---|---|
| 复杂度 | 低 | 中 | 高 |
| 最少 YAML 段数 | 1 段(Deployment/Pod) | 2 段(PV+PVC) | 通常 1 段 PVC(PV/目录动态生成) |
| 目录创建 | 需手工提前创建 | 需手工提前创建 | 通常由 provisioner 自动创建子目录 |
| 与应用耦合 | 高(写死 server/path) | 低(应用只引用 PVC) | 低(应用只引用 PVC) |
| 适用场景 | 临时验证、概念验证(先跑通) | 稳定运行、团队协作 | 大规模/多团队、追求自动化 |
操作步骤
- 按复杂度选择一种方式
- 应用清单
- 验证读写
结论:K3s 使用 NFS 不需要先把 NFS 手工挂到每台节点主机;Pod/PVC 可直接挂远端 NFS。
方式 1:Pod 直接挂 NFS(最简单,快速验证)
说明:
nfs.path指向的目录(或子目录)需要在 NFS 服务端提前创建并设置好权限;K3s 不会自动在 NFS 服务端创建该目录。例如在 onecloud 上预创建子目录:
sudo mkdir -p /sdcard/app-a sudo chown 1000:1000 /sdcard/app-a sudo chmod 0770 /sdcard/app-a
apiVersion: apps/v1 # Deployment 使用的 API 版本
kind: Deployment # 资源类型:Deployment
metadata: # 资源元信息
name: nfs-direct-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 1 # 副本数
selector: # Pod 选择器
matchLabels: # 标签匹配条件
app: nfs-direct-demo # 选择带 app=nfs-direct-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nfs-direct-demo # Pod 标签值
spec: # Pod 规格
containers: # 容器列表
- name: app # 容器名称
image: nginx:alpine # 容器镜像
volumeMounts: # 容器内挂载点
- name: nfs-data # 引用下方 volumes 的卷名
mountPath: /usr/share/nginx/html # 挂载到容器内目录
volumes: # Pod 卷定义
- name: nfs-data # 卷名
nfs: # 直接使用 NFS 卷
server: <NFS_SERVER_IP> # NFS 服务器地址(应用前替换)
path: <NFS_EXPORT_PATH_OR_SUBDIR> # NFS 导出目录或子目录(应用前替换)
可执行真源:ansible/files/03-06/nfs-direct-demo.yaml。
方式 2:静态 NFS(PV + PVC,推荐)
唯一真源:ansible/files/03-06/nfs-pv-pvc-demo.yaml。
为减少硬编码,示例清单已改为占位符:
<NFS_SERVER_IP>、<NFS_EXPORT_PATH>。
应用前必须先替换(例如192.168.2.22、/sdcard)。
静态 NFS 同样需要在服务端提前创建目录并设置权限;不会自动创建目录。
方式 3:动态 NFS(选装 provisioner)
这是选装增强,不是 K3s 内置默认能力。常见组件是 nfs-subdir-external-provisioner。
3.1 安装 provisioner(Helm)
前提:NFS 服务端已可用(例如
192.168.2.22:/sdcard),且 K3s 节点网络可达。
# 1) 添加 chart 仓库
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update
# 2) 安装(建议放到 kube-system)
helm upgrade --install nfs-subdir-external-provisioner \
nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
-n kube-system --create-namespace \
--set nfs.server=192.168.2.22 \
--set nfs.path=/sdcard \
--set storageClass.name=nfs-client \
--set storageClass.defaultClass=false \
--set storageClass.reclaimPolicy=Delete \
--set storageClass.archiveOnDelete=true
参数说明(最常改):
nfs.server:NFS 服务器地址nfs.path:NFS 导出根目录(provisioner 会在其下创建子目录)storageClass.name:动态供给使用的 StorageClass 名称archiveOnDelete:删除 PVC 时是否归档目录(true更稳妥,false更干净)
3.2 验证 provisioner 与 StorageClass
kubectl -n kube-system get pod -l app.kubernetes.io/name=nfs-subdir-external-provisioner -o wide
kubectl get storageclass
3.3 用动态 PVC 验证自动建卷
创建一个最小 PVC(示例):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-dynamic-pvc-demo
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 5Gi
可执行真源:ansible/files/03-06/nfs-dynamic-pvc-demo.yaml。
应用并验证:
kubectl apply -f ansible/files/03-06/nfs-dynamic-pvc-demo.yaml
kubectl get pvc nfs-dynamic-pvc-demo -n default
kubectl get pv | grep nfs-dynamic-pvc-demo
当 PVC Bound 后,通常可在 NFS 服务器的 /sdcard 下看到自动创建的子目录(命名规则由 provisioner 管理)。
3.5 本次实机验证记录(ylc61 + onecloud)
nfs-subdir-external-provisioner安装成功并RunningStorageClass nfs-client创建成功- 动态 PVC
nfs-dynamic-pvc-demo成功Bound - 自动创建 PV 成功,并在 NFS 服务端目录下生成子目录:
/sdcard/default-nfs-dynamic-pvc-demo-pvc-<uid>
- 删除临时 PVC 后,PV 按
Delete回收完成
备注:验证中曾出现过镜像拉取
EOF(偶发网络抖动),重试后官方镜像registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2已可正常拉取并启动。
3.4 何时选动态 NFS
- 目录数量多、变更频繁
- 希望减少手工维护 PV/PVC 对应关系
- 团队里希望应用只声明 PVC,平台统一管理 StorageClass
验证命令(若 YAML 在 manifests 目录,按实际路径选择其一复制执行)
# 仓库根直接应用
# 先替换 ansible/files/03-06/nfs-pv-pvc-demo.yaml 里的占位符
# <NFS_SERVER_IP> -> 例如 192.168.2.22
# <NFS_EXPORT_PATH> -> 例如 /sdcard
kubectl apply -f ansible/files/03-06/nfs-pv-pvc-demo.yaml
# 或默认路径(已拷贝到 manifests 时)
kubectl apply -f /var/lib/rancher/k3s/server/manifests/nfs-pv-pvc.yaml
kubectl get pv,pvc -A
kubectl describe pv nfs-pv-demo
# 自定义 data-dir(如 /storage)
kubectl apply -f /storage/server/manifests/nfs-pv-pvc.yaml
kubectl get pv,pvc -A
kubectl describe pv nfs-pv-demo
# 对方式 1(Pod 直挂)可这样验证
kubectl apply -f ansible/files/03-06/nfs-direct-demo.yaml
kubectl rollout status deploy/nfs-direct-demo --timeout=180s
kubectl exec deploy/nfs-direct-demo -- sh -c 'echo nfs-direct-ok > /usr/share/nginx/html/nfs.txt && cat /usr/share/nginx/html/nfs.txt'
预期
- PV/PVC 状态为
Bound - 业务 Pod 可读写挂载目录
失败排查
- 检查 NFS 服务与导出目录权限
- 检查节点到 NFS 服务器网络
- 检查
path与server配置是否正确 - 若报
Permission denied,回到01-05的root_squash权限章节,确认导出目录与业务 UID/GID 对齐
下一步
03-05-k3s-local-path-pvc.md:单副本应用用 K3s 自带 local-path 即可,无需 NFS05-06-openlist挂载网盘与自动备份.md
排障
- 先看 playbook 输出:失败时先定位是 deploy/wait/http_check 哪一步。
- 集群侧总览:
kubectl get nodes -o wide、kubectl -n kube-system get pods -o wide。 - 事件与日志:
kubectl -n <ns> describe ...、kubectl -n <ns> logs ... --tail=200。