外观
K3s 使用 NFS Provisioner 实现动态存储供应 (Ubuntu 版本)
概述
本文档详细介绍了如何在基于 Ubuntu 系统的 K3s 集群中配置和使用 NFS Subdir External Provisioner 来实现动态存储供应。通过此方案,您可以为 Kubernetes 工作负载提供持久化存储,而无需手动管理 PersistentVolumes (PV)。
NFS Subdir External Provisioner 是一个自动配置 NFS 存储卷的存储供应器,它能够根据 PersistentVolumeClaims (PVC) 动态创建 NFS 子目录并将其作为 PV 提供给应用程序使用。
目录
前置条件
在开始部署 NFS Provisioner 之前,请确保满足以下条件:
- 已成功部署基于 Ubuntu 系统的 K3s 集群
- 具备集群管理员权限
- 准备一台可用的 NFS 服务器(可以是集群内的节点或独立服务器)
- 确保 K3s 节点能够访问 NFS 服务器
- 确保 K3s 客户端节点已安装 nfs-common 和 rpcbind 软件包
- 确保防火墙规则允许 NFS 相关端口通信(通常为 111, 2049, 以及挂载端口)
NFS 服务器配置
在K3s集群中,NFS服务器可以部署在集群外部或者作为集群中的一个节点。无论采用哪种方式,都需要确保所有K3s节点都能够访问NFS服务器。
在 K3s 客户端节点安装 NFS 客户端工具
在K3s集群的所有节点(包括master和worker节点)上,都需要安装NFS客户端工具,以便能够挂载NFS共享卷:
bash
sudo apt update
sudo apt install -y nfs-common rpcbind安装完成后,可以通过以下命令验证安装:
bash
# 检查NFS客户端服务状态
sudo systemctl status rpcbind
# 验证NFS工具是否可用
showmount --help注意:虽然rpcbind服务在某些情况下不是必需的,但为了确保兼容性,建议在所有K3s节点上都安装。
在 Ubuntu 上配置 NFS 服务器
- 更新系统并安装 NFS 服务器软件包:
bash
sudo apt update
sudo apt install -y nfs-kernel-server- 创建共享目录并设置权限:
bash
sudo mkdir -p /srv/nfs/k3s-storage
sudo chown nobody:nogroup /srv/nfs/k3s-storage
sudo chmod 777 /srv/nfs/k3s-storage- 配置 NFS 共享:
编辑 /etc/exports 文件:
bash
sudo tee -a /etc/exports <<EOF
/srv/nfs/k3s-storage *(rw,sync,no_subtree_check,no_root_squash,insecure)
EOF参数说明:
*(rw,sync,no_subtree_check,no_root_squash,insecure):*: 允许所有客户端访问(生产环境中应指定具体IP)rw: 允许读写访问sync: 同步写入磁盘no_subtree_check: 要用子树检查以提高性能no_root_squash: 允许客户端以root身份写入insecure: 允许使用大于1024的端口连接
- 重启 NFS 服务:
bash
sudo exportfs -ra
sudo systemctl restart nfs-kernel-server
sudo systemctl enable nfs-kernel-server- 配置 UFW 防火墙(如果启用了UFW):
bash
sudo ufw allow from 192.168.0.0/16 to any port nfs
sudo ufw allow ssh- 验证 NFS 服务:
bash
showmount -e localhost应该能看到类似以下输出:
text
Export list for localhost:
/srv/nfs/k3s-storage *部署 NFS Subdir External Provisioner
方法一:使用 Helm 部署(推荐)
- 添加 NFS Subdir External Provisioner Helm 仓库:
bash
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update- 创建命名空间(可选):
bash
kubectl create namespace nfs-provisioner- 部署 NFS Subdir External Provisioner:
bash
helm upgrade --install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--namespace=nfs-provisioner \
--create-namespace \
--set image.repository=willdockerhub/nfs-subdir-external-provisioner \
--set image.tag=v4.0.2 \
--set replicaCount=2 \
--set storageClass.name=nfs-client \
--set storageClass.defaultClass=true \
--set nfs.server=192.168.192.250 \
--set nfs.path=/fs/1000/nfs/NFSData/k3s-platform-storage \
--set podSecurityContext.runAsUser=999 \
--set podSecurityContext.fsGroup=999方法二:使用 YAML 文件部署
- 创建部署文件
nfs-provisioner.yaml:
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: kube-system
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-system
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: kube-system
labels:
app: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: YOUR_NFS_SERVER_IP
- name: NFS_PATH
value: /fs/1000/nfs/NFSData/k3s-platform-storage
volumes:
- name: nfs-client-root
nfs:
server: YOUR_NFS_SERVER_IP
path: /fs/1000/nfs/NFSData/k3s-platform-storage- 部署 NFS Provisioner:
bash
kubectl apply -f nfs-provisioner.yaml配置 StorageClass
创建默认 StorageClass
如果您希望将 NFS Provisioner 设置为默认存储类,可以创建如下 StorageClass:
bash
sudo tee storageclass.yaml << 'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
server: 192.168.192.250
path: /fs/1000/nfs/NFSData/kk3s-platform-storage
onDelete: delete
EOF创建非默认 StorageClass
如果您不想将 NFS Provisioner 设置为默认存储类,可以创建如下 StorageClass:
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
server: 192.168.192.250
path: /fs/1000/nfs/NFSData/k3s-platform-storage
onDelete: delete参数说明:
onDelete: 定义删除 PVC 时的行为delete: 删除 PVC 时同时删除 NFS 子目录retain: 删除 PVC 时保留 NFS 子目录
应用 StorageClass
bash
kubectl apply -f storageclass.yaml验证部署
查看
bash
kubectl get pods,deploy,sc -n nfs-provisioner检查 Provisioner Pod 状态
bash
kubectl get pods -n kube-system | grep nfs-client-provisioner应该看到类似以下输出:
text
nfs-client-provisioner-xxxxxx-xxxxx 1/1 Running 0 5m检查 StorageClass
bash
kubectl get storageclass应该能看到我们创建的 nfs-client 存储类。
测试动态供应
创建一个测试 PVC:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs-client应用配置:
bash
kubectl apply -f test-pvc.yaml检查 PVC 状态:
bash
kubectl get pvc test-claim应该看到 PVC 状态为 Bound,表示已成功绑定到 PV。
查看创建的 PV:
bash
kubectl get pv应该能看到一个自动创建的 PV,其存储源为 NFS。
使用示例
部署使用 NFS 存储的应用
创建一个使用 NFS 存储的简单应用示例:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
storageClassName: nfs-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-storage
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-storage
persistentVolumeClaim:
claimName: nginx-pvc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort应用配置:
bash
kubectl apply -f nginx-example.yaml验证应用状态:
bash
kubectl get pods
kubectl get pvc测试数据持久化
进入容器并创建一个测试文件:
bash
kubectl exec -it deployment/nginx-deployment -- /bin/bash
echo "Hello NFS Storage!" > /usr/share/nginx/html/index.html
exit删除并重新创建 Pod:
bash
kubectl delete pod -l app=nginx等待新 Pod 启动后,再次检查文件是否存在:
bash
kubectl exec -it deployment/nginx-deployment -- cat /usr/share/nginx/html/index.html应该能看到之前创建的文件内容,证明数据持久化成功。
最佳实践
1. 安全配置
- 限制 NFS 服务器访问范围:
bash
/srv/nfs/k3s-storage 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)- 使用专用用户而非 root:
text
/srv/nfs/k3s-storage 192.168.1.0/24(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)2. 性能优化
调整 NFS 挂载选项:
yamlvolumes: - name: nfs-client-root nfs: server: YOUR_NFS_SERVER_IP path: /srv/nfs/k3s-storage readOnly: false根据应用需求选择合适的访问模式:
ReadWriteOnce: 单个节点读写ReadOnlyMany: 多节点只读ReadWriteMany: 多节点读写(需要 NFS 支持)
3. 存储管理
合理设置存储请求和限制:
yamlresources: requests: storage: 5Gi limits: storage: 10Gi使用不同的 StorageClass 满足不同应用需求:
高性能存储类:
yaml# 高性能存储类 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-fast provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: server: YOUR_NFS_SERVER_IP path: /srv/nfs/fast-storage归档存储类:
yaml# 归档存储类 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-archive provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: server: YOUR_NFS_SERVER_IP path: /srv/nfs/archive-storage
故障排除
常见问题
PVC 一直处于 Pending 状态
检查 StorageClass 是否正确配置:
bashkubectl describe pvc YOUR_PVC_NAME检查 NFS Provisioner 日志:
bashkubectl logs -n kube-system deployment/nfs-client-provisionerPod 无法挂载 NFS 卷
检查 NFS 服务器是否可达:
bashshowmount -e YOUR_NFS_SERVER_IP检查防火墙设置是否允许 NFS 端口通信。
权限问题
检查 NFS 共享目录权限:
bashls -la /srv/nfs/k3s-storage确保设置了正确的
no_root_squash或all_squash选项。
诊断命令
bash
# 检查 NFS Provisioner Pod 状态
kubectl get pods -n kube-system | grep nfs
# 查看 NFS Provisioner 日志
kubectl logs -n kube-system deployment/nfs-client-provisioner
# 检查 StorageClass
kubectl get storageclass
# 检查 PVC 状态
kubectl describe pvc YOUR_PVC_NAME
# 检查 PV 状态
kubectl describe pv YOUR_PV_NAME
# 检查 NFS 服务器导出的共享
showmount -e YOUR_NFS_SERVER_IP
# 在节点上测试 NFS 挂载
mkdir /tmp/test-nfs
mount -t nfs YOUR_NFS_SERVER_IP:/srv/nfs/k3s-storage /tmp/test-nfs
umount /tmp/test-nfs
rmdir /tmp/test-nfs日志分析
如果遇到问题,请重点检查以下日志:
NFS Provisioner 日志:
bashkubectl logs -n kube-system deployment/nfs-client-provisioner相关事件:
bashkubectl get events --sort-by=.metadata.creationTimestampPVC 详细信息:
bashkubectl describe pvc YOUR_PVC_NAME
通过以上步骤,您应该能够在基于 Ubuntu 系统的 K3s 集群中成功配置和使用 NFS Subdir External Provisioner 来实现动态存储供应。
