我们知道,一个运行中的容器,默认情况下,对文件系统的写入,都是发生在其分层文件系统的可写层,一旦容器运行结束,所有文件都会被丢弃。所以,我们通常会在容器中挂载volumn,做数据持久化。但是,在某些业务场景下,我们需要pod中的多容器、多pod之间做数据共享,那么,k8s是如何支持共享存储的?我们在k8s中,应该怎么高效便捷的使用共享存储?接下来我会从如下三点介绍:

  1. 为什么要共享存储?
  2. k8s支持哪些共享存储方案,这些方案的差异是什么?
  3. 如何在k8s中高效便捷的使用共享存储?

为什么要做共享存储?

共享存储的场景有两种,分为pod内容器的数据共享,和多pod之间的数据共享。pod内的容器,有时会因为业务需要进行数据共享。比如,pod中有两个容器,一个容器需要收集另一个容器的日志,此时,让俩个容器挂载同一个volumn,避免容器间本地通信,提高效率。多pod之间可能会涉及到数据的同步,但是通过共享存储,可以有效解决数据同步可能出现的问题。

k8s支持的共享存储方案以及各种方案的差异

k8s支持的存储类型

k8s提供了以volumn插件为基础的存储系统,有很多存储实现了该插件,其中每一个插件都可以实现共享存储,只是实现功能上,会有一定的差异。现将其分类如下:

宿主机: emptyDir、HostPath
集群存储: configmap、secret
云存储: awsElasticBlockStore、gcePersistentDisk等
外部存储: glusterfs、Ceph等

宿主机类

emptyDir

emptyDir是宿主机上创建的临时目录,其优点是能方便地为Pod中的容器提供共享存储,不需要额外的配置。但它不具备持久性,且只能和一个pod绑定,如果Pod重启或删除,emptyDir也就没有了。根据这个特性,emptyDir特别适合Pod中容器需要临时共享存储空间的场景,比如生产者消费者用例。具体使用方式如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-emptydir
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
hostpath

hostpath会把宿主机的目录作为volumn挂载到pod的容器之中,比如一些容器需要在内部使用docker daemon, 则需要挂载宿主机的 /var/lib/docker 文件。Pod中的容器也可以共享hostpath存储,和emptyDir不同的是,如果pod重启或销毁,hostpath对应的目录仍然会存在。但缺点是pod和宿主机节点产生了耦合,当pod漂移后,如果想要继续使用宿主机上的文件,就需要给pod指定宿主机,这一定程度上,限制了Pod的使用。hostpath具体使用方式如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-hospath
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /var/lib/docker
      name: hostpath-volume
  volumes:
  - name: hostpath-volume
    hostPath:
      path: /var/lib/docker

综上:宿主机类存储,emptryDir只能在一个pod中,共享临时数据,pod删除重启后,数据也会删除。hostpath 可以共享单pod、多pod的数据,pod删除或重启,数据也可以保留,但必须将pod和宿主机进行绑定,另外,当宿主机宕机,数据也会丢失。

集群类存储

configmap、secret

configMap和secret可以作为volumn被pod使用,可以进行单pod,多pod数据共享,它们会在K8s集群的etcd中保存,但只适合存储配置信息和一些敏感文件。configmap具体使用方式如下:

//创建configmap
kind: ConfigMap
apiVersion: v1
metadata:
  name: lykops-config
  namespace: default  
  labels:
    software: apache
    project: lykops
    app: configmap
    version: v1
data:
  PWD: /
  user: lykops
  mysql.config : |-
    username: lykops
    host: localhost
    port: 3306
--------------------- 

//在pod中使用
apiVersion: v1 
kind: Pod 
metadata:
  name: lykops-cm-pod    
spec:
  containers:
  - name: lykops-cm-pod
    image: web:apache 
    command: ['sh',/etc/run.sh] 
    env:
    - name: SPECIAL_USER
      valueFrom:
        configMapKeyRef:
          name: lykops-config
          key: username
    volumeMounts:
    - name: config-volume
      mountPath: /data/
  volumes:
    - name: config-volume
      configMap:
        name: lykops-config

Note:configmap和secret虽然可以满足共享存储的需求,数据也能持久化,但只适合存储配置信息,并不适合存储数据。

云存储

AWS、AzureFile

云存储是云提供商提供的网络存储方案,如awsElasticBlockStore, 使用时,只需将AWS EBS volumn 绑定到Pod中。 当删除Pod时,EBS volumn的数据将被保留,并且仅卸载volumn,volumn也可以在单pod、多pod间共享。但缺点是,正在运行pod的节点,必须是此云提供商的节点,这和云提供商深度耦合,也不可取。
具体的使用方式是需要创建先创建云存储volumn,然后在pod中使用,如下:

