美文网首页Docker容器
在K8S中使用Local持久卷

在K8S中使用Local持久卷

作者: 何约什 | 来源:发表于2019-03-09 17:46 被阅读4次

    基本概念

    Volumn

    由于容器本身是非持久化的,因此需要解决在容器中运行应用程序遇到的一些问题。首先,当容器崩溃时,kubelet将重新启动容器,但是写入容器的文件将会丢失,容器将会以镜像的初始状态重新开始;第二,在通过一个Pod中一起运行的容器,通常需要共享容器之间一些文件。Kubernetes通过卷解决上述的两个问题。

    在Docker有卷的概念卷,但Docker中存储卷只是磁盘的或另一个容器中的目录,并没有对其生命周期进行管理。Kubernetes的存储卷有自己的生命周期,它的生命周期与使用的它Pod生命周期一致。因此,相比于在Pod中运行的容器来说,存储卷的存在时间会比的其中的任何容器都长,并且在容器重新启动时会保留数据。当然,当Pod停止存在时,存储卷也将不再存在。在Kubernetes支持多种类型的卷,而Pod可以同时使用各种类型和任意数量的存储卷。在Pod中通过指定下面的字段来使用存储卷:

    • spec.volumes:通过此字段提供指定的存储卷
    • spec.containers.volumeMounts:通过此字段将存储卷挂接到容器中

    Persistent Volumn(PV)

    PV是系统管理员设置的存储,它是群集的一部分,是一种资源,所以它有独立于Pod的生命周期。
    PVC是用户存储的请求。它与Pod相似,Pod消耗节点的CPU和内存资源,PVC则消耗PV资源,可以生命特定的容量大小和访问模式。

    PV和PVC遵循如下的生命周期管理

    配置

    PV有两种配置方式:静态或动态

    • 静态
      管理员创建静态一些PV,它们带有可供集群用户使用的实际存储的细节,它们存在于Kubernetes API中,可用于消费;
    • 动态
      当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses:PVC 必须请求存储类,并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置。

    要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass 准入控制器。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作。有关 API server 命令行标志的更多信息,请检查 kube-apiserver 文档。

    绑定

    一旦用户创建或已经创建了具有特定存储量的 PersistentVolumeClaim 以及某些访问模式。Kubernetes控制器会监视到新的 PVC,并寻找匹配的 PV,并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则控制器会始终将该 PV 绑定到 PVC。总之,用户总会得到他们所请求的存储,但是容量可能超出请求的数量。一旦 PV 和 PVC 绑定后,PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射。

    如果没有匹配的PV,PVC将无限期地保持未绑定状态。随着匹配PV的可用,PVC将被绑定。例如,配置了许多 50Gi PV的集群将不会匹配请求 100Gi 的PVC。将100Gi PV 添加到群集时,则可以绑定到该 PVC。

    使用

    Pod 使用PVC作为卷。集群检查PVC以查找绑定的卷并为集群挂载该卷。对于支持多种访问模式的卷,用户指定在使用声明作为容器中的卷时所需的模式(读写、只读)。

    用户生命了PVC,并且该PVC是绑定的,则只要用户需要,绑定的 PV 就属于该用户。用户通过在 Pod 的 volume 配置中包含persistentVolumeClaim来调度 Pod 并访问用户声明的 PV。

    我们项目中常用的挂载模式

    K8S支持的卷类型很多,主要分为分布式文件系统、ConfigMap和本地文件系统这几种,其中本地文件系统支持:hostPath和local(从1.11开始出了Beta版本,编写本文时目前K8S最新版本是1.13了)。

    在我们目前项目的实际开发中,我们常用两种卷挂载模式:

    • hostPath
      一般hostPath的使用,多半用于规范的一些文件和目录的挂载,譬如监控用到的相关的文件、时区文件的挂载,一般都直接从宿主机直接挂载到容器中,这种情况下,我们会用到hostPath。

    • ConfigMap
      另外对于配置的挂载,一般会用到ConfigMap。

    但是对于使用到本地磁盘来存储数据时,hostPath往往不太适合我们了,虽然它能够让Pod使用本地存储,将Node文件系统中的文件或者目录挂载到容器内,但是在这种应用场景,hostPath并不适合生产环境使用,主要原因如下:

    • 在集群中为了使用hostPath,我们需要通过NodeSelector来进行精确到节点的调度,这样非常不利于系统的开发,与K8S的调度灵活性背道而驰;
    • hostPath卷的大小不在调度器的考虑范围,它也不能申明存储空间大小,这样调度时需要额外做磁盘大小的检查;

    当然hostPath还有其他的缺点,由于以上这两个关键的缺陷,在真正需要持久化数据存储的场景,我们不得不考虑local存储卷了。

    local存储卷的工作原理

    Local PV是从kuberntes 1.10开始引入,本质目的是为了解决hostPath的缺陷。通过PV控制器与Scheduler的结合,会对local PV做针对性的逻辑处理,从而,让Pod在多次调度时,能够调度到同一个Node上。

    local存储卷的使用

    首先,我们要准备好K8S环境,检查一下K8S中的节点。

    # kubectl get nodes
    yuxianbing@ubuntu:~$ kubectl get nodes
    NAME                               STATUS    ROLES     AGE       VERSION
    wuxi-inner-c2-4-10-25-69-15        Ready     <none>    10d       v1.13.0
    wuxi-inner-c2-4-10-25-69-17        Ready     <none>    10d       v1.13.0
    wuxi-inner-c3-4.1-10-25-68-239     Ready     <none>    22d       v1.13.0
    wuxi-inner-c3-4.1-10-25-68-240     Ready     <none>    22d       v1.13.0
    wuxi-inner-c3-4.1-10-25-68-241     Ready     <none>    22d       v1.13.0
    

    现在登录到节点10.25.68.239上,然后手工创建一个目录,我们将后在后面创建的local PV绑定到这个目录中。

    root@ubuntu:/home/yuxianbing# mkdir -p /data/pv
    root@ubuntu:/home/yuxianbing# 
    

    创建local的Storage Class

    cat > storage-class.yml <<EOF
    kind: StorageClass
    apiVersion: storage.k8s.io/v1
    metadata:
      name: local-storage
    provisioner: kubernetes.io/no-provisioner
    volumeBindingMode: WaitForFirstConsumer
    EOF
    
    kubectl create -f storage-class.yml
    

    查看一下创建的storage class

    yuxianbing@ubuntu:~$ kubectl get sc
    NAME            PROVISIONER                    AGE
    local-storage   kubernetes.io/no-provisioner   2d
    

    创建PV:

    cat > pv-sdc.yml <<EOF
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: local-pv-sdc
    spec:
      capacity:
        storage: 440Gi
      accessModes:
      - ReadWriteOnce
      persistentVolumeReclaimPolicy: Retain
      storageClassName: local-storage
      local:
        path: /data/pv
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
              - cka
    EOF
    
    kubectl create -f pv-sdc.yml
    

    查看一下pv情况

    yuxianbing@ubuntu:~$ kubectl get pv
    NAME           CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS    REASON    AGE
    local-pv-sdc   440Gi      RWO            Retain           Available             local-storage             15s
    

    创建PVC

    cat > pvc1.yml <<EOF
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: pvc1
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: local-storage
      resources:
        requests:
          storage: 100Gi
    EOF
    
    kubectl create -f pvc1.yml
    

    PVC将会一直处于Pending状态直到我们创建一个Pod使用它。

    yuxianbing@ubuntu:~$ kubectl get pvc
    NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS    AGE
    pvc1      Pending                                       local-storage   5s
    

    创建一个deployment使用这个pvc。

    cat > deploy-nginx.yml <<EOF
    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:1.7.9
            ports:
            - containerPort: 80
            volumeMounts:
            - mountPath: "/usr/share/nginx/html"
              name: storage
          volumes:
            - name: storage
              persistentVolumeClaim:
                claimName: pvc1
    EOF
    
    kubectl create -f deploy-nginx.yml
    

    pvc现在绑定到对应的pv上面了。

    yuxianbing@ubuntu:~$ kubectl get pvc
    NAME      STATUS    VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS    AGE
    pvc1      Bound     local-pv-sdc   440Gi      RWO            local-storage   59s
    

    查看pod被调度的节点,可以看到这个pod是会被调度到PV所在的Node:

    yuxianbing@ubuntu:~$ kubectl get pod -o wide
    NAME                                                READY     STATUS              RESTARTS   AGE       IP              NODE
    nginx-deployment-5bd8c777fd-v2xn9                   1/1       Running   0          11m       10.221.56.63         wuxi-inner-c3-4.1-10-25-68-239
    

    我们再次登录到10.25.68.239,在/data/pv下生成index.html文件

    echo "Hello World!" > /data/pv/index.html
    

    检查一下index.html文件的服务

    yuxianbing@ubuntu:~$ curl http://10.221.56.63
    Hello World!
    yuxianbing@ubuntu:~$ kubectl exec nginx-deployment-5bd8c777fd-v2xn9 cat /usr/share/nginx/html/index.html 
    Hello World!
    

    总结

    Local持久卷基本具备了hostPath的绑定本地文件系统目录的能力和方便性,同时自动具备调度到指定节点的能力,并且可以对持久卷进行管理。
    唯一的问题在于,我们还需要手工去对应的节点创建对应的目录和删除对应的目录,这需要结合我们的应用系统来进行统一的设计和管理。
    总得来说,对于状态应用程序的部署来说,Local持久卷能够提供分布式存储无法提供的高性能,同时具备了一定的调度的灵活性,是一个不错的选择。

    相关文章

      网友评论

        本文标题:在K8S中使用Local持久卷

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