大家好,我是腾意。
应用程序在处理请求时,可根据其对当前请求的处理是否受影响于此前的请求,将应用划分为有状态应用和无状态应用两种。微服务体系中,各种应用均被拆分成了众多微服务或更小的应用模块,因此往往会存在为数不少的有状态应用,当然,也会存在数量可观的无状态应用。而对于有状态应用来说,数据持久化几乎是必然之需。
Kubernetes提供的存储卷(Volume)属于Pod资源级别,共享于Pod内的所有容器,可用于在容器的文件系统之外存储应用程序的相关数据,甚至还可独立于Pod的生命周期之外实现数据持久化。本篇主要介绍Kubernetes系统之上的主流存储卷类型及其应用。
1.1 存储卷概述
Pod本身具有生命周期,故其内部运行的容器及其相关数据自身均无法持久存在。Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应地,Kubernetes也支持类似的存储卷功能,不过,其存储卷是与Pod资源绑定而非容器。简单来说,存储卷是定义在Pod资源之上、可被其内部的所有容器挂载的共享目录,它关联至某外部的存储设备之上的存储空间,从而独立于容器自身的文件系统,而数据是否具有持久能力则取决于存储卷自身是否支持持久机制。Pod、容器与存储卷的关系如图7-1所示。
图7-1 Pod、容器与存储卷
1.1.1 Kubernetes支持的存储卷类型
Kubernetes支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,甚至还支持Secret和ConfigMap这样的特殊存储资源。对于Pod来说,卷类型主要是为关联相关的存储系统时提供相关的配置参数,例如,关联节点本地的存储目录与关联GlusterFS存储系统所需要的配置参数差异巨大,因此指定存储卷类型时也就限定了其关联到的后端存储设备。目前,Kubernetes支持的存储卷包含以下这些类型。
·emptyDir
·hostPath
·nfs
·fc
·iscsi
·flocker
·Glusterfs
·rbd
·cephfs
·cinder
·awsElasticBlockStore
·gcePersistentDisk
·azureDisk
·azureFile
·gitRepo
·downwardAPI
·ConfigMap
·secret
·projected
·name
·persistentVolumeClaim
·downwardAPI
·vsphereVolume
·quobyte
·portworxVolume
·photonPersistentDisk
·scaleIO
·flexVolume
·storageOS
·local
上述类型中,emptyDir与hostPath属于节点级别的卷类型,emptyDir的生命周期与Pod资源相同,而使用了hostPath卷的Pod一旦被重新调度至其他节点,那么它将无法再使用此前的数据。因此,这两种类型都不具有持久性。要想使用持久类型的存储卷,就得使用网络存储系统,如NFS、Ceph、GlusterFS等,或者云端存储,如gcePersistentDisk、awsElasticBlockStore等。
然而,网络存储系统通常都不太容易使用,有的甚至很复杂,以至于对大多数用户来说它是一个难以逾越的障碍。Kubernetes为此专门设计了一种集群级别的资源PersistentVolume(简称PV),它借由管理员配置存储系统,而后由用户通过“persistentVolumeClaim”(简称PVC)存储卷直接申请使用的机制大大简化了终端存储用户的配置过程,有效降低了使用难度。
再者,Secret和ConfigMap算得上是两种特殊的卷类型。
1)Secret用于向Pod传递敏感信息,如密码、私钥、证书文件等,这些信息如果直接定义在镜像中很容易导致泄露,有了Secret资源,用户可以将这些信息存储于集群中而后由Pod进行挂载,从而实现将敏感数据与系统解耦。
2)ConfigMap资源则用于向Pod注入非敏感数据,使用时,用户将数据直接存储于ConfigMap对象中,而后直接在Pod中使用ConfigMap卷引用它即可,它可以帮助实现容器配置文件集中化定义和管理。
另外,Kubernetes自1.9版本起对存储的支持做了进一步的增强,引入了容器存储接口(Container Storage Interface,CSI)的一套alpha实现版本,其能够将插件的安装流程简化至与创建Pod相当,并允许第三方存储供应商在无须修改Kubernetes代码库的前提下提供自己的解决方案。因此,Kubernetes系统支持的卷存储机制必将进一步增强。
1.1.2 存储卷的使用方式
在Pod中定义使用存储卷的配置由两部分组成:一是通过.spec.volumes字段定义在Pod之上的存储卷列表,其支持使用多种不同类型的存储卷且配置参数差别很大;另一个是通过.spec.containers.volumeMounts字段在容器上定义的存储卷挂载列表,它只能挂载当前Pod资源中定义的具体存储卷,当然,也可以不挂载任何存储卷。如图7-1所示。
在Pod级别定义存储卷时,.spec.volumes字段的值是对象列表格式,每个对象为一个存储卷的定义,它由存储卷名称(.spec.volumes.name
spec: … volumes: - name: logdata emptyDir: {} - name: example gitRepo: repository: https://github.com/iKubernetes/k8s_book.git revision: master directory: .
定义好的存储卷可由当前Pod资源内的各容器进行挂载。事实上,也只有多个容器挂载同一个存储卷时,“共享”才有了具体的意义。当Pod中只有一个容器时,使用存储卷的目的通常在于数据持久化。.spec.containers.volumeMounts字段的值也是对象列表格式,由一到多个存储卷挂载定义组成。无论何种类型的存储卷,它们的挂载格式基本上都是相同的,下面的代码段是在容器中定义挂载卷时的通用语法形式:
spec: … containers: - name: <String> … volumeMounts: - name <string> -required- mountPath <string> -required- readOnly <boolean> subPath <string> mountPropagation <string>
其中各字段的意义及使用要求具体如下。
·name
·mountPath
·readOnly
·subPath
下面是一个挂载示例,容器myapp将logdata存储卷挂载于/var/log/myapp,将example挂载到/webdata/example目录:
spec: containers: - name: myapp image: ikubernetes/myapp:v7 volumeMounts: - name: logdata mountPath: /var/log/myapp/ - name: example mountPath: /webdata/example/
存储卷的定义基本相似,因此本篇后面的篇幅将重点放在介绍主流存储卷类型的配置及使用方式,而且出于保持示例简洁及节约篇幅的目的,本篇的示例主要以自主式Pod资源为主。
1.2 临时存储卷
Kubernetes支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过,基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从相应的Git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。
1.2.1 emptyDir存储卷
emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的“docker挂载卷”,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除。不具有持久能力的emptyDir存储卷只能用于某些特殊场景中,例如,同一Pod内的多个容器间文件的共享,或者作为容器数据的临时存储目录用于数据缓存系统等。
emptyDir存储卷则定义于.spec.volumes.emptyDir嵌套字段中,可用字段主要包含两个,具体如下。
·medium:此目录所在的存储介质的类型,可取值为“default”或“Memory”,默认为default,表示使用节点的默认存储介质;“Memory”表示使用基于RAM的临时文件系统tmpfs,空间受限于内存,但性能非常好,通常用于为容器中的应用提供缓存空间。
·sizeLimit:当前存储卷的空间限额,默认值为nil,表示不限制;不过,在medium字段值为“Memory”时建议务必定义此限额。
下面是一个使用了emptyDir存储卷的简单示例,它保存于vol-emptydir.yaml配置文件中:
apiVersion: v1 kind: Pod metadata: name: vol-emptydir-pod spec: volumes: - name: html emptyDir: {} containers: - name: nginx image: nginx:1.12-alpine volumeMounts: - name: html mountPath: /usr/share/nginx/html - name: pagegen image: alpine volumeMounts: - name: html mountPath: /html command: ["/bin/sh", "-c"] args: - while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done
上面的示例中定义的存储卷名称为html,挂载于容器nginx的/usr/share/nginx/html目录,以及容器pagegen的/html目录。容器pagegen每隔10秒向存储卷上的index.html文件中追加一行信息,而容器nginx中的nginx进程则以其为站点主页,如图7-2所示。
图7-2 存储卷使用示意图
Pod资源的详细信息中会显示存储卷的相关状态,包括其是否创建成功(在Events字段中输出)、相关的类型及参数(在Volumes字段中输出)以及容器中的挂载状态等信息(在Containers字段中输出)。如下面的命令结果所示:
~]$ kubectl describe pods vol-emptydir-pod Name: vol-emptydir-pod …… Containers: nginx: …… Mounts: /usr/share/nginx/html from html (rw) pagegen: …… Mounts: /html from html (rw) …… Volumes: html: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: …… Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 10s default-scheduler Successfully assigned vol-emptydir to node03.ilinux.io Normal SuccessfulMountVolume 10s kubelet, node03.ilinux.io MountVolume. SetUp succeeded for volume "html"
而后,可以为其创建Service资源并进行访问测试,或者在集群中直接对Pod的地址发起访问请求,以测试两个容器通过emptyDir卷共享数据的结果状态:
~]$ curl 10.1.3.106 vol-emptydir Wed Mar 7 01:10:56 UTC 2018
作为边车(sidecar)的容器paggen,其每隔10秒生成一行信息追加到存储卷上的index.html文件中,因此,通过主容器nginx的应用访问到的内容也会处于不停的变动中。另外,emptyDir存储卷也可以基于RAM创建tmpfs文件系统的存储卷,常用于为容器的应用提供高性能缓存,下面是一个配置示例:
volumes: - name: cache emptyDir: medium: Memory
emptyDir卷简单易用,但仅能用于临时存储。另外还存在一些类型的存储卷建构在emptyDir之上,并额外提供了它所没有的功能,例如,将于1.2.2节介绍的gitRepo存储卷。
1.2.2 gitRepo存储卷
gitRepo存储卷可以看作是emptyDir存储卷的一种实际应用,使用该存储卷的Pod资源可以通过挂载目录访问指定的代码仓库中的数据。使用gitRepo存储卷的Pod资源在创建时,会首先创建一个空目录(emptyDir)并克隆(clone)一份指定的Git仓库中的数据至该目录,而后再创建容器并挂载该存储卷。
定义gitRepo类型的存储卷时,其可嵌套使用的字段具体包含如下三个。
·repository
·directory
·revision
注意 使用gitRepo存储卷的Pod资源运行的工作节点上必须安装有Git程序,否则克隆仓库的操作将无从完成。另外,自Kubernetes 1.12起,gitRepo存储卷已经被废弃。
下面示例(vol-gitrepo.yaml配置文件)中的Pod资源在创建时,首先会创建一个空目录,将指定的Git仓库https://github.com/iKubernetes/k8s_book.git 中的数据复制一份直接保存于此目录中,而后将此目录创建为存储卷html,最后由容器Nginx将此存储卷挂载于/usr/share/nginx/html目录上:
apiVersion: v1 kind: Pod metadata: name: vol-gitrepo-pod spec: containers: - name: nginx image: nginx:1.12-alpine volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html gitRepo: repository: https://github.com/iKubernetes/k8s_book.git directory: . revision: "master"
访问此Pod资源中的Nginx服务即可看到其来自于Git仓库中的页面资源。不过,gitRepo存储卷在其创建完成后不会再与指定的仓库执行同步操作,这就意味着在Pod资源运行期间,如果仓库中的数据发生了变化,那么gitRepo存储卷不会同步到这些内容。当然,此时可以为Pod资源创建一个专用的边车容器用于执行此类的同步操作,尤其是数据来源于私有仓库时,通过边车容器完成其复制就更为必要。
gitRepo存储卷建构于emptyDir存储卷之上,它的生命周期与隶属的Pod对象相同,因此使用时不建议在此类存储卷中保存由容器生成的重要数据。另外,自Kubernetes 1.12版起,gitRepo存储卷已经被废弃,所以在之后的版本中若要使用它配置Pod对象,建议读者借助初始化容器(InitContainer)将仓库中的数据复制到emptyDir存储卷上,并在主容器中使用此存储卷。
1.3 节点存储卷hostPath
hostPath类型的存储卷是指将工作节点上某文件系统的目录或文件挂载于Pod中的一种存储卷,它可独立于Pod资源的生命周期,因而具有持久性。但它是工作节点本地的存储空间,仅适用于特定情况下的存储卷使用需求,例如,将工作节点上的文件系统关联为Pod的存储卷,从而使得容器访问节点文件系统上的数据。这一点在运行有管理任务的系统级Pod资源需要访问节点上的文件时尤为有用。
配置hostPath存储卷的嵌套字段共有两个:一个是用于指定工作节点上的目录路径的必选字段path;另一个是指定存储卷类型的type,它支持使用的卷类型包含如下几种。
·DirectoryOrCreate:指定的路径不存时自动将其创建为权限是0755的空目录,属主属组均为kubelet。
·Directory:必须存在的目录路径。
·FileOrCreate:指定的路径不存时自动将其创建为权限是0644的空文件,属主和属组同是kubelet。
·File:必须存在的文件路径。
·Socket:必须存在的Socket文件路径。
·CharDevice:必须存在的字符设备文件路径。
·BlockDevice:必须存在的块设备文件路径。
下面是定义在vol-hostpath.yaml配置文件中的Pod资源,它运行着日志收集代理应用filebeat,负责收集工作节点及容器相关的日志信息发往Redis服务器,它使用了三个hostPath类型的存储卷:
apiVersion: v1 kind: Pod metadata: name: vol-hostpath-pod spec: containers: - name: filebeat image: ikubernetes/filebeat:5.6.7-alpine env: - name: REDIS_HOST value: redis.ilinux.io:6379 - name: LOG_LEVEL value: info volumeMounts: - name: varlog mountPath: /var/log - name: socket mountPath: /var/run/docker.sock - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: socket hostPath: path: /var/run/docker.sock
这类Pod资源通常受控于daemonset类型的Pod控制器,它运行于集群中的每个工作节点之上,负责收集工作节点上系统级的相关数据,因此使用hostPath存储卷也是理所应当的。读者在创建上述Pod资源时,如果有可用的Redis服务器,则可通过环境变量REDIS_HOST传递给Pod资源,待其Ready之后即可通过Redis服务器查看到由其发送的日志信息。在filebeat的应用架构中,这些日志信息会发往Elasticsearch,并通过Kibana进行展示。
另外,使用hostPath存储卷时需要注意到,不同节点上的文件或许并不完全相同,于是,那些要求事先必须存在的文件或目录的满足状态也可能会有所不同;另外,基于资源可用状态的调度器调度Pod时,hostPath资源的可用性状态不会被考虑在内;再者,在节点中创建的文件或目录默认仅有root可写,若期望容器内的进程拥有写权限,则要么将它运行为特权容器,要么修改节点上目录路径的权限。
那些并非执行系统级管理任务的且不受控于Daemonset控制器的无状态应用在Pod资源被重新调度至其他节点运行时,此前创建的文件或目录大多都不会存在。因此,hostPath存储卷虽然能持久保存数据,但对于被调度器按需调度的应用来说并不适用,这时需要用到的是独立于集群节点的持久性存储卷,即网络存储卷。
1.4 网络存储卷
如前所述,Kubernetes拥有众多类型的用于适配专用存储系统的网络存储卷。这类存储卷包括传统的NAS或SAN设备(如NFS、iSCSI、fc)、分布式存储(如GlusterFS、RBD)、云端存储(如gcePersistentDisk、azureDisk、cinder和awsElasticBlockStore)以及建构在各类存储系统之上的抽象管理层(如flocker、portworxVolume和vsphereVolume)等。
1.4.1 NFS存储卷
NFS即网络文件系统(Network File System),它是一种分布式文件系统协议,最初是由Sun MicroSystems公司开发的类Unix操作系统之上的一款经典的网络存储方案,其功能旨在允许客户端主机可以像访问本地存储一样通过网络访问服务器端文件。作为一种由内核原生支持的网络文件系统,具有Linux系统使用经验的读者大多数都应该对NFS有一定的使用经验。
Kubernetes的NFS存储卷用于将某事先存在的NFS服务器上导出(export)的存储空间挂载到Pod中以供容器使用。与emptyDir不同的是,NFS存储卷在Pod对象终止后仅是被卸载而非删除。另外,NFS是文件系统级共享服务,它支持同时存在的多路挂载请求。定义NFS存储卷时,常用到以下字段。
·server
·path
·readOnly
Redis(REmote DIctionary Server)是一个著名的高性能key-value存储系统,应用非常广泛,将其部署运行于Kubernetes系统之上时,需要持久化存储卷的支持。下面是简单使用Redis的一个示例:
apiVersion: v1 kind: Pod metadata: name: vol-nfs-pod labels: app: redis spec: containers: - name: redis image: redis:4-alpine ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redisdata volumes: - name: redisdata nfs: server: nfs.ilinux.io path: /data/redis readOnly: false
上面的示例定义在资源配置文件vol-nfs.yaml中,其中的Pod资源拥有一个关联至NFS服务器nfs.ilinux.io的存储卷,Redis容器将其挂载于/data目录上,它是运行于容器中的redis-server数据的持久保存位置。
提示 这里应确保事先要存在一个名为nfs.ilinux.io的NFS服务器,其输出了/data/redis目录,并授权给了Kubernetes集群中的节点访问。主机和目录都可以按需要进行调整。
资源创建完成后,可通过其命令客户端redis-cli创建测试数据,并手动触发其同步于存储系统中,下面加粗部分的字体为要执行的Redis命令:
~]$ kubectl exec -it vol-nfs-pod redis-cli 121.0.0.1:6379> set mykey "hello ilinux.io" OK 121.0.0.1:6379> get mykey "hello ilinux.io" 121.0.0.1:6379> BGSAVE Background saving started 121.0.0.1:6379> exit
为了测试其数据持久化效果,下面删除Pod资源vol-nfs-pod,并于再次重建后检测数据是否依然能够访问:
~]$ kubectl delete pods vol-nfs-pod pod "vol-nfs-pod" deleted ~]$ kubectl apply -f vol-nfs.yaml pod "vol-nfs-pod" created
待其重建完成后,通过再一次创建的Pod资源的详细信息,我们可以观察到它挂载使用NFS存储卷的相关信息。接下来再次检查redis-server中是否还保存有此前存储的数据:
~]$ kubectl exec -it vol-nfs-pod redis-cli 121.0.0.1:6379> get mykey "hello ilinux.io" 121.0.0.1:6379>
从上面的命令结果中可以看出,此前创建的键mykey及其数据在Pod资源重建后依然存在,这表明在删除Pod资源时,其关联的外部存储卷并不会被一同删除。如果需要清除此类的数据,需要用户通过存储系统的管理接口手动进行。
1.4.2 RBD存储卷
Ceph是一个专注于分布式的、弹性可扩展的、高可靠的、性能优异的存储系统平台,同时支持提供块设备、文件系统和REST三种存储接口。它是一个高度可配置的系统,并提供了一个命令行界面用于监视和控制其存储集群。Ceph还包含鉴证和授权功能,可兼容多种存储网关接口,如OpenStack Swift和Amazon S3。Kubernetes也支持通过RBD卷类型使用Ceph存储系统为Pod提供存储卷。要配置Pod资源使用RBD存储卷,需要事先满足如下几个前提条件。
·存在某可用的Ceph RBD存储集群,否则就需要创建一个。
·在Ceph RBD集群中创建一个能满足Pod资源数据存储需要的存储映像(image)。
·在Kubernetes集群内的各节点上安装Ceph客户端程序包(ceph-common)。
在配置RBD类型的存储卷时,需要指定要连接的目标服务器和认证信息等,这一点通常使用以下嵌套字段进行定义。
·monitors<[]string>:Ceph存储监视器,逗号分隔的字符串列表;必选字段。
·image
·pool
·user
·keyring
·secretRef"Object":RBD用户认证时使用的保存有相应认证信息的Secret对象,会覆盖由keyring字段提供的密钥信息。
·readOnly
·fsType:要挂载的存储卷的文件系统类型,至少应该是节点操作系统支持的文件系统,如ext4、xfs、ntfs等,默认为ext4。
下面是一个定义在vol-rbd.yaml配置文件中使用RBD存储卷的Pod资源示例:
apiVersion: v1 kind: Pod metadata: name: vol-rbd-pod spec: containers: - name: redis image: redis:4-alpine ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-rbd-vol volumes: - name: redis-rbd-vol rbd: monitors: - '172.16.0.56:6789' - '172.16.0.57:6789' - '172.16.0.58:6789' pool: kube image: redis fsType: ext4 readOnly: false user: admin secretRef: name: ceph-secret
此示例依赖于事先存在的一个Ceph存储集群,这里假设其监视器的地址为172.16.0.56、172.16.0.57和172.16.0.58三个主机IP,并且集群上的存储池kube中存在创建好的映像Redis,此映像拥有ext4文件系统。Ceph客户端访问集群时需要事先完成认证之后才能进行后续的访问操作,此示例上,其认证信息保存于名为ceph-secret的Secret资源对象中。示例所实现的逻辑架构如图7-3所示。
图7-3 RBD存储卷
提示 此配置示例依赖于一个可用的Ceph集群,且上述配置示例中的部分参数需要参照实际环境进行修改。
1.4.3 GlusterFS存储卷
GlusterFS(Gluster File System)是一个开源的分布式文件系统,是水平扩展存储解决方案Gluster的核心,具有强大的横向扩展能力,GlusterFS通过扩展能够支持数PB存储容量和处理数千客户端。GlusterFS借助TCP/IP或InfiniBand RDMA网络将物理分布的存储资源聚集在一起,使用单一全局命名空间来管理数据。另外,GlusterFS基于可堆叠的用户空间设计,可为各种不同的数据负载提供优异的性能,是另一种流行的分布式存储解决方案。要配置Pod资源使用GlusterFS存储卷,需要事先满足以下前提条件。
1)存在某可用的GlusterFS存储集群,否则就要创建一个。
2)在GlusterFS集群中创建一个能满足Pod资源数据存储需要的卷。
3)在Kubernetes集群内的各节点上安装GlusterFS客户端程序包(glusterfs和glusterfs-fuse)。
另外,若要基于GlusterFS使用存储卷的动态供给机制(请参考1.5.6节),还需要事先部署heketi,它用于为GlusterFS集群提供RESTful风格的管理接口。Gluster存储集群及heketi的配置示例请参考附录B。
定义Pod资源使用GlusterFS类型的存储卷时,常用的配置字段包含如下几个。
·endpoints
·path
·readOnly
下面是一个定义在vol-glusterfs.yaml配置文件中的Pod资源示例,它使用了GlusterFS存储卷持久保存应用数据。它通过glusterfs-endpoints资源中定义的GlusterFS集群节点信息接入集群,并以kube-redis卷作为Pod资源的存储卷。glusterfs-endpoints资源需要在Kubernetes集群中事先创建,而kube-redis则需要事先创建于Gluster集群:
apiVersion: v1 kind: Pod metadata: name: vol-glusterfs-pod labels: app: redis spec: containers: - name: redis image: redis:alpine ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redisdata volumes: - name: redisdata glusterfs: endpoints: glusterfs-endpoints path: kube-redis readOnly: false
用于访问Gluster集群的相关节点信息要事先保存于某特定的Endpoints资源中,例如上面示例中调用的glusterfs-endpoints。此类的Endpoints资源可由用户根据实际需求手动创建,例如,下面的保存于glusterfs-endpoints.yaml文件中的资源示例中定义了三个接入相关的Gluster存储集群的节点gfs01.ilinux.io、gfs02.ilinux.io和gfs03.ilinux.io,其中的端口信息仅为满足Endpoints资源的必选字段要求,因此其值可以随意填写:
apiVersion: v1 kind: Endpoints metadata: name: glusterfs-endpoints subsets: - addresses: - ip: gfs01.ilinux.io ports: - port: 24007 name: glusterd - addresses: - ip: gfs02.ilinux.io ports: - port: 24007 name: glusterd - addresses: - ip: gfs03.ilinux.io ports: - port: 24007 name: glusterd
首先创建Endpoints资源glusterfs-endpoints,而后再创建Pod资源vol-glusterfs-pod,即可测试其数据持久存储的效果。
1.4.4 Cinder存储卷
Cinder是OpenStack Block Storage的项目名称,用来为虚拟机(VM)实例提供持久块存储。Cinder通过驱动架构支持多种后端(back-end)存储方式,包括LVM、NFS、Ceph和其他诸如EMC、IBM等商业存储产品和方案,其提供了调用度来调度卷创建的请求,能合理优化存储资源的分配,而且还拥有REST API。将Kubernetes集群部署于OpenStack构建的IaaS环境中时,Cinder的块存储功能可为Pod资源提供外部持久存储的有效方式。
在Pod资源上定义使用Cinder存储卷时,其可用的嵌套字段包含如下几个。
·volumeID
·readOnly
fsType:要挂载的存储卷的文件系统类型,至少应该是节点操作系统支持的文件系统,如ext4、xfs、ntfs等,默认为“ext4”。
下面的资源清单是定义在vol-cinder.yaml文件中的使用示例,假设在OpenStack环境中有创建好的Cinder卷“e2b8d2f7-wece-90d1-a505-4acf607a90bc”可用:
apiVersion: v1 kind: Pod metadata: name: vol-cinder-pod spec: containers: - image: mysql name: mysql args: - "--ignore-db-dir" - "lost+found" env: - name: MYSQL_ROOT_PASSWORD value: YOUR_PASS ports: - containerPort: 3306 name: mysqlport volumeMounts: - name: mysqldata mountPath: /var/lib/mysql volumes: - name: mysqldata cinder: volumeID: e2b8d2f7-wece-90d1-a505-4acf607a90bc fsType: ext4
配置可用的系统环境和存储资源时,将其匹配于资源清单文件中即可完成Pod资源创建。另外,Kubernetes所支持的各类持久存储卷其配置使用方式各有不同,鉴于篇幅有限,这里不再一一列举其使用方式。
1.5 持久存储卷
通过前面使用持久存储卷(Persistent Volume)的示例可知,Kubernetes用户必须要清晰了解所用到的网络存储系统的访问细节才能完成存储卷相关的配置任务,例如,NFS存储卷的server和path字段的配置就依赖于服务器地址和共享目录路径。这与Kubernetes的向用户和开发隐藏底层架构的目标有所背离,对存储资源的使用最好也能像使用计算资源一样,用户和开发人员无须了解Pod资源究竟运行于哪个节点,也无须了解存储系统是什么设备以及位于何处。为此,Kubernetes的PersistentVolume子系统在用户与管理员之间添加了一个抽象层,从而使得存储系统的使用和管理职能互相解耦,如图7-4所示。
PersistentVolume(PV)是指由集群管理员配置提供的某存储系统上的一段存储空间,它是对底层共享存储的抽象,将共享存储作为一种可由用户申请使用的资源,实现了“存储消费”机制。通过存储插件机制,PV支持使用多种网络存储系统或云端存储等多种后端存储系统,例如,前面使用的NFS、RBD和Cinder等。PV是集群级别的资源,不属于任何名称空间,用户对PV资源的使用需要通过PersistentVolumeClaim(PVC)提出的使用申请(或称为声明)来完成绑定,是PV资源的消费者,它向PV申请特定大小的空间及访问模式(如rw或ro),从而创建出PVC存储卷,而后再由Pod资源通过PersistentVolumeClaim存储卷关联使用,如图7-4所示。
图7-4 Pod存储卷、PVC、PV及存储设备的调用关系
尽管PVC使得用户可以以抽象的方式访问存储资源,但很多时候还是会涉及PV的不少属性,例如,用于不同场景时设置的性能参数等。为此,集群管理员不得不通过多种方式提供多种不同的PV以满足用户不同的使用需求,两者衔接上的偏差必然会导致用户的需求无法全部及时有效地得到满足。Kubernetes自1.4版起引入了一个新的资源对象StorageClass,可用于将存储资源定义为具有显著特性的类别(Class)而不是具体的PV,例如“fast”“slow”或“glod”“silver”“bronze”等。用户通过PVC直接向意向的类别发出申请,匹配由管理员事先创建的PV,或者由其按需为用户动态创建PV,这样做甚至免去了需要事先创建PV的过程。
PV对存储系统的支持可通过其插件来实现,目前,Kubernetes支持如下类型的插件。
·GCEPersistentDisk
·AWSElasticBlockStore
·AzureFile
·AzureDisk
·FC(Fibre Channel)**
·FlexVolume
·Flocker
·NFS
·iSCSI
·RBD(Ceph Block Device)
·CephFS
·Cinder(OpenStack block storage)
·Glusterfs
·VsphereVolume
·Quobyte Volumes
·HostPath
·VMware Photon
·Portworx Volumes
·ScaleIO Volumes
·StorageOS
1.5.1 创建PV
PersistentVolume Spec主要支持以下几个通用字段,用于定义PV的容量、访问模式和回收策略。
1)Capacity:当前PV的容量;目前,Capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。
2)访问模式:尽管在PV层看起来并无差别,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类似的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能,具体如图7-5所示。
·ReadWriteOnce:仅可被单个节点读写挂载;命令行中简写为RWO。
·ReadOnlyMany:可被多个节点同时只读挂载;命令行中简写为ROX。
·ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。
3)persistentVolumeReclaimPolicy:PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete,具体说明如下。
·Retain:保持不动,由管理员随后手动回收。
·Recycle:空间回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),目前仅NFS和hostPath支持此操作。
·Delete:删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder。
4)volumeMode:卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为Filesystem。
5)storageClassName:当前PV所属的StorageClass的名称;默认为空值,即不属于任何StorageClass。
6)mountOptions:挂载选项组成的列表,如ro、soft和hard等。
图7-5 各PV支持的访问模式
下面的资源清单配置示例中定义了一个使用NFS存储后端的PV,空间大小为10GB,支持多路的读写操作。待后端存储系统满足需求时,即可进行如下PV资源的创建:
kind: PersistentVolume metadata: name: pv-nfs-0001 labels: release: stable spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: slow mountOptions: - hard - nfsvers=4.1 nfs: path: "/webdata/htdocs" server: nfs.ilinux.io
创建完成后,可以看到其状态为“Available”,即“可用”状态,表示目前尚未被PVC资源所“绑定”:
~]$ kubectl get pv pv-nfs-0001 -o custom-columns=NAME:metadata.name,STATUS:status. phase NAME STATUS pv-nfs-0001 Available
下面是另一个PV资源的配置清单,它使用RBD存储后端,空间大小为2GB,仅支持单个客户端的读写访问。将RBD相关属性设定为匹配实际的环境需求,例如在Ceph集群中创建映像pv-rbd-0001,大小为2GB,并在映射后进行映像文件格式化,随后即可创建如下的PV资源:
apiVersion: v1 kind: PersistentVolume metadata: name: pv-rbd-0001 spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce rbd: monitors: - ceph-mon01.ilinux.io:6789 - ceph-mon02.ilinux.io:6789 - ceph-mon03.ilinux.io:6789 pool: kube image: pv-rbd-0001 user: admin secretRef: name: ceph-secret fsType: ext4 readOnly: false persistentVolumeReclaimPolicy: Retain
使用资源的查看命令可列出PV资源的相关信息。创建完成的PV资源可能处于下列四种状态中的某一种,它们代表着PV资源生命周期中的各个阶段。
·Available:可用状态的自由资源,尚未被PVC绑定。
·Bound:已经绑定至某PVC。
·Released:绑定的PVC已经被删除,但资源尚未被集群回收。
·Failed:因自动回收资源失败而处于的故障状态。
1.5.2 创建PVC
PersistentVolumeClaim是存储卷类型的资源,它通过申请占用某个PersistentVolume而创建,它与PV是一对一的关系,用户无须关心其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、PV标签选择器和StorageClass等相关信息即可。PVC的Spec字段的可嵌套字段具体如下。
·accessMode:当前PVC的访问模式,其可用模式与PV相同。
·resources:当前PVC存储卷需要占用的资源量最小值;目前,PVC的资源限定仅指其空间大小。
·selector:绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-
·pressions),用于挑选要绑定的PV;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出。
·storageClassName:所依赖的存储类的名称。
·volumeMode:卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为“Filesystem”。
·volumeName:用于直接指定要绑定的PV的卷名。
下面的配置清单(pvc-nfs-0001.yaml文件)定义了一个PVC资源示例,其选择PV的挑选机制是使用了标签选择器,适配的标签是release:stable,存储类为slow,这会关联到前面创建的PV资源pv-nfs-0001:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs-0001 lables: release: “stable” spec: accessModes: - ReadWriteMany volumeMode: Filesystem resources: requests: storage: 5Gi storageClassName: slow selector: matchLabels: release: "stable"
使用资源创建命令完成资源创建,而后即可查看其绑定PV资源的相关信息:
~]$ kubectl get pvc pvc-nfs-0001 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs-0001 Bound pv-nfs-0001 5Gi RWX slow 6s
如果需要绑定此前创建的PV资源pv-rbd-0001,那么创建类似如下的资源配置即可,它将保存于配置文件pvc-rbd-0001.yaml中:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-rbd-0001 spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 2Gi storageClassName: fast selector: matchLabels: release: "stable"
使用资源创建命令完成资源创建,而后即可查看其绑定PV资源的相关信息:
~]$ kubectl get pvc/pvc-rbd-0001 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-rbd-0001 Bound pv-rbd-0001 2Gi RWO fast 5s
创建好PVC资源之后,即可在Pod资源中通过persistenVolumeClain存储卷引用它,而后挂载于容器中进行数据持久化。需要注意的是,PV是集群级别的资源,而PVC则隶属于名称空间,因此,PVC在绑定目标PV时不受名称空间的限制,但Pod引用PVC时,则只能是属于同一名称空间中的资源。
1.5.3 在Pod中使用PVC
在Pod资源中调用PVC资源,只需要在定义volumes时使用persistentVolumeClaims字段嵌套指定两个字段即可,具体如下。
·claimName:要调用的PVC存储卷的名称,PVC卷要与Pod在同一名称空间中。
·readOnly:是否将存储卷强制挂载为只读模式,默认为false。
下面的清单定义了一个Pod资源,它是1.4.2节中直接使用RBD存储的Pod资源,此处改为调用了前面刚刚创建的名为pv-rbd-0001的PVC资源:
apiVersion: v1 kind: Pod metadata: name: vol-rbd-pod spec: containers: - name: redis image: redis:4-alpine ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-rbd-vol volumes: - name: redis-rbd-vol persistentVolumeClaim: claimName: pv-rbd-0001
资源创建完成后,即可通过类似于此前1.4.1节示例中的方式完成数据持久性测试。
1.5.4 存储类
存储类(storage class)是Kubernetes资源类型的一种,它是由管理员为管理PV之便而按需创建的类别(逻辑组),例如可按存储系统的性能高低分类,或者根据其综合服务质量级别进行分类(如图7-6所示)、依照备份策略分类,甚至直接按管理员自定义的标准进行分类等。不过,Kubernetes自身无法理解“类别”到底意味着什么,它仅仅是将这些当作PV的特性描述。
图7-6 基于综合服务质量的存储系统分类
存储类的好处之一便是支持PV的动态创建。用户用到持久性存储时,需要通过创建PVC来绑定匹配的PV,此类操作需求量较大,或者当管理员手动创建的PV无法满足PVC的所有需求时,系统按PVC的需求标准动态创建适配的PV会为存储管理带来极大的灵活性。
存储类对象的名称至关重要,它是用户调用的标识。创建存储类对象时,除了名称之外,还需要为其定义三个关键字段:provisioner、parameter和reclaimPolicy。
1.StorageClass Spec
StorageClass Spec中的字段是定义存储类时最重要的字段,其包含以下五个可用字段。
·provisioner(供给方):即提供了存储资源的存储系统,存储类要依赖Provisioner来判定要使用的存储插件以便适配到目标存储系统。Kubernetes内建有多种供给方(Provisioner),这些供给方的名字都以“kubernetes.io”为前缀。另外,它还支持用户依据Kubernetes规范自定义Provisioner。
·parameters(参数):存储类使用参数描述要关联到的存储卷,不过,不同的Provisioner可用的参数各不相同。
·reclaimPolicy:为当前存储类动态创建的PV指定回收策略,可用值为Delete(默认)和Retain;不过,那些由管理员手工创建的PV的回收策略则取决于它们自身的定义。
·volumeBindingMode:定义如何为PVC完成供给和绑定,默认值为“VolumeBinding Immediate”;此选项仅在启用了存储卷调度功能时才能生效。
·mountOptions:由当前类动态创建的PV的挂载选项列表。
下面是一个定义在glusterfs-storageclass.yaml配置文件中的资源清单,它定义了一个使用Gluster存储系统的存储类glusterfs,并通过annotations字段将其定义为默认的存储类:
kind: StorageClass apiVersion: storage.k8s.io/v1beta1 metadata: name: glusterfs provisioner: kubernetes.io/glusterfs parameters: resturl: "http://heketi.ilinux.io:8080" restauthenabled: "false" restuser: "ik8s" restuserkey: "ik8s.io"
这里需要特别提醒读者的是,parameters.resturl字段用于指定Gluster存储系统的RESTful风格的访问接口,本示例中使用的“http://heketi.ilinux.io:8080 ”应替换为读者自己实际环境中的可用地址。Gluster存储系统本身并不支持这种访问方式,管理员需要额外部署heketi配合Gluster以提供此类服务接口。Heketi支持认证访问,不过只有在restauthenabled设置为“true”时,restuser和restuserkey字段才会启用。Heketi的设置及使用方式请参考附录B。
2.动态PV供给
动态PV供给的启用,需要事先由管理员创建至少一个存储类,不同的Provisoner的创建方法各有不同,具体内容如前一节所示。另外,并非所有的存储卷插件都由Kubernetes内建支持PV动态供给功能,具体信息如图7-7所示。
上文中定义glusterfs存储类资源创建完成后,便可以据此使用动态PV供给功能。下面的资源清单定义在pvc-gluserfs-dynamic-0001.yaml配置文件中,它将从glusterfs存储类中申请使用5GB的存储空间:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-gluster-dynamic-0001 annotations: volume.beta.kubernetes.io/storage-class: glusterfs spec: # storageClassName: "glusterfs" accessModes: - ReadWriteOnce resources: requests: storage: 5Gi
图7-7 各存储插件对动态供给方式的支持状况
目前,在PVC的定义中指定使用的存储类资源的方式共有两种:一种是使用spec.storageClassName字段,另一种是使用“volume.beta.kubernetes.io/storage-class”注解信息,如上面示例中所示。不过,建议仅使用一种方式,以免两者设置为不同的值时会出现配置错误。接下来创建定义的PVC,并检查其绑定状态:
~]$ kubectl create -f pvc-glusterfs-dynamic-0001.yaml persistentvolumeclaim "pvc-gluster-dynamic-0001" created
通过如下命令输出的PVC资源的描述信息可以看到,PVC存储卷已创建完成且已经完成了PV绑定,绑定的PV资源由persistentvolume-controller控制器动态提供:
~]$ kubectl describe pvc pvc-gluster-dynamic-0001 Name: pvc-gluster-dynamic-0001 Namespace: default StorageClass: glusterfs Status: Bound Volume: pvc-5836eb47-6c77-11e8-9bab-000c29be4e28 Labels: <none> Annotations: pv.kubernetes.io/bind-completed=yes pv.kubernetes.io/bound-by-controller=yes volume.beta.kubernetes.io/storage-provisioner=kubernetes.io/glusterfs Finalizers: [kubernetes.io/pvc-protection] Capacity: 5Gi Access Modes: RWO Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ProvisioningSucceeded 55s persistentvolume-controller Successfully provisioned volume pvc-5836eb47-6c77-11e8-9bab-000c29be4e28 using kubernetes. io/glusterfs
任何支持PV动态供给的存储系统都可以在定义为存储类后由PVC动态申请使用,这对于难以事先预估使用到的存储空间大小及存储卷数量的使用场景尤为有用,例如由StatefulSet控制器管理Pod对象时,存储卷是必备资源,且随着规模的变动,存储卷的数量也会随之变动。
另外,用户也可以使用云端存储提供的PV动态供给机制,如AWS EBS、AzureDisk、Cinder或GCEPersistentDisk等,将Kubernetes部署于IaaS云端时,此种存储方式使用的较多。各类云存储动态供给的具体使用方式请参考相关的使用手册。
1.5.5 PV和PVC的生命周期
PV是Kubernetes集群的存储资源,而PVC则代表着资源需求。创建PVC时对PV发起的使用申请,即为“绑定”。PV和PVC是一一对应的关系,可用于响应PVC申请的PV必须要能够容纳PVC的请求条件,它们二者的交互遵循如下生命周期。
1.存储供给
存储供给(Provisioning)是指为PVC准备可用PV的机制。Kubernetes支持两种PV供给方式:静态供给和动态供给。
(1)静态供给
静态供给是指由集群管理员手动创建一定数量的PV的资源供应方式。这些PV负责处理存储系统的细节,并将其抽象成易用的存储资源供用户使用。静态提供的PV可能属于某存储类(StorageClass),也可能没有存储类,这一点取决于管理员的设定。
(2)动态供给
不存在某静态的PV匹配到用户的PVC申请时,Kubernetes集群会尝试为PVC动态创建符合需求的PV,此即为动态供给。这种方式依赖于存储类的辅助,PVC必须向一个事先存在的存储类发起动态分配PV的请求,没有指定存储类的PVC请求会被禁止使用动态创建PV的方式。
另外,为了支持使用动态供给机制,集群管理员需要为准入控制器(admission controller)启用“DefaultStorageClass”选项,这一点通过“--admission-control”命令行选项为API Server进行设定即可,后文会对准入控制器予以描述。
2.存储绑定
用户基于一系列存储需求和访问模式定义好PVC后,Kubernetes系统的控制器即会为其查找匹配的PV,并于找到之后在此二者之间建立起关联关系,而后它们二者之间的状态即转为“绑定”(Binding)。若PV是为PVC而动态创建的,则该PV专用于其PVC。
若是无法为PVC找到可匹配的PV,则PVC将一直处于未绑定(unbound)状态,直到有符合条件的PV出现并完成绑定方才可用。
(1)存储使用(Using)
Pod资源基于persistenVolumeClaim卷类型的定义,将选定的PVC关联为存储卷,而后即可为内部的容器所使用。对于支持多种访问模式的存储卷来说,用户需要额外指定要使用的模式。一旦完成将存储卷挂载至Pod对象内的容器中,其应用即可使用关联的PV提供的存储空间。
(2)PVC保护(Protection)
为了避免使用中的存储卷被移除而导致数据丢失,Kubernetes自1.9版本起引入了“PVC保护机制”。启用了此特性后,万一有用户删除了仍处于某Pod资源使用中的PVC时,Kubernetes不会立即予以移除,而是推迟到不再被任何Pod资源使用后方才执行删除操作。处于此种阶段的PVC资源的status字段为“Termination”,并且其Finalizers字段中包含“kubernetes.io/pvc-protection”。
3.存储回收(Reclaiming)
完成存储卷的使用目标之后,即可删除PVC对象以便进行资源回收。不过,至于如何操作则取决于PV的回收策略。目前,可用的回收策略有三种:Retained、Recycled和Deleted。
(1)留存(Retain)
留存策略意味着在删除PVC之后,Kubernetes系统不会自动删除PV,而仅仅是将它置于“释放”(released)状态。不过,此种状态的PV尚且不能被其他PVC申请所绑定,因为此前的申请生成的数据仍然存在,需要由管理员手动决定其后续处理方案。这就意味着,如果想要再次使用此类的PV资源,则需要由管理员按下面的步骤手动执行删除操作。
1)删除PV,这之后,此PV的数据依然留存于外部的存储之上。
2)手工清理存储系统上依然留存的数据。
3)手工删除存储系统级的存储卷(例如,RBD存储系统上的image)以释放空间,以便再次创建,或者直接将其重新创建为PV。
(2)回收(Recycle)
如果可被底层存储插件支持,资源回收策略会在存储卷上执行数据删除操作并让PV资源再次变为可被Claim。另外,管理员也可以配置一个自定义的回收器Pod模板,以便执行自定义的回收操作。不过,此种回收策略行将废弃。
(3)删除(Delete)
对于支持Deleted回收策略的存储插件来说,在PVC被删除后会直接移除PV对象,同时移除的还有PV相关的外部存储系统上的存储资产(asset)。支持这种操作的存储系统有AWS EBS、GCE PD、Azure Disk或Cinder。动态创建的PV资源的回收策略取决于相关存储类上的定义,存储类上相关的默认策略为Delete,大多数情况下,管理员都需要按用户期望的处理机制修改此默认策略,以免导致数据非计划内的误删除。
4.扩展PVC
Kubernetes自1.8版本起增加了扩展PV空间的特性,截至目前,它所支持的扩展PVC机制的存储卷共有以下几种。
·gcePersistentDisk
·awsElasticBlockStore
·Cinder
·glusterfs
·rbd
“PersistentVolumeClaimResize”准入插件负责对支持空间大小变动的存储卷执行更多的验证操作,管理员需要事先启用此插件才能使用PVC扩展机制,那些将“allowVolume Expansion”字段的值设置为“true”的存储类即可动态扩展存储卷空间。随后,用户改动Claim请求更大的空间即能触发底层PV空间扩展从而带来PVC存储卷的扩展。
对于包含文件系统的存储卷来说,只有在有新的Pod资源基于读写模式开始使用PVC时才会执行文件系统的大小调整操作。换句话说,如果某被扩展的存储卷已经由Pod资源所使用,则需要重建此Pod对象才能触发文件系统大小的调整操作。支持空间调整的文件系统仅有XFS和EXT3/EXT4。
1.6 downwardAPI存储卷
很多时候,应用程序需要基于其所在的环境信息设定运行特性等,这类环境信息包括节点及集群的部分详细属性信息等,例如,Nginx进程可根据节点的CPU核心数量自动设定要启动的worker进程数,JVM虚拟机可根据节点内存资源自动设定其堆内存大小。类似地,托管运行于Kubernetes的Pod对象中的容器化应用偶尔也需要获取其所属Pod对象的IP、主机名、标签、注解、UID、请求的CPU及内存资源量及其限额,甚至是Pod所在的节点名称等,容器可以通过环境变量或downwardAPI存储卷访问此类信息,不过,标签和注解仅支持通过存储卷暴露给容器。
1.6.1 环境变量式元数据注入
引用downwardAPI元数据信息的常用方式之一是使用容器的环境变量,它通过在valueFrom字段中嵌套fieldRef或resourceFieldRef字段来引用相应的数据源。不过,通常只有常量类的属性才能够通过环境变量注入到容器中,毕竟,在进程启动完成后将无法再向其告知变量值的变动,于是,环境变量也就不支持中途的更新操作。
可通过fieldRef字段引用的信息具体如下。
·spec.nodeName:节点名称。
·status.hostIP:节点IP地址。
·metadata.name:Pod对象的名称。
·metadata.namespace:Pod对象隶属的名称空间。
·status.podIP:Pod对象的IP地址。
·spec.serviceAccountName:Pod对象使用的ServiceAccount资源的名称。
·metadata.uid:Pod对象的UID。
·metadata.labels['
·metadata.annotations['
另外,可通过resourceFieldRef字段引用的信息是指当前容器的资源请求及资源限额的定义,因此它们包括requests.cpu、limits.cpu、requests.memory和limits.memory四项。
下面的资源配置清单示例(downwardAPI-env.yaml)中定义的Pod对象通过环境变量向容器env-test-container中注入了Pod对象的名称、隶属的名称空间、标签app的值以及容器自身的CPU资源限额和内存资源请求等信息:
apiVersion: v1 kind: Pod metadata: name: env-test-pod labels: app: env-test-pod spec: containers: - name: env-test-container image: busybox command: [ "/bin/sh", "-c", "env" ] resources: requests: memory: "32Mi" cpu: "125m" limits: memory: "64Mi" cpu: "250m" env: - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_APP_LABEL valueFrom: fieldRef: fieldPath: metadata.labels['app'] - name: MY_CPU_LIMIT valueFrom: resourceFieldRef: resource: limits.cpu - name: MY_MEM_REQUEST valueFrom: resourceFieldRef: resource: requests.memory divisor: 1Mi restartPolicy: Never
此Pod对象创建完成后,向控制台打印所有的环境变量即可终止运行,它仅用于测试通过环境变量注入信息到容器的使用效果:
~]$ kubectl create -f downwardAPI-env.yaml pod "env-test-pod" created $ kubectl get pods -l app=env-test-pod NAME READY STATUS RESTARTS AGE env-test-pod 0/1 Completed 0 1m
而后即可通过控制台日志获取注入的环境变量:
~]$ kubectl logs env-test-pod | grep "^MY_" MY_POD_NAMESPACE=default MY_CPU_LIMIT=1 MY_APP_LABEL=env-test-pod MY_MEM_REQUEST=32 MY_POD_NAME=env-test-pod
如示例中的最后一个环境变量所示,在定义资源请求或资源限制时还可额外指定一个“divisor”字段,用于为引用的值指定一个除数以实现所引用的相关值的单位换算。CPU资源的divisor字段其默认值为1,表示为1个核心,相除的结果不足1个单位时则向上圆整(例如,0.25向上圆整的结果为1),它的另一个可用单位为1m,即表示1个微核心。内存资源的divisor字段其默认值为也是1,不过,它意指1个字节,此时,32Mi的内存资源则要换算为33554432的结果予以输出。其他可用的单位还有1Ki、1Mi、1Gi等,于是,在将divisor字段的值设置为1Mi时,32Mi的内存资源的换算结果即为32。
注意 未为容器定义资源请求及资源限额时,downwardAPI引用的值即默认为节点的可分配CPU及内存资源量。
1.6.2 存储卷式元数据注入
向容器注入元数据信息的另一种方式是使用downwardAPI存储卷,它将配置的字段数据映射为文件并可通过容器中的挂载点进行访问。1.2.5节中能够通过环境变量的方式注入的元数据信息也都可以使用存储卷的方式进行信息暴露,除此之外,还可以在downwardAPI存储卷中使用fieldRef引用如下两个数据源。
·metadata.labels:Pod对象的所有标签信息,每行一个,格式为label-key="escaped-label-value"。
·metadata.annotations:Pod对象的所有注解信息,每行一个,格式为annotation-key="escaped-annotation-value"。
下面的资源配置清单示例(downwardAPI-vol.yaml)中定义的Pod对象通过downwardAPI存储卷向容器volume-test-container中注入了Pod对象隶属的名称空间、标签、注解以及容器自身的CPU资源限额和内存资源请求等信息。存储卷在容器中的挂载点为/etc/podinfo目录,因此,注入的每一项信息均会映射为此路径下的一个文件:
kind: Pod apiVersion: v1 metadata: labels: zone: east-china rack: rack-101 app: dapi-vol-pod name: dapi-vol-pod annotations: annotation1: "test-value-1" spec: containers: - name: volume-test-container image: busybox command: ["sh", "-c", "sleep 864000"] resources: requests: memory: "32Mi" cpu: "125m" limits: memory: "64Mi" cpu: "250m" volumeMounts: - name: podinfo mountPath: /etc/podinfo readOnly: false volumes: - name: podinfo downwardAPI: defaultMode: 420 items: - fieldRef: fieldPath: metadata.namespace path: pod_namespace - fieldRef: fieldPath: metadata.labels path: pod_labels - fieldRef: fieldPath: metadata.annotations path: pod_annotations - resourceFieldRef: containerName: volume-test-container resource: limits.cpu path: "cpu_limit" - resourceFieldRef: containerName: volume-test-container resource: requests.memory divisor: "1Mi" path: "mem_request"
创建资源配置清单中定义的Pod对象后即可测试访问由downwardAPI存储卷映射的文件pod_namespace、pod_labels、pod_annotations、limits_cpu和mem_request等:
~]$ kubectl create -f downwardAPI-vol.yaml pod "dapi-vol-pod" created
接下来即可测试访问上述的映射文件,例如,查看Pod对象的标签列表:
~]$ kubectl exec dapi-vol-pod -- cat /etc/podinfo/pod_labels app="dapi-vol-pod" rack="rack-101" zone="east-china"
如命令结果所示,Pod对象的标签信息每行一个地映射于自定义的路径/etc/podinfo/pod_labels文件中,类似地,注解信息也以这种方式进行处理。如前面的章节中所述,标签和注解支持运行时修改,其改动的结果也会实时映射进downwardAPI生成的文件中。例如,为dapi-vol-pod添加新的标签:
~]$ kubectl label pods dapi-vol-pod env="test" pod "dapi-vol-pod" labeled
而后再次查看容器内的pod_labels文件的内容,由如下的命令结果可知新的标签已经能够通过相关的文件获取到:
~]$ kubectl exec dapi-vol-pod -- cat /etc/podinfo/pod_labels app="dapi-vol-pod" env="test" rack="rack-101" zone="east-china"
downwardAPI存储卷为Kubernetes上运行容器化应用提供了获取外部环境信息的有效途径,这一点对那些非为云原生开发的应用程序在不进行代码重构的前提下,获取环境信息进行自身配置等操作时尤为有用。
1.7 小结
本篇主要讲解了Kubernetes的存储卷及其功用,并通过应用示例给出了部署存储卷类型的使用方法,具体如下。
·临时存储卷emptyDir和gitRepo的生命周期与Pod对象相同,但gitRepo能够通过引用外部Git仓库的数据来实现数据的持久性。
·节点存储卷hostPath提供了节点级别的数据持久能力。
·网络存储卷NFS、GlusterFS和RBD等是企业内部较为常用的独立部署的持久存储系统。
·云存储卷AWS ebs等是托管于云端的Kubernetes系统上较为常用的持久存储系统。
·PV和PVC可将存储管理和存储使用解耦为消费者模型。
·基于StorageClass可以实现PV的动态供给,GlusterFS和Ceph RBD,以及云端存储AWS ebs等都可以实现此类功能。
版权声明:如无特殊说明,文章均为本站原创,版权所有,转载需注明本文链接
本文链接:http://www.bianchengvip.com/article/Kubernetes-Storage-Volumes-and-Data-Persistence/