20.Kubernetes共享存储

Kubermetes对于有状态的容器应用或者对数据须要持久化的应用,不只须要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,并且须要更加可靠的存储来保存应用产生的重要数据,以便容器应用在重建以后,仍然可使用以前的数据。不过,存储资源和计算资源(CPU/内存)的管理方式彻底不一样。为了可以屏蔽底层存储实现的细节,让用户方便使用,同时能让管理员方便管理, Kubernetes从v1.0版本就引入PersistentVolumePersistentVolumeClaim两个资源对象来实现对存储的管理子系统。html

PersistentVolume (PV)是对底层网络共享存储的抽象,将共享存储定义为一种“资源”,好比节点(Node) 也是一种容 器应用能够“消费”的资源。PV由管理员进行建立和配置,它与共享存储的具体实现直接相关,例如GlusterFS、iSCSI、 RBD或GCB/AWS公有云提供的共享存储,经过插件式的机制完成与共享存储的对接,以供应用访问和使用。node

PersistentVolumeClaim (PVC)则是用户对于存储资源的一个“申请”。就像Pod“消费”Node的资源一-样, PVC会“消费”PV资源。PVC 能够申请特定的存储空间和访问模式。nginx

StorageClass,使用PVC“申请”到必定的存储空间仍然不足以知足应用对于存储设备的各类需求。一般应用程序都会对存储设备的特性和性能有不一样的要求,包括读写速度、并发性能、数据冗余等更高的要求,Kubernetes 从v1.4版本开始引入了-一个新的资源对象StorageClass,用于标记存储资源的特性和性能。到v1.6版本时,StorageClass动态资源供应的机制获得了完善,实现了存储卷的按需建立,在共享存储的自动化管理进程中实现了重要的一步。git

经过StorageClass的定义,管理员能够将存储资源定义为某种类别(Class), 正如存储设备对于自身的配置描述(Profile),例如“快速存储”“慢速存储”“有数据冗余”“无数据冗余”等。用户根据StorageClas的描述就可以直观得知各类存储资源的特性,就能够根据应用对存储资源的需求去申请存储资源了。github

下面对Kubermetes的PVPVCStorageClass动态资源供应等共享存储管理机制进行详细说明。web

PV 详解

PV做为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。下面的例子声明的PV具备以下属性: 5Gi 存储空间,访问模式为“ReadWriteOnce”,存储类型为“slow" (要求系统中已存在名为slow的StorageClass),回收策略为“Recycle",而且后端存储类型为“nfs”(设置了NFS Server的IP地址和路径):json

apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv1
  spec:
    capacity:
      storage: 5Gi
    accessModes:
      - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: slow nfs: path: /tmp server: 172.17.0.2 

Kubernetes支持的PV类型以下。后端

  • gcePersistentDisk: GCE公有云提供的PersistentDisk。
  • AWSElasticBlockStore: AWS公有云提供的ElasticBlockStore.
  • AzureFile: Azure公有云提供的File。
  • AzureDisk: Azure 公有云提供的Disk。
  • FC ( Fibre Channel)。
  • Flocker。
  • NFS:网络文件系统。
  • iSCSI。
  • RBD (Rados Block Device); Ceph块存储。
  • CephFS。
  • Cinder: OpenStack Cinder块存储。
  • GlusterFS。
  • VsphereVolume.
  • Quobyte Volumes。
  • VMware Photon。
  • Portworx Volumes。
  • ScaleIO Volumes。
  • HostPath:宿主机目录,仅用于单机测试。

每种存储类型都有各自的特色,在使用时须要根据它们各自的参数进行设置。centos

一、Capacity(容量)

描述存储设备具有的能力,目前仅支持对存储空间的设置( storage= =xx),将来可能加入api

二、访问模式(Access Modes)

  • ReadWriteOnce (简写为 RWO): 读写权限,而且只能被单个Node挂载。
  • ReadOnlyMany (简写为 ROX): 只读权限,容许被多个Node挂载。
  • ReadWriteMany (简写为 RWX): 读写权限,容许被多个Node挂载。

注意:即便volume支持不少种访问模式,但它同时只能使用一种访问模式。好比,GCEPersistentDisk能够被单个节点映射为ReadWriteOnce,或者多个节点映射为ReadOnlyMany,但不能同时使用这两种方式来映射。

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
FC -
FlexVolume -
Flocker - -
GCEPersistentDisk -
Glusterfs
HostPath - -
iSCSI -
PhotonPersistentDisk - -
Quobyte
NFS
RBD -
VsphereVolume - -
PortworxVolume -
ScaleIO -

三、存储类别(Class)

PV能够设定其存储的类别(Class),经过storageClassName参数指定一个StorageClass资源对象的名称。具备特定“类别”的PV只能与请求该“类别”的PVC进行绑定。未设定“类别”的PV则只能与不请求任何“类别”的PVC进行绑定。

四、回收策略(Reclaim Policy)

