美文网首页
聊一聊Kubernetes数据持久化管理

聊一聊Kubernetes数据持久化管理

作者: 小二上酒8 | 来源:发表于2022-08-30 09:32 被阅读0次

    Kubernetes为了能更好的支持有状态应用的数据存储问题,除了基本的HostPath和EmptyDir提供的数据持久化方案之外,还提供了PV,PVC和StorageClass资源对象来对存储进行管理。

    PV的全称是Persistent Volume(持久化卷),是对底层数据存储的抽象,PV由管理员创建、维护以及配置,它和底层的数据存储实现方法有关,比如Ceph,NFS,ClusterFS等,都是通过插件机制完成和共享存储对接。

    PVC的全称是Persistent Volume Claim(持久化卷声明),我们可以将PV比喻为接口,里面封装了我们底层的数据存储,PVC就是调用接口实现数据存储操作,PVC消耗的是PV的资源。

    StorageClass是为了满足用于对存储设备的不同需求,比如快速存储,慢速存储等,通过对StorageClass的定义,管理员就可以将存储设备定义为某种资源类型,用户根据StorageClass的描述可以非常直观的知道各种存储资源的具体特性,这样就可以根据应用特性去申请合适的资源了。

    安装存储系统

    存储系统的选择有很多,常见的有NFS、Ceph、GlusterFS、FastDFS等,具体使用什么根据企业情况而定。在这里使用的是NFS,下面简单介绍一下如何安装。

    1、安装服务

    $ yum install nfs-utils rpcbind -y
    

    2、创建共享目录

    $ mkdir /data/k8s -p
    

    3、配置NFS配置文件

    $ vim /etc/exports
    /data/k8s *(rw,sync,no_root_squash)
    

    4、启动服务

    $ systemctl start rpcbind
    $ systemctl start nfs
    $ systemctl enable rpcbind
    $ systemctl enable nfs
    

    5、测试

    $ showmount -e 192.168.205.128
    Export list for 192.168.205.128:
    /data/k8s *
    

    PS:所有节点都需要安装NFS客户端。

    PV

    PV(Persistent Volume)作为Kubernetes存储设备,可以由管理员提前配置,也可以通过StorageClass来动态供应。

    PV是集群资源,可以通过kubectl explain pv来查看如何配置,主要包括存储能力,访问模式,存储类型,回收信息等关键信息。例如:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: my-pv01
      labels:
        storage: pv
    spec:
      accessModes:
      - ReadWriteOnce
      capacity:
        storage: 1Gi
      persistentVolumeReclaimPolicy: Recycle
      nfs:
        path: /data/k8s
        server: 192.168.205.128
    

    参数说明:

    1、accessMode:访问模式,有ReadWriteOnce,ReadOnlyMany,ReadWriteMany。其中:

    ReadWriteOnce:表示具有读写权限,但是只能被一个node挂载一次。
    ReadOnlyMany:表示具有只读权限,可以被多个node多次挂载。
    ReadWriteMany:表示具有读写权限,可以被多个node多次挂载。
    2、capacity:持久卷资源和容量的描述,存储大小是唯一可设置或请求的资源。

    3、persistentVolumeReclaimPolicy:回收策略,也就是释放持久化卷时的策略,其有以下几种:

    Retain:保留数据,如果要清理需要手动清理数据,默认的策略。
    Delete:删除,将从Kubernetes中删除PV对象,以及外部基础设施中相关的存储资产,比如AWS EBS, GCE PD, Azure Disk, 或Cinder volume。
    Recycle:回收,清楚PV中的所有数据,相当于执行rm -rf /pv-volume/*。
    创建过后,PV的状态如下:

    $ kubectl get pv
    NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
    my-pv01   1Gi        RWO            Recycle          Available                                   5s
    
    $ kubectl describe pv my-pv01 
    Name:            my-pv01
    Labels:          storage=pv
    Annotations:     <none>
    Finalizers:      [kubernetes.io/pv-protection]
    StorageClass:    
    Status:          Available
    Claim:           
    Reclaim Policy:  Recycle
    Access Modes:    RWO
    VolumeMode:      Filesystem
    Capacity:        1Gi
    Node Affinity:   <none>
    Message:         
    Source:
        Type:      NFS (an NFS mount that lasts the lifetime of a pod)
        Server:    192.168.205.128
        Path:      /data/k8s
        ReadOnly:  false
    Events:        <none>
    

    当前PV的状态是Available,表示处于随时可用状态。PV总共有以下四种状态:

    Available(可用):表示可用状态,还未被任何 PVC 绑定。
    Bound(已绑定):表示 PVC 已经被 PVC 绑定。
    Released(已释放):PVC 被删除,但是资源还未被集群重新声明。
    Failed(失败):表示该 PV 的自动回收失败。
    单纯的创建PV,我们并不能直接使用,需要使用PVC(Persistent Volume Claim)来进行声明。

    PVC
    PVC(Persistent Volume Claim)用于表达用户对存储的需求,申请PVC会消耗掉PV的资源,可以通过kubectl explain pvc来查看帮助文档。

    在上一节我们创建了PV,现在要申明PVC,如下:

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

    spec参数说明:

    1、accessModes:主要定义卷所应该拥有的访问模式

    2、resources:主要定义卷应该拥有的最小资源

    3、dataSource:定义如果提供者具有卷快照功能,就会创建卷,并将数据恢复到卷中,反之不创建

    4、selector:定义绑定卷的标签查询

    5、storageClassName:定义的storageClass的名字

    6、volumeMode:定义卷的类型

    7、volumeName:需要绑定的PV的名称链接

    创建过后,查看PV和PVC的状态,如下:

    $ kubectl get pvc
    NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc-test   Bound    my-pv01   1Gi        RWO                           2s
    $ kubectl get pv
    NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
    my-pv01   1Gi        RWO            Recycle          Bound    default/pvc-test                           20m
    

    我们从上面可以看到pvc处于Bound状态,Bound的VOLUME是my-pv01,我们再看pv的状态有Available变为Bound,其CLAIM是default/pvc-test,其中default为namespace名称。

    在上面我们创建了一个PVC,其绑定了我们创建的PV,如果此时我们再创建一个PVC,结果又会如何?我们copy以下上面的PVC文件,将其名称改一下,如下:

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

    然后查看PVC的状态,如下:

    $ kubectl get pvc
    NAME        STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc-test    Bound     my-pv01   1Gi        RWO                           3m57s
    pvc-test2   Pending                                                      4s
    

    我们可以看到我们刚创建的pvc-test2的STATUS处于Pending状态,这是由于集群里声明的PV都使用完了,PVC在申请的时候没有找到合适的PV,所以处于这个状态,这时候如果我们创建一个新的并满足要求的PV,则可以看到这个PVC会处于Bound状态。如下:

    $ kubectl get pv
    NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   REASON   AGE
    my-pv01   1Gi        RWO            Recycle          Bound       default/pvc-test                           27m
    my-pv02   1Gi        RWO            Recycle          Available                                              5s
    $ kubectl get pvc
    NAME        STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc-test    Bound    my-pv01   1Gi        RWO                           6m50s
    pvc-test2   Bound    my-pv02   1Gi        RWO                           2m57s
    

    PVC也在申领PV的时候也不是随意申领的,它需要符合以下要求:(1)PVC申领的模式要和PV匹配上,假如PVC的模式是ReadWriteOnce,而PV的模式是ReadWriteMany,则申领部成功。(2)PVC申领的容量要小于等于PV的容量,否则申请不成功。(3)一个PV只能绑定一个PVC。

    另外,如果我们的PVC需求的容量小于PV的可用容量,绑定的容量是PV的可用容量。

    StorageClass
    上面介绍的PV和PVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,Kubernetes提供一种自动创建PV的机制,叫StorageClass,它的作用就是创建PV的模板。

    具体来说,StorageClass会定义一下两部分:

    PV的属性 ,比如存储的大小、类型等。
    创建这种PV需要使用到的存储插件,比如Ceph等。
    有了这两部分信息,Kubernetes就能够根据用户提交的PVC,找到对应的StorageClass,然后Kubernetes就会调用 StorageClass声明的存储插件,创建出需要的PV。

    这里我们以NFS为例,要使用NFS,我们就需要一个nfs-client的自动装载程序,我们称之为Provisioner,这个程序会使用我们已经配置好的NFS服务器自动创建持久卷,也就是自动帮我们创建PV。说明:

    自动创建的PV会以{pvcName}-{pvName}的目录格式放到NFS服务器上。 如果这个PV被回收,则会以archieved-{pvcName}-{pvName}这样的格式存放到NFS服务器上。
    安装NFS Provisioner
    1、创建ServiceAccount,为NFS Provisioner授权:

    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: nfs-client-provisioner
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: nfs-client-provisioner-clusterrole
    rules:
      - 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: ["list", "watch", "create", "update", "patch"]
      - apiGroups: [""]
        resources: ["endpoints"]
        verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: nfs-client-provisioner-clusterrolebinding
    subjects:
    - kind: ServiceAccount
      name: nfs-client-provisioner
      namespace: default
    roleRef:
      kind: ClusterRole
      name: nfs-client-provisioner-clusterrole
      apiGroup: rbac.authorization.k8s.io
    

    2、创建NFS Provisioner


    kind: Deployment
    metadata:
      name: nfs-client-prosioner
    spec:
      replicas: 1
      strategy:
        type: Recreate
      selector:
        matchLabels:
          app: nfs-client-prosioner
      template:
        metadata:
          labels:
            app: nfs-client-prosioner
        spec:
          serviceAccountName: nfs-client-provisioner
          containers:
          - name: nfs-client-prosioner
            image: registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:4.0
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - name: nfs-client-root
              mountPath: /data/pv
            env:
            - name: PROVISIONER_NAME
              value: rookieops/nfs
            - name: NFS_SERVER
              value: 192.168.205.128
            - name: NFS_PATH
              value: /data/k8s
          volumes:
          - name: nfs-client-root
            nfs:
              server: 192.168.205.128
              path: /data/k8s
    

    执行完成后,查看NFS Provisioner的状态,如下:

    $ kubectl get po
    NAME                                    READY   STATUS    RESTARTS   AGE
    nfs-client-prosioner-54d64dfc85-b4ht4   1/1     Running   0          10s
    

    使用StorageClass
    上面已经创建好NFS Provisioner,现在我们可以直接创建StroageClass,如下:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: nfs
    provisioner: rookieops/nfs
    

    每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。

    在配置StorageClass的时候,如果没有指定reclaimPolicy,则默认是Delete,除此之外,还有Retain。

    StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。

    使用kubectl apply -f sc.yaml创建StorageClass,创建完成过后如下:

    $ kubectl get sc
    NAME   PROVISIONER     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    nfs    rookieops/nfs   Delete          Immediate           false                  9m41s
    

    现在,我们就可以使用动态存储申领PVC,如下:

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

    使用kubectl apply -f pvc-from-sc.yaml,查看PVC创建情况,如下:

    $ kubectl get pvc
    NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc-from-sc   Bound    pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            nfs            8s
    $ kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
    pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            Delete           Bound    default/pvc-from-sc   nfs                     86s
    

    可以看到自动创建了一个PV,然后和PVC进行绑定。

    为了方便使用,有时候会给集群默认设置一个StorageClass,以便在需要使用动态存储,但是未声明的情况下使用默认的动态存储。设置方式如下:

    $ kubectl patch storageclass nfs -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
    

    通过向其添加 storageclass.kubernetes.io/is-default-class 注解来将特定的 StorageClass 标记为默认。当集群中存在默认的 StorageClass 并且用户创建了一个未指定 storageClassName 的 PersistentVolumeClaim 时, DefaultStorageClass 准入控制器会自动向其中添加指向默认存储类的 storageClassName 字段。

    请注意,集群上最多只能有一个 默认 存储类,否则无法创建没有明确指定 storageClassName 的 PersistentVolumeClaim。

    如果要取消默认StorageClass,只需要把注解设置为flase即可,如下:

    $ kubectl patch storageclass nfs -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
    

    如果我们要在Pod中使用PVC,则直接如下声明:

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: nfs-pvc
          mountPath: /mnt
      restartPolicy: Never
      volumes:
      - name: nfs-pvc
        persistentVolumeClaim:
          claimName: pvc-from-sc
    

    可以进入容器,到挂载目录输出,例如:

    $ kubectl exec -it nginx -- /bin/bash
    root@nginx:/mnt# echo "test" > /mnt/text.txt
    

    然后到NFS对应的目录查看是否一致。

    $ cd /data/k8s/default-pvc-from-sc-pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a
    $ cat text.txt 
    test
    

    这表示Pod使用持久化成功。

    总结
    在Kubernetes中,虽然我们建议使用无状态应用,但是对于有些特殊应用,数据持久化还是必不可少的。数据持久化的难度不在于创建几个PV或者PVC,而是后端的存储系统,比如Ceph,如果使用它作为后端存储,你必须对其非常熟悉,方便在出问题的时候好排查,如果你对这些存储系统都不熟悉,在使用的时候可能会出现很多问题。

    相关文章

      网友评论

          本文标题:聊一聊Kubernetes数据持久化管理

          本文链接:https://www.haomeiwen.com/subject/ykvlnrtx.html