//创建 aws的volumn
aws ec2 create-volume --availability-zone=eu-west-1a --size=10 --volume-type=gp2
----------
//pod中使用aws volumn
apiVersion: v1
kind: Pod
metadata:
  name: test-ebs
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: test-volume
  volumes:
  - name: test-volume
    # This AWS EBS volume must already exist.
    awsElasticBlockStore:
      volumeID: <volume-id>
      fsType: ext4

分布式存储

ceph 、glusterfs

k8s中可以使用分布式存储系统glusterfs、ceph,完成单pod容器、多pod之间的数据共享。当pod挂了,volumn的数据将会保留。重新调度启动后,pod可能在不同的Node上启动,但连接的都是同一个分布式存储卷,此外,分布式存储还提供了副本机制、高可用、易扩展等特性,能最大程度的解决原来单点存储的不可靠性。相对于 emptyDir 和 hostPath,这些 Volume 类型的最大特点就是不依赖 Kubernetes。Volume 的底层基础设施由独立的存储系统管理,与 Kubernetes 集群是分离的。数据被持久化后,即使整个 Kubernetes 崩溃也不会受损。

我们这里只是简单的说下分布式存储,解决的问题,和使用方式,后面我们会详细说明ceph和glusterfs的功能和使用。

apiVersion: v1
kind: Pod
metadata:
  name: test-ceph
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: ceph-volume
  volumes:
  - name: ceph-volume
    cephfs:
	  path: /ceph/cephfs 	
      monitor: "1.2.3.4:6789"
      secretFile: /etc/ceph/admin.secret
nfs

nfs volumn和分布式存储一样,也可以完美的解决单pod容器、多pod之间的数据共享,但是nfs服务器只有单节点,且传输效率低下。

高效便捷的使用共享存储

从上面我们知道,volumn提供了很好的数据持久化方案,但是使用起来很是麻烦,比如,我们后面需要使用的分布式存储,当要在pod中使用volumn时,就必须先在分布式存储中创建此目录。这意味着开发人员和存储系统耦合太高。此外,这种方式使用存储,并不能限制volumn的大小,设置访问模式和回收策略,功能很单一,所以k8s提供了pv和pvc机制。

PV和PVC

①:PV是k8s对底层网络共享存储的抽象,将共享存储定义为一种"资源",比如节点(Node)是pod可以“消费”的资源。PV由管理员进行创建和配置,它与共享存储的具体实现直接相关,例如GlusterFS、Ceph、RBD或GCE/AWS公有云提供的共享存储。

②:PVC则是用户对于存储资源的一个“申请”。就像Pod“消费”Node的资源一样,PVC会“消费”PV资源。PVC通常由用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,k8s会查找并提供满足条件的PV供pod使用。

PV

PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。
示例:下面是一个存储大小为1G,访问模式为read-write,回收策略为自动回收、存储后端类型为nfs的pv:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: test-pv
  labels:
    type: local
spec:
  storageClassName: nfs
  capacity:
    storage: 1Gi
  persistentVolumeReclaimPolicy: Recycle
  accessModes:
    - ReadWriteOnce
  nfs:
    path: "/nfsdata/pv1"
	server: 1.2.3.4

pv的核心设置和策略:
①:pv的访问模式:
ReadWriteOnce: PV 能以 read-write 模式 mount 到单个节点
ReadOnlyMany: PV 能以 read-only 模式 mount 到多个节点
ReadWriteMany: PV 能以 read-write 模式 mount 到多个节点
②:存储回收策略:
Retain:需要管理员手工回收。
Recycle:清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。
Delete:删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

③:存储类别(class)
PV可以设定其存储的类别(Class),通过storageClassName参数指定一个StorageClass资源对象的名称。具有特定“类别”的PV只能与请求了该“类别”的PVC进行绑定。未设定“类别”的PV则只能与不请求任何“类别”的PVC进行绑定。
④:后端存储类型:
k8s支持的PV类型很多,如GlusterFS、Ceph、nfs、云存储AzureFile、HostPath(宿主机目录,仅用于单机测试)等。每种存储类型都有各自的特点,在使用时需要根据它们各自的参数进行设置。

pvc

pvc是pod使用pv的方式,当需要在pod中使用pv时,只需要引入pvc,在pvc中定义需要使用的pv容量、访问模式、和pv类型。
示例:下面创建pvc申请pv资源,代表需要申请1GiB容量、访问模式为单点读写、存储类别为nfs的pv。

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

