NFS实现K8S存储

189 阅读7分钟

NFS实现k8s存储

K8s存储的几个重要概念: 卷(Volume),持久卷(PV),持久卷申领(PVC),存储类(StorageClass)

卷(VOLUME)

持久卷(PV)

持久卷申领(PVC)

存储类(StorageClass)

PV/PVC/StorageClass的关系

NFS实现K8S存储

NFS静态供应

NFS flex-volume动态供应

NFS csi动态供应

参考

K8s存储的几个重要概念: 卷(Volume),持久卷(PV),持久卷申领(PVC),存储类(StorageClass)

卷(VOLUME)

容器中的文件在磁盘上是临时存放的,这给在容器中运行较重要的应用带来一些问题。 当容器崩溃或停止时会出现一个问题。此时容器状态未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题。 跨所有容器设置和访问共享文件系统具有一定的挑战性 ——来自官方文档

  1. 由于卷属于 Pod 内部共享资源存储,生命周期和 Pod 相同,与 Container 无关,即使 Pod 上的容器停止或者重启,Volume 不会受到影响,但是如果Pod终止,那么这个 Volume 的生命周期也将结束。
  2. 卷的类型比较多:hostPathconfigMapemptyDirlocalnfsiscsi具体可以参考官方文档(点我):

持久卷(PV)

持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。背后可能是 NFS、iSCSI、特定于云平台的存储系统等。
以下是来自官网的一个例子:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

请注意,这是hostPath类型的PV,可以参考官方文档hostPath


root@master:/home/guanwu/k8s/pvtest# k apply -f task-pv-volume.yaml 
persistentvolume/task-pv-volume created
root@master:/home/guanwu/k8s/pvtest# k get pv
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
task-pv-volume   10Gi       RWO            Retain           Available           manual                  53s

创建PVC(下文会介绍)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: task-pv-claim
spec:
 storageClassName: manual
 accessModes:
   - ReadWriteOnce
 resources:
   requests:
     storage: 3Gi
root@master:/home/guanwu/k8s/pvtest# k apply -f task-pv-claim.yaml 
persistentvolumeclaim/task-pv-claim created
root@master:/home/guanwu/k8s/pvtest# k get pvc
NAME            STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   AGE
task-pv-claim   Bound    task-pv-volume   10Gi       RWO            manual         3s

创建pod,这里请注意,这个pod需要被schedule到对应的提供hostPath卷的主机中,可以自行添加属性 spec.nodeSelector选择调度的节点,也可以参考节点调度污点和容忍度
请注意,/mnt/data目录创建在master 节点中,要使用这个pv的pod需要被调度到当前工作节点,可以使用NodeSelector和label确保pod被调度到当前节点

root@master:/home/guanwu/k8s/pvtest# find /mnt/data
/mnt/data

请注意观察,我当前环境的master节点加上了type=ssd的标签

root@master:/home/guanwu/k8s/pvtest# k get nodes --show-labels
NAME      STATUS   ROLES                  AGE   VERSION   LABELS
master    Ready    control-plane,master   92d   v1.28.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,nfs=app,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=,type=ssd
worker1   Ready    worker1                92d   v1.28.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssy,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux,node-role.kubernetes.io/worker1=
worker2   Ready    worker2                92d   v1.28.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker2,kubernetes.io/os=linux,node-role.kubernetes.io/worker2=
root@master:/home/guanwu/k8s/pvtest# 

以下是我的pod文件配置

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage
  nodeSelector:
      type: ssd #请注意这里,缺少这个配置会schedule该Pod到正确的节点

检查pod是否正常运行

root@master:/home/guanwu/k8s/pvtest# k apply -f pv-pod.yaml
pod/task-pv-pod created
root@master:/home/guanwu/k8s/pvtest# k get pod task-pv-pod
NAME          READY   STATUS    RESTARTS   AGE
task-pv-pod   1/1     Running   0          12s
root@master:/home/guanwu/k8s/pvtest# k get pv
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
task-pv-volume   10Gi       RWO            Retain           Bound    test/task-pv-claim   manual                  24m
root@master:/home/guanwu/k8s/pvtest# k get pvc
NAME            STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   AGE
task-pv-claim   Bound    task-pv-volume   10Gi       RWO            manual         20m

