大家好,欢迎来到运维有术
欢迎来到云原生运维实战系列之基于 KubeSphere 玩转 Kubernetes 第二季
导图
知识量
知识点
实战服务器配置 (架构 1:1 复刻小规模生产环境,配置略有不同)
主机名 | IP | CPU | 内存 | 系统盘 | 数据盘 | 用途 |
---|---|---|---|---|---|---|
k8s-master-1 | 192.168.9.91 | 4 | 16 | 40 | 100 | KubeSphere/k8s-master |
k8s-master-2 | 192.168.9.92 | 4 | 16 | 40 | 100 | KubeSphere/k8s-master |
k8s-master-3 | 192.168.9.93 | 4 | 16 | 40 | 100 | KubeSphere/k8s-master |
k8s-worker-1 | 192.168.9.95 | 4 | 16 | 40 | 100 | k8s-worker/CI |
k8s-worker-2 | 192.168.9.96 | 4 | 16 | 40 | 100 | k8s-worker |
k8s-worker-3 | 192.168.9.97 | 4 | 16 | 40 | 100 | k8s-worker |
k8s-storage-1 | 192.168.9.81 | 4 | 16 | 40 | 100/100/100/100/100 | ElasticSearch/GlusterFS/Ceph-Rook/Longhorn/NFS/ |
k8s-storage-2 | 192.168.9.82 | 4 | 16 | 40 | 100/100/100/100 | ElasticSearch/GlusterFS/Ceph-Rook/Longhorn/ |
k8s-storage-3 | 192.168.9.83 | 4 | 16 | 40 | 100/100/100/100 | ElasticSearch/GlusterFS/Ceph-Rook/Longhorn/ |
registry | 192.168.9.80 | 4 | 8 | 40 | 100 | Sonatype Nexus 3 |
合计 | 10 | 40 | 152 | 400 | 2000 |
实战环境涉及软件版本信息
玩转 Kubernetes 必然少不了持久化存储,不考虑各种公有云上的 Kubernetes 集群和商业化集中存储,自建的 Kubernetes 集群中,后端持久化存储常见的解决方案有 Ceph、GlusterFS、NFS,以及这两年新兴起的 Longhorn(也可能还有我不了解的其他的更好的)。
所以我们的实战课程基于这四种常见持久化存储解决方案设计了「Kubernetes 持久化存储四部曲」。上一期我们完成了第一部 GlusterFS 存储的实战。本期实战课程,我们将完成第二部实战 NFS,我们使用 Helm 的方式安装 Kubernetes NFS Subdir External Provisioner 插件,并实战演示如何将 NFS 作为 Kubernetes 集群的后端持久化存储。
本文介绍的内容可直接用于研发、测试环境,对于生产环境不建议使用 NFS 存储,主要原因如下:
虽然,我个人不建议在生产环境使用 NFS 作为 Kubernetes 的后端持久化存储。但是,在我们做过的小调研中发现在生产环境使用 NFS 的比率还挺高。
这是为什么呢?我左思右想可能的原因有:
本文选择 IP 为 192.168.9.81 的 k8s-storage-1 节点,作为 NFS 存储服务器。本示例的 NFS 服务器配置仅适用于测试环境,生产环境请谨慎评估、配置。
yum install nfs-utils
执行以下命令创建共享数据存储目录,本文以 /data/k8s
为例,请根据实际情况修改。
mkdir -p /data/k8s
chown nfsnobody:nfsnobody /data/k8s
配置 NFS 服务器数据导出目录及访问 NFS 服务器的客户端机器权限。
编辑配置文件 vi /etc/exports
,添加如下内容
/data/k8s 192.168.9.0/24(rw,sync,all_squash,anonuid=65534,anongid=65534,no_subtree_check)
说明:
192.168.9.0/24:可以访问 NFS 存储的客户端 IP 地址 rw:读写操作,客户端机器拥有对卷的读写权限。 sync:内存数据实时写入磁盘,性能会有所限制 all_squash:NFS 客户端上的所有用户在使用共享目录时都会被转换为一个普通用户的权限 anonuid:转换后的用户权限 ID,对应的操作系统的 nfsnobody 用户 anongid:转换后的组权限 ID,对应的操作系统的 nfsnobody 组 no_subtree_check:不检查客户端请求的子目录是否在共享目录的子树范围内,也就是说即使输出目录是一个子目录,NFS 服务器也不检查其父目录的权限,这样可以提高效率。
systemctl enable nfs-server --now
exportfs -v
[root@ksp-v341-storage-1 ~]# exportfs -v
/data/k8s 192.168.9.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,root_squash,all_squash)
找一台额外的机器作为客户端验证测试,本示例使用 ksp-aster-1 节点。
mkdir /mnt/nfs
yum install nfs-utils
mount -t nfs 192.168.9.81:/data/k8s /mnt/nfs/
# 创建测试目录、创建测试文件、测试文件写入内容、查看写入目录和文件权限、删除目录和文件
[root@k8s-master-1 ~]# cd /mnt/nfs/
[root@k8s-master-1 nfs]# mkdir nfs-test
[root@k8s-master-1 nfs]# touch nfs-test.txt
[root@k8s-master-1 nfs]# echo "nfs-test" > nfs-test.txt
[root@k8s-master-1 nfs]# ll
total 4
drwxr-xr-x 2 nfsnobody nfsnobody 6 Nov 29 16:56 nfs-test
-rw-r--r-- 1 nfsnobody nfsnobody 9 Nov 29 16:56 nfs-test.txt
[root@k8s-master-1 nfs]# rmdir nfs-test
[root@k8s-master-1 nfs]# rm nfs-test.txt
rm: remove regular file ‘nfs-test.txt’? y
[root@k8s-master-1 nfs]# ll
total 0
想要 Kubernetes 支持 NFS 存储,我们需要安装 nfs-subdir-external-provisioner ,它是一个存储资源自动调配器,它可将现有的 NFS 服务器通过持久卷声明来支持 Kubernetes 持久卷的动态分配。该组件是对 Kubernetes NFS-Client Provisioner 的扩展, nfs-client-provisioner 已经不提供更新,而且 Github 仓库 也已经处于归档状态,已经迁移到 nfs-subdir-external-provisioner 的仓库。
官方提供的安装方式有三种:
使用 Helm 的方式比较简单,也是现在官方推荐的、使用率最高的方式,本文仅实战演示 Helm 部署方式,其他方式请参考官方文档。
所有 Kubernetes 集群节点需要提前安装 nfs-utils,否则在部署过程中会报错,报错信息见「常见问题 1」。
yum install nfs-utils
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
可选配置,主要是方便资源管理。
kubectl create ns nfs-system
(首选方案,使用命令行设置变量参数)
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set storageClass.name=nfs-sc --set nfs.server=192.168.9.81 --set nfs.path=/data/k8s -n nfs-system
说明:
--set storageClass.name=nfs-sc:指定 storageClass 的名字 --set nfs.server=192.168.9.81:指定 NFS 服务器的地址 --set nfs.path=/data/k8s:指定 NFS 导出的共享数据目录 --set storageClass.defaultClass=true:指定为默认的 sc,本示例没使用 -n nfs-system:指定命名空间
(备选方案,使用配置文件设置变量参数),有更多定制化需求时可以选择自定义 values.yaml
的方式进行安装,实际使用中与命令行安装 NFS Subdir External Provisioner 的方式二选一即可。
helm pull nfs-subdir-external-provisioner/nfs-subdir-external-provisioner
# 查看
[root@k8s-master-1 charts]# ll
total 8
-rw-r--r-- 1 root root 5991 Nov 30 10:26 nfs-subdir-external-provisioner-4.0.18.tgz
# 解压
[root@k8s-master-1 charts]# tar xvf nfs-subdir-external-provisioner-4.0.18.tgz
nfs-subdir-external-provisioner/Chart.yaml
nfs-subdir-external-provisioner/values.yaml
nfs-subdir-external-provisioner/templates/_helpers.tpl
nfs-subdir-external-provisioner/templates/clusterrole.yaml
nfs-subdir-external-provisioner/templates/clusterrolebinding.yaml
nfs-subdir-external-provisioner/templates/deployment.yaml
nfs-subdir-external-provisioner/templates/persistentvolume.yaml
nfs-subdir-external-provisioner/templates/persistentvolumeclaim.yaml
nfs-subdir-external-provisioner/templates/poddisruptionbudget.yaml
nfs-subdir-external-provisioner/templates/podsecuritypolicy.yaml
nfs-subdir-external-provisioner/templates/role.yaml
nfs-subdir-external-provisioner/templates/rolebinding.yaml
nfs-subdir-external-provisioner/templates/serviceaccount.yaml
nfs-subdir-external-provisioner/templates/storageclass.yaml
nfs-subdir-external-provisioner/README.md
nfs-subdir-external-provisioner/ci/test-values.yaml
[root@k8s-master-1 charts]# cat nfs-subdir-external-provisioner/values.yaml | egrep -v '#|^$'
筛选过滤后的可用配置项,详细配置及说明
replicaCount: 1
strategyType: Recreate
image:
repository: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner
tag: v4.0.2
pullPolicy: IfNotPresent
imagePullSecrets: []
nfs:
server:
path: /nfs-storage
mountOptions:
volumeName: nfs-subdir-external-provisioner-root
reclaimPolicy: Retain
storageClass:
create: true
defaultClass: false
name: nfs-client
allowVolumeExpansion: true
reclaimPolicy: Delete
archiveOnDelete: true
onDelete:
pathPattern:
accessModes: ReadWriteOnce
volumeBindingMode: Immediate
annotations: {}
leaderElection:
enabled: true
rbac:
create: true
podSecurityPolicy:
enabled: false
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
serviceAccount:
create: true
annotations: {}
name:
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
labels: {}
podDisruptionBudget:
enabled: false
maxUnavailable: 1
nfs-subdir-external-provisioner/values.yaml
# 主要修改内容如下
image:
repository: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner #镜像拉取地址,默认可能拉取不下来,建议替换成本地或是其他可正常访问的仓库,也可以使用我DockerHub上的的 unopsman/nfs-subdir-external-provisioner
tag: v4.0.2 #镜像 tag 默认为 v4.0.2,可根据实际情况替换
nfs:
server: 192.168.9.81 #指定 NFS 服务器的地址
path: /data/k8s #指定 NFS 导出的共享数据目录
storageClass:
defaultClass: false #是否设置为默认的 StorageClass,本示例没设置,有需要的可以设置为 true
name: nfs-sc #指定 storageClass 的名字
[root@k8s-master-1 charts]# helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -f nfs-subdir-external-provisioner/values.yaml -n nfs-system
NAME: nfs-subdir-external-provisioner
LAST DEPLOYED: Thu Nov 30 14:00:21 2023
NAMESPACE: nfs-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
[root@k8s-master-1 ~]# kubectl get sc nfs-sc -o wide
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-sc cluster.local/nfs-subdir-external-provisioner Delete Immediate true 66s
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.k8s.io/sig-storage/nfs-subdir-external-provisioner v4.0.2 932b0bface75 2 years ago 43.8MB
$ docker scout quickview registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
✓ SBOM of image already cached, 46 packages indexed
Target │ registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 │ 3C 57H 34M 4L
digest │ 932b0bface75 │
特殊说明:
镜像构建时间有点久远,在 2023 年 11 月 29 日查看时显示为 2 年前。 镜像安全扫描结果(安全隐患):98 vulnerabilities found in 9 packages,LOW 4,MEDIUM 34,HIGH 57,CRITICAL 3。
[root@k8s-master-1 ~]# kubectl get deployment -n nfs-system -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nfs-subdir-external-provisioner 1/1 1 1 73m nfs-subdir-external-provisioner registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 app=nfs-subdir-external-provisioner,release=nfs-subdir-external-provisioner
[root@k8s-master-1 ~]# kubectl get pod -n nfs-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-subdir-external-provisioner-65f66bc6cd-hdpts 1/1 Running 0 4m20s 10.233.85.5 k8s-master-2 <none> <none>
vi test-nfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-nfs-pvc
spec:
storageClassName: nfs-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
kubectl apply -f test-nfs-pvc.yaml -n nfs-system
[root@k8s-master-1 ~]# kubectl get pvc -n nfs-system -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
test-nfs-pvc Bound pvc-2d245af1-f613-485d-9782-0c315cead56c 1Gi RWX nfs-sc 20s Filesystem
vi test-nfs-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: test-nfs-pod
spec:
containers:
- name: test-nfs-pod
image: busybox:stable
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && sleep 3600"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-nfs-pvc
kubectl apply -f test-nfs-pod.yaml -n nfs-system
[root@k8s-master-1 ~]# kubectl get pods -n nfs-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-subdir-external-provisioner-5d5bcf7fcb-d98gd 1/1 Running 0 97s 10.233.85.28 k8s-master-2 <none> <none>
test-nfs-pod 1/1 Running 0 11s 10.233.85.29 k8s-master-2 <none> <none>
[root@k8s-master-1 ~]# kubectl exec test-nfs-pod -n nfs-system -- df -h
Filesystem Size Used Available Use% Mounted on
overlay 100.0G 2.3G 97.7G 2% /
tmpfs 64.0M 0 64.0M 0% /dev
tmpfs 7.8G 0 7.8G 0% /sys/fs/cgroup
192.168.9.81:/data/k8s/nfs-system-test-nfs-pvc-pvc-2d245af1-f613-485d-9782-0c315cead56c
99.9G 33.0M 99.9G 0% /mnt
/dev/mapper/centos-root
37.0G 3.1G 33.8G 8% /etc/hosts
/dev/mapper/centos-root
37.0G 3.1G 33.8G 8% /dev/termination-log
/dev/sdb1 100.0G 2.3G 97.7G 2% /etc/hostname
/dev/sdb1 100.0G 2.3G 97.7G 2% /etc/resolv.conf
shm 64.0M 0 64.0M 0% /dev/shm
tmpfs 14.4G 12.0K 14.4G 0% /var/run/secrets/kubernetes.io/serviceaccount
tmpfs 7.8G 0 7.8G 0% /proc/acpi
tmpfs 64.0M 0 64.0M 0% /proc/kcore
tmpfs 64.0M 0 64.0M 0% /proc/keys
tmpfs 64.0M 0 64.0M 0% /proc/timer_list
tmpfs 64.0M 0 64.0M 0% /proc/sched_debug
tmpfs 7.8G 0 7.8G 0% /proc/scsi
tmpfs 7.8G 0 7.8G 0% /sys/firmware
注意: 在输出结果中我们可以看到挂载的 NFS 存储的可用空间为 99.9G,而不是我们 PVC 中分配的 1G。
# 写入 2G 的数据
[root@k8s-master-1 ~]# kubectl exec test-nfs-pod -n nfs-system -- dd if=/dev/zero of=/mnt/test.img bs=1M count=2000
2000+0 records in
2000+0 records out
2097152000 bytes (2.0GB) copied, 3.115083 seconds, 642.0MB/s
# 查看结果
[root@k8s-master-1 ~]# kubectl exec test-nfs-pod -n nfs-system -- ls -lh /mnt/
total 3G
-rw-r--r-- 1 nobody nobody 0 Nov 30 06:01 SUCCESS
-rw-r--r-- 1 nobody nobody 2.0G Nov 30 06:04 test.img
注意: 实际测试我们写入了 2G 的数据量,已经超过了我们创建的 PVC 1G 的限制。因此,要特别注意,使用 NFS 存储时无法限制存储使用量。
SSH 登陆 NFS 存储节点,执行以下命令。
[root@k8s-storage-1 ~]# cd /data/k8s/
[root@k8s-storage-1 k8s]# ll
total 0
drwxrwxrwx 2 nfsnobody nfsnobody 37 Nov 30 14:04 nfs-system-test-nfs-pvc-pvc-2d245af1-f613-485d-9782-0c315cead56c
[root@k8s-storage-1 k8s]# ll nfs-system-test-nfs-pvc-pvc-2d245af1-f613-485d-9782-0c315cead56c/
total 2975684
-rw-r--r-- 1 nfsnobody nfsnobody 0 Nov 30 14:01 SUCCESS
-rw-r--r-- 1 nfsnobody nfsnobody 2097152000 Nov 30 14:04 test.img
在输出结果中,可以看见 SUCCESS 和 test.img 两个文件,并且可以看出命名目录的规则为 namespace 名称-pvc 名称-pv 名称。
PV 名称格式是 pv+随机字符串,所以,每次只要不删除 PVC,那么 Kubernetes 中 PV 与存储绑定将不会丢失,要是删除 PVC 也就意味着删除了绑定的文件夹,下次就算重新创建相同名称的 PVC,生成的文件夹名称也不会一致,因为 PV 名是随机生成的字符串,而文件夹命名又跟 PV 有关,所以删除 PVC 需谨慎。
kubectl delete -f test-nfs-pod.yaml -n nfs-system
kubectl delete -f test-nfs-pvc.yaml -n nfs-system
[root@k8s-storage-1 k8s]# ll
total 0
drwxrwxrwx 2 nfsnobody nfsnobody 37 Nov 30 14:04 archived-nfs-system-test-nfs-pvc-pvc-2d245af1-f613-485d-9782-0c315cead56c
[root@k8s-storage-1 k8s]# ll archived-nfs-system-test-nfs-pvc-pvc-2d245af1-f613-485d-9782-0c315cead56c/
total 2048004
-rw-r--r-- 1 nfsnobody nfsnobody 0 Nov 30 14:01 SUCCESS
-rw-r--r-- 1 nfsnobody nfsnobody 2097152000 Nov 30 14:04 test.img
从结果中可以看到,Kubernetes 删除 PVC 后,在 NFS 存储层并没有立即删除 PVC 对应的数据目录及已有数据,而是将原来的数据目录改名为 archived-+原有数据目录名称的形式。
在控制台左侧功能菜单依次选择,「集群」->「存储」->「持久卷声明」,点击「创建」按钮。
nfs-sc
,容量选择 2G创建完成后,查看已经创建的 PVC 及详情。
当需要卸载 NFS Subdir External Provisioner 时,建议按以下操作流程执行。
archived-
命名的所有数据目录(可选,谨慎操作)# 查看 helm 资源
[root@k8s-master-1 ~]# helm list -n nfs-system
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
nfs-subdir-external-provisioner nfs-system 1 2023-11-29 14:40:31.868722982 +0800 CST deployed nfs-subdir-external-provisioner-4.0.18 4.0.2
# 卸载
[root@k8s-master-1 ~]# helm uninstall nfs-subdir-external-provisioner -n nfs-system
release "nfs-subdir-external-provisioner" uninstalled
# 查看
[root@k8s-master-1 ~]# helm list -n nfs-system
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
[root@k8s-master-1 ~]# kubectl get deploy,pod,sc -n nfs-system
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
storageclass.storage.k8s.io/local (default) openebs.io/local Delete WaitForFirstConsumer false 22h
说明: 结果中显示的是默认的 openebs 存储,nfs 相关资源已经删除
kubectl delete ns nfs-system
# Pod nfs-subdir-external-provisioner 创建失败,查看详细报错
$ kubectl describe pod nfs-subdir-external-provisioner-7845498695-hmf7f -n nfs-system
报错如下:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m58s default-scheduler Successfully assigned nfs-system/nfs-subdir-external-provisioner-7845498695-hmf7f to k8s-master-2
Warning FailedMount 48s (x10 over 4m58s) kubelet MountVolume.SetUp failed for volume "nfs-subdir-external-provisioner-root" : mount failed: exit status 32
Mounting command: mount
Mounting arguments: -t nfs 192.168.9.81:/data/k8s /var/lib/kubelet/pods/371d85d0-5e31-4d9e-a56f-87a66a35b97e/volumes/kubernetes.io~nfs/nfs-subdir-external-provisioner-root
Output: mount: wrong fs type, bad option, bad superblock on 192.168.9.81:/data/k8s,
missing codepage or helper program, or other error
(for several filesystems (e.g. nfs, cifs) you might
need a /sbin/mount.<type> helper program)
In some cases useful info is found in syslog - try
dmesg | tail or so.
Warning FailedMount 38s (x2 over 2m55s) kubelet Unable to attach or mount volumes: unmounted volumes=[nfs-subdir-external-provisioner-root], unattached volumes=[nfs-subdir-external-provisioner-root kube-api-access-jnxrs]: timed out waiting for the condition
所有节点需要安装 nfs 客户端软件包,CentOS 使用 yum install nfs-utils
。
本文实战演示了在操作系统 CentOS 7.9 x64 上安装配置 NFS 的详细过程,同时,也详细讲解了在基于 KubeSphere 部署的 Kubernetes 集群中,使用 helm 方式安装 Kubernetes NFS Subdir External Provisioner 的两种方法及相关技术细节。
概括总结全文主要涉及以下内容:
本文的实战环境虽然是基于 CentOS 7.9 x64,但是整个操作流程同样适用于其他操作系统。
如果你喜欢本文,请持续关注我,并将本文分享给你的小伙伴!
基于 KubeSphere 玩转 Kubernetes 第二季系列文档,是 知识星球 运维有术 推出的云原生运维实战训练营之基于 KubeSphere 玩转 Kubernetes 第二季的实战文档。
该系列文档从纯实战角度出发,无废话、纯实战快速带你玩转 KubeSphere、玩转 Kubernetes 运维。
本系列文档内容涵盖 (但不限于) 以下技术领域:
获取更多的 KubeSphere、Kubernetes、云原生运维实战技能,请持续关注我,也可以直接加入我们。
Get 技术支持
Get 文档 / 代码
Get 视频
版权声明
About Me
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有