pvc的核心设置和策略:

①:资源请求(Resources):描述对存储资源的请求,目前仅支持request.storage的设置,即存储空间大小。
②:访问模式(Access Modes):PVC也可以设置访问模式,用于描述用户应用对存储资源的访问权限。可以设置的三种访问模式与PV相同。
③:PV选择条件(Selector):通过Label Selector的设置,PVC可以对系统中已存在的PV进行筛选。系统将根据标签选择出合适的PV与该PVC进行绑定。选择条件可以使用matchLabels和matchExpressions进行设置。如果两个条件都设置了,则Selector的逻辑是两组条件同时满足才能完成匹配。
④:存储类别(Class):PVC在定义时可以设定需要的后端存储的"类别"(通过storageClassName字段指定),只有设置了该Class的PV才能被系统选出,并与该PVC进行绑定。PVC也可以不设置Class需求。如果不设置storageClassName字段的值被设置为空(storageClassName=""),则表示该PVC不要求特定的Class。此时,只会在没有class的pv中进行匹配。

PV 和PVC的生命周期

PV可以看作可用的存储资源,PVC则是对存储资源的需求,PV和PVC的相互关系遵循下图所示的生命周期。

①:资源供应:创建pv,创建pv有两种方式,static(静态模式)和dynamic(动态模式)  
            静态模式:手动创建PV,在定义PV时需要将后端存储的特性进行设置。  
            动态模式:无须手动创建PV,而是通过StorageClass的设置对后端存储进行请求,此时要求PVC对存储类型进行声明,系统将自动完成PV的创建及与PVC的绑定。  
②:资源绑定:定义好pvc后,k8s会根据pvc的定义,在已存在pv中,匹配满足条件的pv进行绑定。一旦找到,就将该pv和用户定义的pvc进行绑定,此时就可以在pod中使用pvc。如果没有找到满足条件的pv,该pvc会一直处于pending状态,直到等到符合条件的pv创建,pvc和pv进行绑定后,pv就不能被其他的pvc所绑定了,因此,手动创建pv的方式会造成,资源的浪费,推荐使用动态创建的pv,与pvc绑定。  


③:资源使用:pod使用volumn的定义,将pvc挂载到容器的某个路径下使用。volume的类型为"persistentVolumeClaim"。这里需要注意的是,多个pod可以使用同一个PVC资源。  
④:资源释放:pod对volumn使用完毕,我们可以删除pvc,此时,与pvc绑定的PV的状态会被释放,状态为release,但此时pv还不能与其他pvc进行绑定,因为pv上的数据还没有被清除,只有在清楚之后pv才能再次使用  
⑤:资源回收:对于pv,可以设置指定的回收策略,比如有delete(自动删除数据),和retain(需要手工清理数据),回收后的pv可以再次被pvc使用。  
storage class

storage class是k8s对存储资源更深层次的抽象,主要完成了动态创建pv的过程,而动态创建pv,与静态创建pv主要有如下优点:
①:用户不需要了解pvc申请存储的细节。
②:不用预先去创建pv让pvc进行绑定。
③:用户申请的pvc会和pv的容量完全匹配,不会造成资源的浪费。
storage核心参数和设置:
①:provisioner:后端资源的提供者,目前k8s支持的Provisioner都以"kubernetes.io/"为开头
②:parameters: 后端资源提供者的参数设置,不同的Provisioner会有不同的参数设置
示例:给出glusterfs的storage Class配置、并创建pvc在pod中使用:

// gulsterfs Class 配置
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: glusterfs-sc
provisioner: kubernetes.io/glusterfs
reclaimPolicy: Retain
parameters:
  gidMax: "50000"
  gidMin: "40000"
  resturl: http://1.2.3.4:30088
  volumetype: replicate:2
  restauthenabled: "true"
  restuser: "admin"
  restuserkey: "123456"
#  secretNamespace: "default"
#  secretName: "my-secret"

创建pvc

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: kafka-pvc
  namespace: kube-system
spec:
  storageClassName: glusterfs-sc
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 20Gi

pod中使用pvc

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-use-pvc
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.11.4-alpine
        imagePullPolicy: IfNotPresent
        name: nginx-use-pvc
        volumeMounts:
        - mountPath: /test
          name: my-pvc
      volumes:
      - name: my-pvc
        persistentVolumeClaim:
          claimName: kafka-pvc

参考:https://kubernetes.io/docs/concepts/storage/persistent-volumes/

Logo

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

更多推荐