在Docker中我们知道,要想实现数据的持久化(所谓Docker的数据持久化即数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中,常用的手段就是Volume数据卷。在K8S中,也提供了存储模型Volume,支持我们将应用中的数据持久化存储到容器中。

(1)学习Volume,以及Kubernetes如何通过Volume为集群中的容器提供存储;
(2)实践几种常用的Volume类型并理解它们各自的应用场景;
(3)学习Kubernetes如何通过 PersistentVolume和PersistentVolumeClaim分离集群管理员与集群用户的职责,并实践Volume的静态供给和动态供给。

1.1 使用K8S Volume

(1)emptyDir
作为K8S最基础的Volume类型,emptyDir提供了最基础的持久化方案,但是这个方案不怎么好。因为,emptyDir对于Pod来说并非持久的(它对于容器来说是持久化的),因为当Pod从节点删除时,Volume的内容也会被删除。但如果只是容器被销毁而Pod还在,则Volume不会受影响。
换句话说:emptyDirVolume的生命周期与Pod一致。鉴于此特性,不建议在实际中使用此类型Volume。

vi volume-demo.yaml

apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
spec:
  containers:
    - image: busybox
      name: producer
      volumeMounts:
        - mountPath: /producer_dir
          name: shared-volume
      args:
        - "/bin/sh"
        - "-c"
        - "echo 'hello world' > /producer_dir/hello; sleep 30000"
    - image: busybox
      name: consumer
      volumeMounts:
        - mountPath: /consumer_dir
          name: shared-volume
      args:
        - "/bin/sh"
        - "-c"
        - "cat /consumer_dir/hello; sleep 30000"
  volumes:
    - name: shared-volume
      emptyDir: {}


这里我们模拟了⼀个producer-consumer场景。Pod有两个容器producer和consumer,它们共享⼀个Volume。producer负责往Volume中写数据,consumer则是从Volume读取数据。
①文件最底部volumes定义了⼀个emptyDir类型的Volume shared-volume。
②producer容器将shared-volumemount到/producer_dir目录。
③producer通过echo将数据写到文件hello里。
④consumer容器将shared-volumemount到/consumer_dir目录。
⑤consumer通过cat从文件hello读数据。

[root@k8s-master ~]# vi volume-demo.yaml
[root@k8s-master ~]#  kubectl apply -f volume-demo.yaml
pod/producer-consumer created
[root@k8s-master ~]# kubectl get pod
NAME                                READY   STATUS              RESTARTS   AGE
producer-consumer                   0/2     ContainerCreating   0          5s
[root@k8s-master ~]# kubectl logs producer-consumer consumer
hello world

kubectl logs显示容器consumer成功读到了producer写入的数据,验证了两个容器共享emptyDir Volume。
emptyDir是Host上创建的临时目录,其优点是能够方便地为Pod中的容器提供共享存储,不需要额外的配置。

1.2 hostPath

相对于emptyDir,hotPath则克服了其生命周期的弱点,如果Pod被销毁,hostPath对应的目录还是会被保留。不过,如果一旦Host崩溃,hostPath也就无法访问了。因为,hostPath是将DockerHost文件系统中已经存在的目录mount给Pod的容器,所以会依赖于Host。

大部分应用都不会使用hostPath Volume,因为这实际上增加了Pod与节点的耦合,限制了Pod的使用。不过那些需要访问Kubernetes或Docker内部数据(配置文件和⼆进制库)的应用则需要使用hostPath。

1.3 外部Storage Provider

如果Kubernetes部署在诸如AWS、GCE、Azure等公有云上,可以直接使用云硬盘作为Volume。下面给出⼀个AWSElasticBlockStore的例子。
vi volume-demo1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: using-ebs
spec:
  containers:
    - image: busybox
      name: using-ebs
      volumeMounts:
        - mountPath: /test-ebs
          name: ebs-volume
  volumes:
    - name: ebs-volume
      awsElasticBlockStore:
        volumeID: <volume-id>  # Replace <volume-id> with the actual volume ID
        fsType: ext4


Kubernetes Volume也可以使用主流的分布式存储,比如Ceph、GlusterFS等。

1.4 PersistentVolume &PersistentVolumeClaim

PV是外部存储系统中的一块存储空间,由管理员创建和维护。与Volume一样,PV具有持久性,生命周期独立于Pod。

PVC则是对PV的申请(Claim),PVC通常由普通用户创建和维护。当需要为Pod分配存储资源的时候,用户就可以创建一个PVC,指明存储资源的容量大小和访问方式(比如ReadOnly)等信息,K8S会查找并提供满足条件的PV。

与K8S Volume一样,K8S PersistentVolume也支持多种类型的存储,比如NFS、AWS EBS、Ceph等等。

1.5 NFS PV的使用

作为准备工作,我们已经在k8s-master节点上搭建了⼀个NFS服务器,目录为/nfsdata。

[root@k8s-master nfs]# showmount -e
Export list for k8s-master:
/nfsdata *

创建目录 pv1 后面使用

mkdir /nfsdata/pv1
chmod o+w /nfsdata/pv1

下面创建⼀个PV mypv1,配置文件nfs-pv1.yml如下所示
vi nfs-pv1.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi ①
  accessModes:
    - ReadWriteOnce ②
  persistentVolumeReclaimPolicy: Recycle ③
  storageClassName: nfs ④
  nfs:
    path: /nfsdata/pv1 ⑤
    server: 192.168.200.128

① capacity指定PV的容量为1GB。
② accessModes指定访问模式为ReadWriteOnce,支持的访问模式有3种:
ReadWriteOnce表示PV能以read-write模式mount到单个节点。
ReadOnlyMany表示PV能以read-only模式mount到多个节点。
ReadWriteMany表示PV能以read-write模式mount到多个节点。
③ persistentVolumeReclaimPolicy 指 定 当 PV 的 回 收 策 略 为Recycle,支持的策略有3种:Retain表示需要管理员手工回收;
Recycle表示清除PV中的数据,效果相当于执行rm-rf/thevolume/*。
Delete表示删除StorageProvider上的对应存储资源,例如AWSEBS、GCEPD、AzureDisk、OpenStackCinderVolume等。
④ storageClassName指定PV的class为nfs。相当于为PV设置了⼀个
分类,PVC可以指定class申请相应class的PV。
⑤ 指定PV在NFS服务器上对应的目录。

创建mypv1,如下所示:

[root@k8s-master ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 created
[root@k8s-master ~]# kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   REASON   AGE
mypv1             1Gi        RWO            Recycle          Available                      nfs                     11s

STATUS为Available,表示mypv1就绪,可以被PVC申请。
接下来创建PVC mypvc1,配置文件nfs-pvc1.yml如下所示。
vi nfs-pvc1.yml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

PVC就很简单了,只需要指定PV的容量、访问模式和class即可。
创建mypvc1,如下所示:

[root@k8s-master ~]# kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created
[root@k8s-master ~]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1   Bound    mypv1    1Gi        RWO            nfs            7s
[root@k8s-master ~]# kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM              STORAGECLASS   REASON   AGE
mypv1             1Gi        RWO            Recycle          Bound      default/mypvc1     nfs                     2m59s
pv-volume-mysql   8Gi        RWO            Retain           Released   default/my-mysql                           25d

从kubectl get pvc和kubectl get pv的输出可以看到mypvc1已经Bound到mypv1,申请成功。
接下来就可以在Pod中使用存储了,Pod配置文件nfs-pod1.yml如下所示:
vi nfs-pod1.yml

apiVersion: v1
metadata:
  name: mypod1
spec:
  containers:
    - name: mypod1
      image: busybox
      args:
        - "/bin/sh"
        - "-c"
        - "sleep 30000"
      volumeMounts:
        - mountPath: "/mydata"
          name: mydata
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc1

创建mypod1,如下所示。

 
[root@k8s-master ~]# kubectl describe pod mypod1
Name:         mypod1
Namespace:    default
Priority:     0
Node:         k8s-node2/192.168.200.130
Start Time:   Thu, 29 Feb 2024 13:31:14 +0800
Labels:       <none>
Annotations:  Status:  Running

验证PV是否可用

[root@k8s-master ~]# kubectl exec mypod1 -- touch /mydata/hello
[root@k8s-master ~]# ls /nfsdata/pv1/
hello  test.txt

可见,在Pod中创建的文件/mydata/hello确实已经保存到了NFS服务器目录/nfsdata/pv1中。

1.6 回收PV

当不需要使用PV时,可用删除PVC回收PV

[root@k8s-master ~]# kubectl delete pvc mypvc1
[root@k8s-master ~]# kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM              STORAGECLASS   REASON   AGE
mypv1             1Gi        RWO            Recycle          Released   default/mypvc1     

此时mypv1的状态为Released,表示已经解除了与mypvc1的Bound,正在清除数据,不过此时还不可用。
当数据清除完毕,mypv1的状态重新变为Available,此时可以被新的PVC申请。

[root@k8s-master ~]# kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM              STORAGECLASS   REASON   AGE
mypv1             1Gi        RWO            Recycle          Available   default/mypvc1     

因为PV的回收策略设置为Recycle,所以数据会被清除,但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为Retain。
vi nfs-pv1.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi 
  accessModes:
    - ReadWriteOnce 
  persistentVolumeReclaimPolicy: Retain 
  storageClassName: nfs 
  nfs:
    path: /nfsdata/pv1 
    server: 192.168.200.128

通过kubectl apply更新PV,回收策略已经变为Retain。

1.7 PV动态供给

在前面的例子中,我们提前创建了PV,然后通过PVC申请PV并在Pod中使用,这种方式叫作静态供给(Static Provision)。
与之对应的是动态供给(DynamicalProvision),即如果没有满足PVC条件的PV,会动态创建PV。相比静态供给,动态供给有明显的优势:不需要提前创建PV,减少了管理员的工作量,效率高。
动态供给是通过StorageClass实现的,StorageClass定义了如何创建PV,下面给出两个例子。
(1)StorageClass standard

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain

(2)StorageClass slow

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  zones: us-east-1d,us-east-1c
  iopsPerGB: "10"

这两个StorageClass都会动态创建AWSEBS,不同点在于standard创建的是gp2类型的EBS,而slow创建的是io1类型的EBS。
不同类型的EBS支持的参数可参考AWS官方文档。
StorageClass支持Delete和Retain两种reclaimPolicy,默认是 Delete。与之前⼀样,PVC在申请PV时,只需要指定StorageClass、容量以及访问模式即可,如下所示。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard

除了AWSEBS,Kubernetes还支持其他多种动态供给PV的Provisioner,完整列表请参考https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