验证挂载的文件(注意k命令是本地kubectl命令的简写

root@master:/home/guanwu/k8s/pvtest# kubectl exec -it task-pv-pod -- /bin/bash
root@task-pv-pod:/# echo "hello world" > /usr/share/nginx/html/index.html
root@task-pv-pod:/# curl localhost
hello world
root@master:/home/guanwu/k8s/pvtest# k get pod task-pv-pod -owide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
task-pv-pod   1/1     Running   0          15m   10.10.0.80   master   <none>           <none>

循环10次修改 /mnt/data/index.html文件的内容,并使用curl 10.10.0.80访问


root@master:/home/guanwu/k8s/pvtest# for ((i=1; i<=10; i++)); do s=$(date); echo  $s >  /mnt/data/index.html; sleep 1s; curl 10.10.0.80 ; done
2024年 01月 05日 星期五 22:52:42 CST
2024年 01月 05日 星期五 22:52:43 CST
2024年 01月 05日 星期五 22:52:44 CST
2024年 01月 05日 星期五 22:52:45 CST
2024年 01月 05日 星期五 22:52:46 CST
2024年 01月 05日 星期五 22:52:47 CST
2024年 01月 05日 星期五 22:52:49 CST
2024年 01月 05日 星期五 22:52:50 CST
2024年 01月 05日 星期五 22:52:51 CST
2024年 01月 05日 星期五 22:52:52 CST
root@master:/home/guanwu/k8s/pvtest# 

清除资源

$ kubectl delete pod task-pv-pod
$ kubectl delete pvc task-pv-claim
$ kubectl delete pv task-pv-volume

持久卷申领(PVC)

表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载。为了满足这类需求,就有了存储类(StorageClass)  资源。

存储类(StorageClass)

StorageClass 为管理员提供了一种描述他们提供的存储的“类”的方法。不同的类可能映 射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。 Kubernetes 本身对 于什么类别代表是不言而喻的。 这个概念有时在其他存储系统中称为“配置文件”

PV/PVC/StorageClass的关系

  1. PV 是运维人员来创建的,开发操作 PVC,可是大规模集群中可能会有很多 PV,如果这 些 PV 都需要运维手动来处理这也是一件很繁琐的事情,所以就有了动态供给概念,也就是 Dynamic Provisioning,动态供给的关键就是 StorageClass,它的作用就是创建 PV 模板。创 建 StorageClass 里面需要定义 PV 属性比如存储类型、大小等;另外创建这种 PV 需要用到 存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass, 则会为其自动创建 PV 并进行绑定。
    它们的关系可以参考下图 image.png
  2. PVC 和 PV 是一一对应的。 PV 和 PVC 中的 spec 关键字段要匹配,比如存储(storage)大小。 PV 和 PVC 中的 storageClassName 字段必须一致

NFS实现K8S存储

NFS安装就不再展示了,网上资料很多,本人挂载的目录是 /usr/share/nfs

root@master:/home/guanwu/k8s/pvtest# ls /usr/share/nfs
pvc-324d5c94-27c6-4d6d-8af0-a08adc9a8de6
root@master:/home/guanwu/k8s/pvtest# showmount -e
Export list for master:
/usr/share/nfs *

请注意!请注意!请注意!k8s集群的节点必须安装nfs客户端,否则pod挂载pv会出现异常的问题,作者本人宿主机环境是Ubuntu20.04 LTS,使用如下命令(其他环境的读者可以自行查找)

sudo apt install nfs-common

NFS静态供应

pv文件

apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv0001
spec:
 capacity:
   storage: 5Gi
 accessModes:
   - ReadWriteMany
 persistentVolumeReclaimPolicy: Recycle
 nfs:
   path: "/usr/share/nfs"
   server: 192.168.201.129

pvc文件

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

pod文件

apiVersion: v1
kind: Pod
metadata:
name: nfs-test
spec:
containers:
- name: my-busybox
  image: busybox
  volumeMounts:
  - mountPath: "/data"
    name: sample-volume
  command: ["sleep", "1000000"]
  imagePullPolicy: IfNotPresent
volumes:
  - name: sample-volume
    persistentVolumeClaim:
      claimName: nfs-pvc #请注意这里,要和 pvc文件name对应

演示效果

root@master:/home/guanwu/k8s/nfs/staticprovisor# k apply -f pv1.yaml 
persistentvolume/pv0001 created
root@master:/home/guanwu/k8s/nfs/staticprovisor# k get pv pv0001
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv0001   5Gi        RWX            Recycle          Available                                   5s
root@master:/home/guanwu/k8s/nfs/staticprovisor# k apply -f pvc1.yaml 
persistentvolumeclaim/nfs-pvc created
root@master:/home/guanwu/k8s/nfs/staticprovisor# k get pvc nfs-pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc   Bound    pv0001   5Gi        RWX                           9s
root@master:/home/guanwu/k8s/nfs/staticprovisor# k apply -f pvc-pod.yaml 
pod/nfs-test created
root@master:/home/guanwu/k8s/nfs/staticprovisor# k get pod nfs-test
NAME       READY   STATUS    RESTARTS   AGE
nfs-test   1/1     Running   0          16s
root@master:/home/guanwu/k8s/nfs/staticprovisor# ls /usr/share/nfs/
pvc-324d5c94-27c6-4d6d-8af0-a08adc9a8de6
root@master:/home/guanwu/k8s/nfs/staticprovisor# k exec -it nfs-test -- sh
/ # 
/ # echo $(date) > /data/date.txt
/ # cat /data/date.txt 
Fri Jan 5 15:31:42 UTC 2024
/ # exit
root@master:/home/guanwu/k8s/nfs/staticprovisor# cat /usr/share/nfs/date.txt
Fri Jan 5 15:31:42 UTC 2024
root@master:/home/guanwu/k8s/nfs/staticprovisor# 

测试多个容器共享volume的情况,创建对应deployment文件

root@master:/home/guanwu/k8s/nfs/staticprovisor# cat test-nfs-static.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
 creationTimestamp: null
 labels:
   app: test-nfs-static
 name: test-nfs-static
spec:
 replicas: 3
 selector:
   matchLabels:
     app: test-nfs-static
 strategy: {}
 template:
   metadata:
     creationTimestamp: null
     labels:
       app: test-nfs-static
   spec:
     containers:
     - image: nginx:1.9
       name: nginx
       resources: {}
       volumeMounts:
       - mountPath: "/data"
         name: sample-volume
       imagePullPolicy: IfNotPresent
     volumes:
     - name: sample-volume
       persistentVolumeClaim:
         claimName: nfs-pvc

创建deployment

root@master:/home/guanwu/k8s/nfs/staticprovisor# k apply -f test-nfs-static.yaml 
deployment.apps/test-nfs-static created
root@master:/home/guanwu/k8s/nfs/staticprovisor# k get pod -l app==test-nfs-static -owide
NAME                               READY   STATUS    RESTARTS   AGE    IP            NODE      NOMINATED NODE   READINESS GATES
test-nfs-static-6bd96c8f59-jt5v9   1/1     Running   0          114s   10.10.0.82    master    <none>           <none>
test-nfs-static-6bd96c8f59-sglqf   1/1     Running   0          114s   10.10.1.123   worker1   <none>           <none>
test-nfs-static-6bd96c8f59-tjfmb   1/1     Running   0          114s   10.10.2.122   worker2   <none>           <none>

验证各个pod节点共享同一个卷,获取所有pod列表,使用podList保存,并对每个Pod执行以下命令

kubectl exec -t $pod -- bash -c 'echo "start>>>" && echo "$(hostname) has dir:" && ls  /data  && echo "end<<<"'
root@master:/home/guanwu/k8s/nfs/staticprovisor# podList=$(k get pods -l app=test-nfs-static | sed 1d | awk -F ' ' '{print $1}')
root@master:/home/guanwu/k8s/nfs/staticprovisor# echo $podList
test-nfs-static-6bd96c8f59-jt5v9 test-nfs-static-6bd96c8f59-sglqf test-nfs-static-6bd96c8f59-tjfmb
root@master:/home/guanwu/k8s/nfs/staticprovisor# for pod in $podList; do kubectl exec -t $pod -- bash -c 'echo "start>>>" && echo "$(hostname) has dir:" && ls  /data  && echo "end<<<"';  done; 
start>>>
test-nfs-static-6bd96c8f59-jt5v9 has dir:
date.txt
pvc-324d5c94-27c6-4d6d-8af0-a08adc9a8de6
end<<<
start>>>
test-nfs-static-6bd96c8f59-sglqf has dir:
date.txt
pvc-324d5c94-27c6-4d6d-8af0-a08adc9a8de6
end<<<
start>>>
test-nfs-static-6bd96c8f59-tjfmb has dir:
date.txt
pvc-324d5c94-27c6-4d6d-8af0-a08adc9a8de6
end<<<
root@master:/home/guanwu/k8s/nfs/staticprovisor# 

可以看到,每个pvc的挂载目录都是同一个,这样就实现了调度到不同的Node节点的pod容器共享相同的存储卷

NFS flex-volume动态供应

juejin.cn/post/732065…

NFS csi动态供应

juejin.cn/post/732065…

参考

kubernetes.io/zh-cn/docs/…