目前支持以下三种回收策略。

  • 保留(Retain): 保留数据,须要手工处理。
  • 回收空间( Recycle):简单清除文件的操做(例如执行m -rf /thevolume/*命令)。
  • 删除(Delete):与PV相连的后端存储完成volume的删除操做(如AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等设备的内部volume清理)。

目前,只有NFS和HostPath两种类型的存储支持“Recycle”策略; AWS EBS、GCE PD、Azure Disk和Cinder volumes支持“Delete”策略。

2. PV生命周期的各个阶段( Phase )

某个PV在生命周期中,可能处于如下4个阶段之一。

  • Available: 可用状态,还未与某个PVC绑定。
  • Bound: 已与某个PVC绑定。
  • Released: 绑定的PVC已经删除,资源已释放,但没有被集群回收。
  • Failed: 自动资源回收失败。

3. PV的挂载参数( Mount Options )

在将PV挂载到一个Node上时,根据后端存储的特色,可能须要设置额外的挂载参数,目前能够经过在PV的定义中,设置一个名为“volume.beta.kubernetes.io/mount-options"的annotation来实现。下面的例子对一个类型为gcePersistentDisk的PV设置了挂载参数“discard":

apiVersion: "v1" kind: "PersistentVolume" metadata : name: gce-disk-1 annotations: volume.beta.kubernetes.io/mount-options: "discard" spec: capacity: storage : "10Gi” accessModes : - ”ReadWriteOnce” gcePersistentDisk: fsType: "ext4" pdName : "gce-disk-1 

并不是全部类型的存储都支持设置挂载参数。从Kubernetes v1.6版本开始,如下存储类型支持设置挂载参数。

  • gcePersistentDisk。
  • AWSElasticBlockStore.
  • AzureFile。
  • AzureDisk。
  • NFS。
  • iSCSI。
  • RBD
  • (Rados Block Device): Ceph 块存储。
  • CephFS。
  • Cinder: OpenStack 块存储。
  • GlusterFS。
  • VsphereVolume.
  • Quobyte Volumes.
  • VMware Photon。

PVC详解

PVC 做为用户对存储资源的需求申请,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。下面的例子中声明的PVC具备以下属性:申请8Gi存储空间,访问模式为"ReadWriteOnce",PV选择条件为包含标签"release=stable"而且包含条件为"environment In [dev]"的标签,存储类别为"slow"(要求系统中已存在名为slow的StorageClass)。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable" matchExpressions: - {key: environment, operator: In, values: [dev]} 

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与之匹配和绑定。PVC也能够彻底不设置storageClassName字段,此时将根据系统是否启用了名为"DefaultStorageClass"的admission controller进行相应的操做。

  • 未启用DefaultStorageClass:等效于PVC设置storageClassName的值为空,即只能选择未设定Class的PV与之匹配和绑定。

  • 启用了DefaultStorageClass:要求集群管理员已定义默认的StorageClass。若是系统中不存在默认的StorageClass,则等效于不启用DefaultStorageClass的状况。若是存在默认的StorageClass,则系统将自动为PVC建立一个PV(使用默认StorageClass的后端存储),并将它们进行绑定。集群管理员设置默认StorageClass的方法为,在StorageClass的定义中加上一个annotation "storageclass.kubernetes.io/is-default-class=true"。若是管理员将多个StorageClass都定义为default,则因为不惟一,系统将没法为PVC建立相应的PV。

注意,PVC和PV都受限于namespace,PVC在选择PV时受到namespace的限制,只有相同namespace中的PV才可能与PVC绑定。Pod在引用PVC时一样受namespace的限制,只有相同namespace中的PVC才能挂载到Pod内。

当Selector和Class都进行了设置时,系统将选择两个条件同时知足的PV与之匹配。

另外,若是资源供应使用的是动态模式,即管理员没有预先定义PV,仅经过StorageClass交给系统自动完成PV的动态建立,那么PVC再设定Selector时,系统将没法为其供应任何存储资源了。

在启动动态供应模式的状况下,一旦用户删除了PVC,与之绑定的PV将根据其默认的回收策略"Delete"也会被删除。若是须要保留PV(用户数据),则在动态绑定成功后,用户须要将系统自动生成PV的回收策略从"Delete"改为"Retain"。

PV和PVC的生命周期

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

screenshot

1 资源供应(Provisioning)

k8s支持两种资源的供应模式:静态模式(Static)动态模式(Dynamic)。资源供应的结果就是建立好的PV。

  • 静态模式:集群管理员手工建立许多PV,在定义PV时须要将后端存储的特性进行设置。
  • 动态模式:集群管理员无须手工建立PV,而是经过StorageClass的设置对后端存储进行描述,标记为某种“类型(Class)”。此时要求PVC对存储类型进行声明,系统将自动完成PV的建立及与PVC的绑定。PVC能够声明Class为"",说明该PVC禁止使用动态模式。

2 资源绑定(Binding)

在用户定义好PVC以后,系统将根据PVC对存储资源的请求(存储空间和访问模式)在已存在的PV中选择一个知足PVC要求的PV,一旦找到,就将该PV与用户定义的PVC进行绑定,而后用户的应用就可使用这个PVC了。若是系统中没有知足PVC要求的PV,PVC就会无限期处于Pending状态,直到等到系统管理员建立了一个符合其要求的PV。PV一旦绑定到某个PVC上,就被这个PVC独占,不能再与其余PVC进行绑定了。在这种状况下,当PVC申请的存储空间与PV的少时,整个PV的空间都会被PVC所用,可能会形成资源的浪费。若是资源供应使用的是动态模式,则系统在为PVC找到合适的StorageClass后,将自动建立一个PV并完成与PVC的绑定。

3 资源使用(Using)

Pod使用volume的定义,将PVC挂载到容器内的某个路径进行使用。volume的类型为"persistentVolumeClaim",在后面的示例中再进行详细说明。在容器应用挂载了一个PVC后,就能被持续独占使用。不过,多个Pod能够挂载同一个PVC,应用程序须要考虑多个实例共同访问同一块存储空间的问题。

4 资源释放(Releasing)

当用户对存储资源使用完毕后,用户能够删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能马上与其余PVC进行绑定。经过以前PVC写入的数据可能还留在存储设备上,只有在清除以后该PV才能再次使用。

5 资源回收(Reclaimig)

对于PV,管理员能够设定回收策略(Reclaim Policy),用于设置与之绑定的PVC释放资源以后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。

下面经过两张图分别对在静态资源供应模式和动态资源供应模式下,PV、PVC、StorageClass及Pod使用PVC的原理进行说明。

在静态资源供应模式下,经过PV和PVC完成绑定,并供Pod使用的存储管理机制。
screenshot
在动态资源供应模式下,经过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。
screenshot

StorageClass详解

StorageClass做为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节。一方面减轻用户对于存储资源细节的关注,另外一方面也减轻管理员手工管理PV的工做,由系统自动完成PV的建立和绑定,实现动态的资源供应。使用基于StorageClass的动态资源供应模式将逐步成为云平台的标准存储配置模式。

StorageClass的定义主要包括名称、后端存储的提供者(Provisioner)和后端存储的相关参数配置。StorageClass一旦被建立出来,就将没法修改,只能删除原StorageClass的定义重建。下面的例子中定义了一个名为“standard"的StorageClass,提供者为aws-ebs,其参数设置了一个type=gp2。

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

1 StorageClass的关键配置参数

1)提供者(Provisioner)

描述存储资源的提供者,也能够看做后端存储驱动。
目前k8s支持的Provisioner都以"kubernetes.io/"为开头,用户也可使用自定义的后端存储提供者。为了符合StorageClass的用法,自定义Provisioner须要符合存储卷的开发规范,详见该连接的说明:https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/volume-provisioning.md

2)参数(Parameters)

后端存储资源提供者的参数设置,不一样的Provisioner包括不一样的参数设置。某些参数能够不显示设定,Provisioner将使用其默认值。

3)下面介绍几种经常使用的Provisioner对StorageClass的定义进行详细说明

AWS EBS存储卷
kind: StorageClass
apiVersion: storage.k8s.io/v1 metadata: name: slow provisioner: kubernetes.io/aws-ebs parameters: type: io1 zone: us-east-id iopsPerGB: "10" 

参数说明以下:

  • type:可选项为io1, gp2, sc1, st1, 默认值为gp2
  • zone:AWS zone的名称
  • iopsPerGB:仅用于io1类型的volume,意为每秒每GiB的I/O操做数量
  • encrypted:是否加密
  • kmsKeyId:加密时的Amazon Resource Name
GCE PD存储卷
kind: StorageClass
apiVersion: storage.k8s.io/v1 metadata: name: slow provisioner: kubernetes.io/gce-pd parameters: type: pd-standard zone: us-centrall-a 

参数说明:

  • type:可选项为pd-standard, pd-ssd, 默认值为pd-standard
  • zone:GCE zone名称
GlusterFS存储卷
kind: StorageClass
apiVersion: storage.k8s.io/v1 metadata: name: slow provisioner: kubernetes.io/glusterfs parameters: resturl: "https://127.0.0.1:8081" clusterid: "sadfa2435hfghsrg462345" restauthenabled: "true" restuser: "admin" secretNamespace: "default" secretName: "heketi-secret" gidMin: "40000" gidMax: "50000" volumetype: "replicate:3" 

参数说明以下(详细说明请参考GlusterFS和Heketi的文档)。

  • resturl: Gluster REST服务(heketi)的URL地址,用于自动完成GlusterFSvolume的设置。
  • restauthenabled: 是否对Gluster REST服务启用安全机制。
  • restuser: 访问Gluster REST服务的用户名。
  • secretNamespace和secretName: 保存访问Gluster REST服务密码的Secret资源对象名。
  • clusterid: GlusterFS的Cluster ID
  • gidMin和gidMAX: StorageClass的GID范围,用于动态资源供应时为PV设置的GID。
  • volumetype: GlusterFS的volume类型设置,例如replicate:3(Replicate类型,3副本);disperse:4:2(Disperse类型,数据4份,冗余2份);none(Distribute类型)。
OpenStack Cinder存储卷
kind: StorageClass
apiVersion: storage.k8s.io/v1 metadata: name: gold provisioner: kubernetes.io/cinder parameters: type: fast availability: nova 

参数说明以下:

  • type: Cinder的VolumeType, 默认值为空。
  • availability: Availability Zone, 默认值为空。

其余Provisioner的StorageClass相关参数设置请参考它们各自的配置手册。

2 设置默认的StorageClass

要在系统中设置一个默认的StorageClass,首先须要启动名为"DefaultStorageClass"admission controller, 即在kube-apiserver的命令行参数--admission-controll中增长:
--admission-control=...,DefaultStorageClass

而后,在StorageClass的定义中设置一个annotation:

kind: StorageClass
apiVersion: storage.k8s.io/v1 metadata: name: gold annotations: storageclass.beta.kubernetes.io/is-default-class="true" provisioner: kubernetes.io/cinder parameters: type: fast availability: nova 

经过kubectl create命令建立成功后,查看StorageClass列表,能够看到名为gold的StorageClass被标记为"default":

# kubectl get sc gold (default) kubernetes.io/cinder 

动态存储管理实战:GlusterFS

本节以GlusterFS为例,从定义StorageClass、建立GlusterFS和Heketi服务、用户申请PVC到建立Pod使用存储资源,对StorageClass和动态资源分配进行详细说明,进一步剖析k8s的存储机制。

1 准备工做

首先在用于部署GlusterFS的三个节点上安装GlusterFS客户端:

yum -y install glusterfs glusterfs-fuse 

GlusterFS管理服务容器须要以特权模式运行,中kube-apiserver的启动参数中确认已经打开了:

--allow-privileged=true 

给要部署GlusterFS管理服务的节点打上"storagenode=glusterfs"的标签,这样能够将GlusterFS容器定向部署到安装了GlusterFS的Node上:

[k8s@kube-server harbor]$ kubectl label node kube-node1 storagenode=glusterfs node "kube-node1" labeled [k8s@kube-server harbor]$ kubectl label node kube-node2 storagenode=glusterfs node "kube-node2" labeled [k8s@kube-server harbor]$ kubectl label node kube-node3 storagenode=glusterfs node "kube-node3" labeled 

2 建立GlusterFS服务容器集群

GlusterFS服务容器以DaemonSet的方式进行部署,确保每台Node上都运行一个GlusterFS管理服务,glusterfs-daemonset.yaml内容以下。参照 https://github.com/gluster/gluster-kubernetes。

1)在各个Node节点的启动参数中增长如下选项,由于GlusterFS须要使用容器的特权模式运行

--allow-privileged 

生效:

systemctl daemon-reload systemctl restart kubelet systemctl status kubelet 

2)给每一个运行GlusterFS的Node节点增长一块数据磁盘

注意数据盘挂载后,在系统中使用的设备描述符,须要在下一步配置中使用到。

3)编辑topology.json拓朴文件

获取一份安装资源:

git clone https://github.com/gluster/gluster-kubernetes.git

[k8s@kube-server deploy]$ pwd /home/k8s/gluster-kubernetes/deploy [k8s@kube-server deploy]$ ls gk-deploy heketi.json.template kube-templates ocp-templates topology.json 

至少须要3个节点,按下面格式对该文件进行更新:

[k8s@kube-server deploy]$ cat topology.json { "clusters": [ { "nodes": [ { "node": { "hostnames": { "manage": [ "kube-node1" ], "storage": [ "172.16.10.101" ] }, "zone": 1 }, "devices": [ "/dev/sdb" ] }, { "node": { "hostnames": { "manage": [ "kube-node2" ], "storage": [ "172.16.10.102" ] }, "zone": 1 }, "devices": [ "/dev/sdb" ] }, { "node": { "hostnames": { "manage": [ "kube-node3" ], "storage": [ "172.16.10.103" ] }, "zone": 1 }, "devices": [ "/dev/sdb" ] } ] } ] } 

4)在k8s上部署 GlusterFS + heketi

须要先检查下环境:

  • 至少须要3个节点
  • 每一个节点上至少提供一个裸块存储设备;
  • 确保如下端口没有被占用:2222,24007, 24008, 49152~49251
  • 在系统中加载如下模块: modprobe dm_snapshot && modprobe dm_mirror && modprobe dm_thin_pool
  • 安装依赖包:yum -y install glusterfs-fuse

执行部署命令:

[k8s@kube-server deploy]$ ./gk-deploy -g 

注:-g参数表示要建立出一套glusterfs集群服务。

若是一切顺利,在结束时会看到下面的输出:

....
service "heketi" created
deployment.extensions "heketi" created
Waiting for heketi pod to start ... OK Flag --show-all has been deprecated, will be removed in an upcoming release heketi is now running and accessible via https://172.30.86.3:8080 . To run administrative commands you can install 'heketi-cli' and use it as follows: # heketi-cli -s https://172.30.86.3:8080 --user admin --secret '<ADMIN_KEY>' cluster list You can find it at https://github.com/heketi/heketi/releases . Alternatively, use it from within the heketi pod: # /opt/k8s/bin/kubectl -n default exec -i heketi-75dcfb7d44-vj9bk -- heketi-cli -s https://localhost:8080 --user admin --secret '<ADMIN_KEY>' cluster list For dynamic provisioning, create a StorageClass similar to this: --- apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: glusterfs-storage provisioner: kubernetes.io/glusterfs parameters: resturl: "https://172.30.86.3:8080" Deployment complete! 

查看下都建立出了哪些服务实例:

[k8s@kube-server deploy]$ kubectl get pods -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP NODE glusterfs-88469 1/1 Running 0 2h 172.16.10.102 kube-node2 glusterfs-lwm4n 1/1 Running 0 2h 172.16.10.103 kube-node3 glusterfs-pfgwb 1/1 Running 0 2h 172.16.10.101 kube-node1 heketi-75dcfb7d44-vj9bk 1/1 Running 0 1h 172.30.86.3 kube-node2 my-nginx-86555897f9-2kn92 1/1 Running 2 8h 172.30.49.2 kube-node1 my-nginx-86555897f9-d95t9 1/1 Running 4 2d 172.30.48.2 kube-node3 [k8s@kube-server deploy]$ kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR heketi ClusterIP 10.254.42.129 <none> 8080/TCP 1h glusterfs=heketi-pod heketi-storage-endpoints ClusterIP 10.254.4.122 <none> 1/TCP 1h <none> kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 7d <none> my-nginx ClusterIP 10.254.191.237 <none> 80/TCP 5d run=my-nginx [k8s@kube-server deploy]$ kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE heketi 1 1 1 1 1h my-nginx 2 2 2 2 5d [k8s@kube-server deploy]$ kubectl get secret NAME TYPE DATA AGE default-token-p5wjd kubernetes.io/service-account-token 3 7d heketi-config-secret Opaque 3 1h heketi-service-account-token-mrtsx kubernetes.io/service-account-token 3 2h kubelet-api-test-token-gdj7g kubernetes.io/service-account-token 3 6d [k8s@kube-server deploy]$ 

5)使用示例

在能够调用kubectl管理k8s集群的节点上,安装一个heketi客户端:

yum -y install heketi-client 

建立个1GB的PV存储卷:

[k8s@kube-server deploy]$ export HEKETI_CLI_SERVER=https://172.30.86.3:8080 [k8s@kube-server deploy]$ heketi-cli volume create --size=1 --persistent-volume --persistent-volume-endpoint=heketi-storage-endpoints | kubectl create -f - persistentvolume "glusterfs-900fb349" created 

回到Dashboard上看看这个刚建立的存储卷:
screenshot
经过heketi服务查看和管理GlusterFS集群: 查看集群列表:

[root@kube-node1 ~]# curl 10.254.42.129:8080/clusters {"clusters":["ada54ffbeac15a5c9a7521e0c7d2f636"]} 

查看集群详情:

[root@kube-node1 ~]# curl 10.254.42.129:8080/clusters/ada54ffbeac15a5c9a7521e0c7d2f636 {"id":"ada54ffbeac15a5c9a7521e0c7d2f636","nodes":["49ac6f56ef21408bcad7c7613cd40bd8","bdf51ae46025cd4fcf134f7be36c32de","fc21262379ec3636e3eadcae15efcc94"],"volumes":["42b01b9b08af23b751b2359fb161c004","900fb349e56af275f47d523d08fdfd6e"],"block":true,"file":true,"blockvolumes":[]} 

查看节点详情:

[root@kube-node1 ~]# curl 10.254.42.129:8080/nodes/49ac6f56ef21408bcad7c7613cd40bd8 {"zone":1,"hostnames":{"manage":["kube-node3"],"storage":["172.16.10.103"]},"cluster":"ada54ffbeac15a5c9a7521e0c7d2f636","id":"49ac6f56ef21408bcad7c7613cd40bd8","state":"online","devices":[{"name":"/dev/sdb","storage":{"total":8253440,"free":5087232,"used":3166208},"id":"2f6b2f6c289a2f6bf48fbec59c0c2009","state":"online","bricks":[{"id":"2ea90ebd791a4230e927d233d1c8a7d1","path":"/var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_2ea90ebd791a4230e927d233d1c8a7d1/brick","device":"2f6b2f6c289a2f6bf48fbec59c0c2009","node":"49ac6f56ef21408bcad7c7613cd40bd8","volume":"42b01b9b08af23b751b2359fb161c004","size":2097152},{"id":"4c98684d878ffe7dbfc1008336460eed","path":"/var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_4c98684d878ffe7dbfc1008336460eed/brick","device":"2f6b2f6c289a2f6bf48fbec59c0c2009","node":"49ac6f56ef21408bcad7c7613cd40bd8","volume":"900fb349e56af275f47d523d08fdfd6e","size":1048576}]}]} 
  • state 为 online说明节点正常

6)建立一个使用GlusterFS动态存储供应服务的nginx应用

注:在本示例中的用户认证是未启用的,若是要启动用户认证服务,则能够建立一个secret,而后经过StorageClass配置参数传递给Gluster动态存储供应服务。
下面是一个存储类的示例,它将请求2GB的按需存储,用于在咱们的HelloWorld应用程序中使用。

gluster-storage-class.yaml apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: gluster-heketi provisioner: kubernetes.io/glusterfs parameters: resturl: "https://10.254.42.129:8080" restuser: "joe" restuserkey: "My Secret Life" 
  • name,StorageClass名称
  • provisioner,存储服务提供者
  • resturl,Heketi REST Url
  • restuser,由于未启用认证,因此这个参数无效
  • restuserkey,同上

建立该存储类:

[k8s@kube-server ~]$ kubectl create -f gluster-storage-class.yaml storageclass.storage.k8s.io "gluster-heketi" created [k8s@kube-server ~]$ kubectl get storageclass NAME PROVISIONER AGE gluster-heketi kubernetes.io/glusterfs 43s 

建立PersistentVolumeClaim(PVC)以请求咱们的HelloWorld应用程序的存储:
咱们将建立一个要求2GB存储空间的PVC,此时,Kubernetes Dynamic Provisioning Framework和Heketi将自动配置新的GlusterFS卷并生成Kubernetes PersistentVolume(PV)对象。

gluster-pvc.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: gluster1 annotations: volume.beta.kubernetes.io/storage-class: gluster-heketi spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi 
  • annotations,Kubernetes存储类注释和存储类的名称

    [k8s@kube-server ~]$ kubectl create -f gluster-pvc.yaml
    persistentvolumeclaim "gluster1" created

能够看到PVC是绑定到一个动态供给的存储卷上的:

[k8s@kube-server ~]$ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE gluster1 Bound pvc-53e824cf-7eb7-11e8-bf5c-080027395360 2Gi RWO gluster-heketi 53s [k8s@kube-server ~]$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE glusterfs-900fb349 1Gi RWX Retain Available 2h pvc-53e824cf-7eb7-11e8-bf5c-080027395360 2Gi RWO Delete Bound default/gluster1 gluster-heketi 1m 

建立一个使用该PVC的nginx实例:

nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod1
  labels:
    name: nginx-pod1
spec:
  containers:
  - name: nginx-pod1 image: nginx:1.7.9 ports: - name: web containerPort: 80 volumeMounts: - name: gluster-vol1 mountPath: /usr/share/nginx/html volumes: - name: gluster-vol1 persistentVolumeClaim: claimName: gluster1 

claimName,要使用的PVC的名称

[k8s@kube-server ~]$ kubectl create -f nginx-pod.yaml pod "nginx-pod1" created [k8s@kube-server ~]$ kubectl get pods -o wide|grep nginx-pod nginx-pod1 1/1 Running 0 33s 172.30.86.3 kube-node2 

登陆到该Pod中并建立一个网页文件:

[k8s@kube-server ~]$ kubectl exec -it nginx-pod1 /bin/bash root@nginx-pod1:/# df -h Filesystem Size Used Avail Use% Mounted on rootfs 41G 7.1G 34G 18% / overlay 41G 7.1G 34G 18% / tmpfs 64M 0 64M 0% /dev tmpfs 496M 0 496M 0% /sys/fs/cgroup /dev/mapper/centos_bogon-root 41G 7.1G 34G 18% /dev/termination-log shm 64M 0 64M 0% /dev/shm /dev/mapper/centos_bogon-root 41G 7.1G 34G 18% /etc/resolv.conf /dev/mapper/centos_bogon-root 41G 7.1G 34G 18% /etc/hostname /dev/mapper/centos_bogon-root 41G 7.1G 34G 18% /etc/hosts /dev/mapper/centos_bogon-root 41G 7.1G 34G 18% /var/cache/nginx 172.16.10.101:vol_1b6e32efd9b6f07e2b056bed2ce6cc73 2.0G 53M 2.0G 3% /usr/share/nginx/html tmpfs 496M 12K 496M 1% /run/secrets/kubernetes.io/serviceaccount tmpfs 64M 0 64M 0% /proc/kcore tmpfs 64M 0 64M 0% /proc/keys tmpfs 64M 0 64M 0% /proc/timer_list tmpfs 64M 0 64M 0% /proc/timer_stats tmpfs 64M 0 64M 0% /proc/sched_debug tmpfs 496M 0 496M 0% /proc/scsi tmpfs 496M 0 496M 0% /sys/firmware root@nginx-pod1:/# cd /usr/share/nginx/html dex.htmlnx-pod1:/usr/share/nginx/html# echo 'Hello World from GlusterFS!!!' > in root@nginx-pod1:/usr/share/nginx/html# ls index.html root@nginx-pod1:/usr/share/nginx/html# cat index.html Hello World from GlusterFS!!! root@nginx-pod1:/usr/share/nginx/html# exit exit 

访问一下咱们的网页:

[k8s@kube-server ~]$ curl https://172.30.86.3 Hello World from GlusterFS!!! 

再检查一下gluster pod,找到咱们刚写入的index.html文件,登陆任一个gluster pod:

screenshot

[root@kube-node1 brick]# pwd /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_6e016b1ed8e16b6a28839f1670a56d00/brick [root@kube-node1 brick]# ls index.html [root@kube-node1 brick]# cat index.html Hello World from GlusterFS!!! 

7)删除 glusterfs 集群配置

curl -X DELETE 10.254.42.129:8080/devices/46b2685901f56d6fe0cc85bf3d37bf75 # 使用的是device id,删除device curl -X DELETE 10.254.42.129:8080/nodes/8bd8497a8dcda0708508228f4ae8c2ae #使用的是node id, 删除node 

每一个节点都要删除掉device才能再删除node

cluster 列表下的全部节点都删除后 才能删除cluster: curl -X DELETE 10.254.42.129:8080/clusters/ada54ffbeac15a5c9a7521e0c7d2f636 

Heketi服务

GlusterFS 是个开源的分布式文件系统,而 Heketi 在其上提供了 REST 形式的 API,两者协同为 Kubernetes 提供了存储卷的自动供给能力。Heketi还支持GlusterFS多集群管理。当一个集群中同时有多种规格、性能和容量特色的存储资源时,Heketi能够经过接入多个存储集群,在每一个集群中又按资源特性划分出多个Zone来进行管理。

screenshot

1)在k8s中部署Heketi服务前须要为其建立一个ServiceAccount帐号

咱们继续看一下前面例子中使用到的一些配置资源:

[k8s@kube-server deploy]$ pwd /home/k8s/gluster-kubernetes/deploy [k8s@kube-server deploy]$ cat ./kube-templates/heketi-service-account.yaml apiVersion: v1 kind: ServiceAccount metadata: name: heketi-service-account labels: glusterfs: heketi-sa heketi: sa [k8s@kube-server deploy]$ kubectl get sa | grep heketi heketi-service-account 1 4h 

2)经过Deployment部署Heketi服务

[k8s@kube-server deploy]$ kubectl get deployment
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
heketi     1         1         1            1           3h

[k8s@kube-server kube-templates]$ pwd
/home/k8s/gluster-kubernetes/deploy/kube-templates [k8s@kube-server kube-templates]$ cat heketi-deployment.yaml --- kind: Service apiVersion: v1 metadata: name: heketi labels: glusterfs: heketi-service heketi: service annotations: description: Exposes Heketi Service spec: selector: glusterfs: heketi-pod ports: - name: heketi port: 8080 targetPort: 8080 --- kind: Deployment apiVersion: extensions/v1beta1 metadata: name: heketi labels: glusterfs: heketi-deployment heketi: deployment annotations: description: Defines how to deploy Heketi spec: replicas: 1 template: metadata: name: heketi labels: glusterfs: heketi-pod heketi: pod spec: serviceAccountName: heketi-service-account containers: - image: heketi/heketi:dev imagePullPolicy: IfNotPresent name: heketi env: - name: HEKETI_USER_KEY value: ${HEKETI_USER_KEY} - name: HEKETI_ADMIN_KEY value: ${HEKETI_ADMIN_KEY} - name: HEKETI_EXECUTOR value: ${HEKETI_EXECUTOR} - name: HEKETI_FSTAB value: ${HEKETI_FSTAB} - name: HEKETI_SNAPSHOT_LIMIT value: '14' - name: HEKETI_KUBE_GLUSTER_DAEMONSET value: "y" - name: HEKETI_IGNORE_STALE_OPERATIONS value: "true" ports: - containerPort: 8080 volumeMounts: - name: db mountPath: "/var/lib/heketi" - name: config mountPath: /etc/heketi readinessProbe: timeoutSeconds: 3 initialDelaySeconds: 3 httpGet: path: "/hello" port: 8080 livenessProbe: timeoutSeconds: 3 initialDelaySeconds: 30 httpGet: path: "/hello" port: 8080 volumes: - name: db glusterfs: endpoints: heketi-storage-endpoints path: heketidbstorage - name: config secret: secretName: heketi-config-secret 

定义了两个volumes,其中"db"是使用的glusterfs提供的存储卷,而"config"的volume则是使用的"secret"

查看一下被看成volume使用的secret的内容:

[k8s@kube-server kube-templates]$ kubectl describe secret heketi-config-secret
Name:         heketi-config-secret
Namespace:    default
Labels:       glusterfs=heketi-config-secret
 heketi=config-secret Annotations: <none> Type: Opaque Data ==== heketi.json: 909 bytes private_key: 0 bytes topology.json: 1029 bytes 

能够看到这个secret中是包含了三个配置文件

这个secret是使用脚本建立的:

[k8s@kube-server deploy]$ pwd /home/k8s/gluster-kubernetes/deploy [k8s@kube-server deploy]$ ls gk-deploy heketi.json.template kube-templates ocp-templates topology.json [k8s@kube-server deploy]$ grep heketi-config-secret gk-deploy eval_output "${CLI} create secret generic heketi-config-secret --from-file=private_key=${SSH_KEYFILE} --from-file=./heketi.json --from-file=topology.json=${TOPOLOGY}" eval_output "${CLI} label --overwrite secret heketi-config-secret glusterfs=heketi-config-secret heketi=config-secret" [k8s@kube-server deploy]$ 

3)为Heketi设置GlusterFS集群

使用一个topology.json的配置文件来完成各个GlusterFS节点和设备的定义。
Heketi要求一个GlusterFS集群中至少有3个节点。在topology.json配置文件的hostnames字段中的manage中填写主机名,在storage上填写IP地址,devices要求是未建立文件系统的裸设备(支持多块磁盘)。这样Heketi就能够自动完成PV、VG和LV的建立。

topology.json文件的内容在前面已经提供了,再也不重复。

当使用gluster-kubernetes项目提供的脚本工具和模板建立glusterfs和heketi服务时,不须要额外的手动干预。但若是是手动建立这些服务,则能够按下面的方法使用Heketi加载topology配置,完成GlusterFS服务集群的建立。
能够登陆进入Hekiti容器执行如下命令:

# export HEKETI_CLI_SERVER=https://localhost:8080 # heketi-cli topology load --json=topology.json 

这样Heketi就完成了GlusterFS集群的建立,同时在GlusterFS集群的各个节点的/dev/sdb盘上成功建立了PV和VG。
此时,查看Heketi的topology信息,能够看到很详细的Node和Device信息:

[k8s@kube-server deploy]$ heketi-cli topology info

Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636

    Volumes:

    Name: vol_1b6e32efd9b6f07e2b056bed2ce6cc73
    Size: 2
    Id: 1b6e32efd9b6f07e2b056bed2ce6cc73
    Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636
    Mount: 172.16.10.103:vol_1b6e32efd9b6f07e2b056bed2ce6cc73
    Mount Options: backup-volfile-servers=172.16.10.102,172.16.10.101 Durability Type: replicate Replica: 3 Snapshot: Disabled Bricks: Id: 6e016b1ed8e16b6a28839f1670a56d00 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_6e016b1ed8e16b6a28839f1670a56d00/brick Size (GiB): 2 Node: fc21262379ec3636e3eadcae15efcc94 Device: f8776d0d92102fc3e272f2ec899e5f18 Id: c7acfcecaf85aada98b0c0208798440f Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_c7acfcecaf85aada98b0c0208798440f/brick Size (GiB): 2 Node: 49ac6f56ef21408bcad7c7613cd40bd8 Device: 2f6b2f6c289a2f6bf48fbec59c0c2009 Id: e2acc8268f17f05e940ebe679dacfa3a Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_e2acc8268f17f05e940ebe679dacfa3a/brick Size (GiB): 2 Node: bdf51ae46025cd4fcf134f7be36c32de Device: e100120226d5d9567ed0f92b9810236c Name: heketidbstorage Size: 2 Id: 42b01b9b08af23b751b2359fb161c004 Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636 Mount: 172.16.10.103:heketidbstorage Mount Options: backup-volfile-servers=172.16.10.102,172.16.10.101 Durability Type: replicate Replica: 3 Snapshot: Disabled Bricks: Id: 2ea90ebd791a4230e927d233d1c8a7d1 Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_2ea90ebd791a4230e927d233d1c8a7d1/brick Size (GiB): 2 Node: 49ac6f56ef21408bcad7c7613cd40bd8 Device: 2f6b2f6c289a2f6bf48fbec59c0c2009 Id: 9dc7238db3240146f12189dd28320227 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_9dc7238db3240146f12189dd28320227/brick Size (GiB): 2 Node: fc21262379ec3636e3eadcae15efcc94 Device: f8776d0d92102fc3e272f2ec899e5f18 Id: cb68cbf4e992abffc86ab3b5db58ef56 Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_cb68cbf4e992abffc86ab3b5db58ef56/brick Size (GiB): 2 Node: bdf51ae46025cd4fcf134f7be36c32de Device: e100120226d5d9567ed0f92b9810236c Name: vol_900fb349e56af275f47d523d08fdfd6e Size: 1 Id: 900fb349e56af275f47d523d08fdfd6e Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636 Mount: 172.16.10.103:vol_900fb349e56af275f47d523d08fdfd6e Mount Options: backup-volfile-servers=172.16.10.102,172.16.10.101 Durability Type: replicate Replica: 3 Snapshot: Disabled Bricks: Id: 4c98684d878ffe7dbfc1008336460eed Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_4c98684d878ffe7dbfc1008336460eed/brick Size (GiB): 1 Node: 49ac6f56ef21408bcad7c7613cd40bd8 Device: 2f6b2f6c289a2f6bf48fbec59c0c2009 Id: 7c82d03c88d73bb18d407a791a1053c2 Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_7c82d03c88d73bb18d407a791a1053c2/brick Size (GiB): 1 Node: bdf51ae46025cd4fcf134f7be36c32de Device: e100120226d5d9567ed0f92b9810236c Id: 822266f7ad3cf62b1e45686265cf7268 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_822266f7ad3cf62b1e45686265cf7268/brick Size (GiB): 1 Node: fc21262379ec3636e3eadcae15efcc94 Device: f8776d0d92102fc3e272f2ec899e5f18 Nodes: Node Id: 49ac6f56ef21408bcad7c7613cd40bd8 State: online Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636 Zone: 1 Management Hostname: kube-node3 Storage Hostname: 172.16.10.103 Devices: Id:2f6b2f6c289a2f6bf48fbec59c0c2009 Name:/dev/sdb State:online Size (GiB):7 Used (GiB):5 Free (GiB):2 Bricks: Id:2ea90ebd791a4230e927d233d1c8a7d1 Size (GiB):2 Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_2ea90ebd791a4230e927d233d1c8a7d1/brick Id:4c98684d878ffe7dbfc1008336460eed Size (GiB):1 Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_4c98684d878ffe7dbfc1008336460eed/brick Id:c7acfcecaf85aada98b0c0208798440f Size (GiB):2 Path: /var/lib/heketi/mounts/vg_2f6b2f6c289a2f6bf48fbec59c0c2009/brick_c7acfcecaf85aada98b0c0208798440f/brick Node Id: bdf51ae46025cd4fcf134f7be36c32de State: online Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636 Zone: 1 Management Hostname: kube-node2 Storage Hostname: 172.16.10.102 Devices: Id:e100120226d5d9567ed0f92b9810236c Name:/dev/sdb State:online Size (GiB):7 Used (GiB):5 Free (GiB):2 Bricks: Id:7c82d03c88d73bb18d407a791a1053c2 Size (GiB):1 Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_7c82d03c88d73bb18d407a791a1053c2/brick Id:cb68cbf4e992abffc86ab3b5db58ef56 Size (GiB):2 Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_cb68cbf4e992abffc86ab3b5db58ef56/brick Id:e2acc8268f17f05e940ebe679dacfa3a Size (GiB):2 Path: /var/lib/heketi/mounts/vg_e100120226d5d9567ed0f92b9810236c/brick_e2acc8268f17f05e940ebe679dacfa3a/brick Node Id: fc21262379ec3636e3eadcae15efcc94 State: online Cluster Id: ada54ffbeac15a5c9a7521e0c7d2f636 Zone: 1 Management Hostname: kube-node1 Storage Hostname: 172.16.10.101 Devices: Id:f8776d0d92102fc3e272f2ec899e5f18 Name:/dev/sdb State:online Size (GiB):7 Used (GiB):5 Free (GiB):2 Bricks: Id:6e016b1ed8e16b6a28839f1670a56d00 Size (GiB):2 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_6e016b1ed8e16b6a28839f1670a56d00/brick Id:822266f7ad3cf62b1e45686265cf7268 Size (GiB):1 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_822266f7ad3cf62b1e45686265cf7268/brick Id:9dc7238db3240146f12189dd28320227 Size (GiB):2 Path: /var/lib/heketi/mounts/vg_f8776d0d92102fc3e272f2ec899e5f18/brick_9dc7238db3240146f12189dd28320227/brick 

总结:使用Kubernetes的动态存储供应模式,配置StorageClass和Heketi共同搭建基于GlusterFS的共享存储,相对于静态模式至少有两大优点。

  1. 一个是管理员无须预先建立大量的PV做为存储资源
  2. 用户在申请PVC时也没法保证容量与预置PV的容量可以一致。所以,从k8s v1.6开始,建议用户优先考虑使用StorageClass的动态存储供应模式进行存储管理。
连接:https://www.orchome.com/1284 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
相关文章
相关标签/搜索