在 OpenShift 中,Pod 会被经常性的创建和销毁,也会在不同的主机之间快速的迁移。为了保证容器在重启或者迁移以后能够使用原来的数据,就必须使用持久化存储。所以,持久化存储的管理对于 PaaS 平台来说就显得非常重要。
1
OpenShift 存储 PV 和 PVC
OpenShift 利用 Kubernetes Persistent Volume 概念来管理存储。管理员可以快速划分卷提供给容器使用。开发人员通过命令行和界面申请使用存储,而不必关心后端存储的具体类型和工作机制。
Persistent Volume(PV)是一个开放的存储管理框架,提供各种不同类型存储的支持。OpenShift 默认支持 NFS、GlusterFS、Cinder、Ceph、EBS、iSCSI 和 Fibre Channel 等存储,用户还可以根据需求对 PV 框架进行扩展,从而使其支持更多类型的存储。
PersistentVolumeClaim (PVC) 是用户的一个 Volume 请求。用户通过创建 PVC 消费 PV 的资源。
PV 只有被 PVC 绑定后才能被 Pod 挂载使用,PV 和 PVC 的生命周期如下图 2-72 所示:
图2-72 PV和PVC的生命周期
从图中可以看到,生命周期包含5个阶段:
Avaliable:这个阶段表示PV创建完成,处于可用状态。创建PV可以通过手动创建或动态创建。
Pending:这个阶段表示PV和PVC处于匹配状态,匹配的策略有访问模式和卷大小,以及支持通过label匹配。如果无法匹配则PVC会一直处于Pending状态,如果可以匹配,但是后端存储置备卷失败,则会转为Failure状态。
Bound:这个阶段表示PV和PVC已经处于绑定状态,这个状态的PVC才能被Pod挂载使用。
Release:这个阶段表示挂载PVC的Pod被删除,PVC处于释放状态,也就是未被任何Pod挂载,但这个状态的PV无法被PVC再次绑定。
Retain:这个阶段表示删除PVC,PV转变为回收状态,该状态下的PV无法直接被新的PVC绑定。回收状态下PV是否保留数据取决于PV的回收策略定义,默认会保留。如果想要将改状态的PV转变为Avaliable,必须删除PV然后重新创建。
在PV和PVC的生命周明中,最关键的两个阶段是创建和绑定。PV按创建方式的不同可分为动态PV和静态PV。静态PV指通过手动创建的PV,而动态卷是指由StorageClass(简称SC)自动创建PV。
静态PV需要手动编辑Yaml文件并应用到集群中,不同的存储后端,PV的配置参数不同,如NFS后端的PV示例内容如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv0001
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /data/mydb
server: xxx.xxx.xxx.xxx
persistentVolumeReclaimPolicy: Retain
其中访问模式和PV容量对能否和PVC绑定至关重要。PV支持的访问模式共有三种,如下表2-8所示:
表2-8 PV访问模式
访问模式 |
简写 |
描述 |
ReadWriteOnce |
RWO |
PV可以由单个Pod以读写方式挂载。 |
ReadOnlyMany |
ROX |
PV可以由多个Pod以只读方式挂载。 |
ReadWriteMany |
RWX |
PV可以由多个Pod以读写方式挂载。 |
对于不同后端存储对访问模式的支持是不同的。接下来我们看一下常见后端存储支持的PV访问模式,如下表2-9所示:
表2-9 不同存储后端支持的访问模式
Volume Plug-in |
ReadWriteOnce |
ReadOnlyMany |
ReadWriteMany |
---|---|---|---|
AWS EBS |
Yes |
No |
No |
Azure File |
Yes |
Yes |
Yes |
Azure Disk |
Yes |
No |
No |
Cinder |
Yes |
No |
No |
Fibre Channel |
Yes |
Yes |
No |
GCE Persistent Disk |
Yes |
No |
No |
HostPath |
Yes |
No |
No |
iSCSI |
Yes |
Yes |
No |
LocalVolume |
Yes |
No |
No |
NFS |
Yes |
Yes |
Yes |
VMware vSphere |
Yes |
No |
No |
从上表中,我们可以看到,Azure File和NFS支持的读写类型是最全的。我们可以使用NAS或者配置NFS Server。当然,企业级NAS的性能要比NFS Server好得多。在OpenShift中,除了上表列出常见存储类型之外,还可以选择软件定义存储,如Ceph,Ceph可以同时提供块存储RBD、对象存储、文件系统存储CephFS。
除了静态PV之外,OpenShift还可以使用StorageClass来管理动态PV。每个StorageClass都定义一个Provisioner属性,也就是后端存储类型。OpenShift安装后,会内嵌一些Provisioner,它们的StorageClass会被自动创建,如下表所示:
Volume Plug-in |
Internal Provisioner |
Storage Class Provisioner Value |
---|---|---|
AWS EBS |
Yes |
kubernetes.io/aws-ebs |
Azure File |
Yes |
kubernetes.io/azure-file |
Azure Disk |
Yes |
kubernetes.io/azure-disk |
Cinder |
Yes |
kubernetes.io/cinder |
Fibre Channel |
No |
|
GCE Persistent Disk |
Yes |
kubernetes.io/gce-pd |
HostPath |
No |
|
iSCSI |
No |
|
LocalVolume |
No |
|
NFS |
No |
|
VMware vSphere |
Yes |
kubernetes.io/vsphere-volume |
如果要创建一个没有对应 Provisioner的StorageClass,也称为静态StorageClass,可以使用kubernetes.io/no-provisioner,示例如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: static-provisioner
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
StorageClass创建之后,就可以通过创建PVC触发StorageClass完成PV的创建。但是,静态StorageClass除外,因为静态StorageClass没有真实的后端存储,依然需要手动创建PV并明确指定storageClassName为静态StorageClass的名称,详细的使用案例参见第三章3.2.4-2小节。
无论是通过静态还是动态创建PV,只有PVC和PV绑定之后才能被Pod使用。尤其在集群中有多个不同的后端的PV,PVC如何能绑定到满足预期的PV将成为关键,下面我们就进行详细说明。
2
PV 和 PVC 绑定逻辑
在上一小节中,我们介绍了 PV 的创建方式和支持的类型,那么如果一个集群中,既有多种类型的 StorageClass,又有多种不同后端的静态 PV,PVC 与 PV 的匹配遵循一定的逻辑,如下图 2-73 所示:
图2-73 PV和PVC匹配逻辑
从图中可以看出动态卷优先,如果动态卷无法满足PVC需求,才会匹配静态PV。而且能否匹配成功是根据PV、PVC、集群中StorageClass的配置等多方面决定的,匹配大致逻辑如下:
1. 创建PVC后,首先会判定PVC中是否指定了storageClassName字段,例如下面PVC定义会触发StorageClass gp2创建的PV并绑定(静态StorageClass需要手动创建PV,后文不再重复强调),如果无法找到指定的StorageClass,则PVC处于Pending状态:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-claim
spec:
storageClassName: gp2
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
2. 如果PVC中没有指定storageClassName参数,则会判定集群中是否有默认StorageClass,如果存在,则会直接使用默认StorageClass创建PV。一个集群最多只能有一个默认StorageClass,表示如果PVC中未指定明确的storageClassName,则使用默认StorageClass创建PV。使用如下命令将集群中一个SC设置为默认StorageClass:
# oc annotate storageclass
建议不要设置静态StorageClass为默认StorageClass,因为静态StorageClass不会自动创建PV,即使设定为默认StorageClass,还是要手动创建设定storageClassName的PV,导致设定为默认StorageClass没有价值。
3. 如果集群未定义默认StorageClass,则会进入静态PV匹配。首先会判定在PVC是否定义了selector用于匹配特定标签的PV。通常在PV上设定标签主要用于对PV分级,比如根据存储性能,存储地理位置等。例如下面的PVC就只能匹配包含storage-tier=gold且volume-type=ssd的PV,如果无法找到符合标签的PV则PVC处于Pending状态。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: high-performance-volume
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
selector:
matchLabels:
storage-tier: gold
volume-type: ssd
4. 如果PVC中未定义selector,或者有满足selector的PV,则根据PVC和PV两者中定义的访问模式和容量大小匹配。其中访问模式必须完全相同,而容量是只要PV定义的容量大小大于等于PVC定义的容量大小就可以匹配成功。如果访问模式或者容量大小无法满足需要,则PVC处于Pending状态。
可以发现,在动态卷绑定时只判断storageClassName,而在静态PV绑定时才会判断selector、访问模式、容量大小。
另外,需要注意的是,访问模式和容量大小的匹配只是逻辑上的,并不会校验后端存储是否支持这种访问模式或后端存储的真实空间大小。例如我们完全可以通过多读写访问模式挂载iSCSI卷,只不过由于锁机制,无法同时启动多个实例。
3
容器云原生存储
OpenShift 目前主推 OpenShift Container Storage(简称 OCS)实现存储层。OCS 主要是通过 Rook+Ceph 实现的。
Rook(https://rook.io/)使 Ceph 部署、引导、配置、供应、扩展、升级、迁移、灾难恢复、监视和资源管理自动化。Operator 将启动和监视 Ceph ( https://rook.io/)使 Ceph 部署、引导、配置、供应、扩展、升级、迁移、灾难恢复、监视和资源管理自动化。Operator 将启动和监视 Ceph ) Monitor 容器,提供 RADOS 存储的 Ceph OSD 守护程序,以及启动和管理其他 Ceph 守护程序。通过初始化 Pod 和运行服务所需的其他工件来管理存储池,对象存储(S3 / Swift)和文件系统的 CRD。
Rook的功能如下:
高可用性和弹性-Ceph没有单点故障(SPOF),并且其所有组件都以高可用性的方式本地工作
数据保护-Ceph会定期清理不一致的对象,并在必要时进行修复,以确保副本始终保持一致
跨混合云的一致存储平台-Ceph可以部署在任何位置(内部部署或裸机),因此无论用户身在何处,都能提供类似的体验
块,文件和对象存储服务-Ceph可以通过多个存储接口公开您的数据,从而解决所有应用程序用例
放大/缩小-Operator完全负责添加和删除存储。
仪表板-Operator部署了一个仪表板,用于监视和自检群集。
OCS的架构图如下图2-74所示:
图2-74 OCS存储架构
OCS通过Operator方式进行安装。目前支持在OpenShift物理节点上离线安装。
OCS的安装很简单,大致步骤如下图2-75所示,安装OCS的Operator:
图2-75安装OCS的Operator
接下来,利用OCS Operator部署的API创建Ceph集群,选择加入到OCS的节点。此处我们选择新添加的三个节点:如下图2-76所示:
图2-76 选择OCS节点
当OCS相关所有Pod都创建成功并处于Running状态,代表OCS部署成功。OCS部署成功后,我们查看OpenShift中的StorageClass,增加了Ceph相关的内容:
# oc get sc
NAME PROVISIONER AGE
localblock kubernetes.io/no-provisioner 51m
ocs-storagecluster-ceph-rbd openshift-storage.rbd.csi.ceph.com 51m
ocs-storagecluster-cephfs openshift-storage.cephfs.csi.ceph.com 51m
openshift-storage.noobaa.io openshift-storage.noobaa.io/obc 45m
部署成功后,就可以在OpenShift中通过CSI的方式调用OCS了。
我们使用配置文件创建一个PVC(调用storageClassName: ocs-storagecluster-ceph-rbd):
# cat create_ns_ocs_pvc.yaml
---
kind: Namespace
apiVersion: v1
metadata:
name: "e-library"
labels:
name: "e-library"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ocs-pv-claim
labels:
name: "e-library"
namespace: "e-library"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: ocs-storagecluster-ceph-rbd
查看PVC创建成功,并且OCS自动创建PV与之绑定。
# oc get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ocs-pv-claim Bound pvc-f06484c8-abd7-11ea-b311-0242ac110022 10Gi RWO ocs-storagecluster-ceph-rbd 3m52s
接下来,我们就可以创建Pod来消费这个PVC了。
在OCS早期版本,只支持内置模式,也是就说,必须把OCS装在OpenShift上,利用OpenShift的Worker Node的本地存储空间做存储空间。这种模式部署、使用都很方便。唯一的问题是:存储服务器无法与OpenShift集群解耦。
OCS 从v4.5版本开始支持外部的存储模式。也就是说,通过OpenShift上安装的OCS Operator,可以对接在外部物理机上安装的Ceph。然后以OpenShift中Rook的方式管理外部物理机上的Ceph,实现存储服务器与OpenShift集群解耦。
我们在OpenShift上部署OCS Operator后,可以选择连接外部的Cluster:然后提示下载Python脚本。将这个脚本在外置的Ceph集群的任意一个Monitor节点上执行,获取到Ceph集群信息,输入到OCS对接外部存储的位置,如下图2-77所示:
图2-77 OCS对接外置存储
我们将这个脚本在外置的ceph的mon的节点上执行:
首先查看脚本的使用帮助:
#python ceph-external-cluster-details-exporter.py --help
在下面的命令中,rbd-data-pool-name指定要创建的pool的名称;rgw-endpoint指定Ceph集群对象网关地址:
#python ceph-external-cluster-details-exporter.py --rbd-data-pool-name abc --rgw-endpoint 192.168.18.203:8080
命令执行后,会以json的方式返回一大串输出结果,我们将结果粘贴到如到上图2-77的所示的空白处,即可完成添加。由于后续的操作步骤与内置模式类似,因此不再展开说明。
OCS对接外置Ceph存储的后续步骤,请参考repo中“ocs外置存储方式”。
4
OpenShift/kubernetes 存储趋势
在 OpenShift 的网络部分,我们提到了一个开源项目 CNI。它定义网络插件和容器之间的通用接口,实现了容器运行时与 SDN 的松耦合。那么,在容器存储方面,有没有类似的开源项目呢?
开源项目 Container Storage Interface(CSI)正是这个目的。CSI旨在提供一种标准,将任意块存储和文件存储在符合这种标准的情况下,可以为Kubernetes上的容器化提供持久存储。随着CSI的采用,Kubernetes存储层变得真正可扩展。使用CSI,第三方存储提供商可以编写和部署插件,在Kubernetes中公开新的存储系统,而无需触及核心Kubernetes代码。CSI为Kubernetes用户提供了更多存储选项,使系统更加安全可靠。目前在OpenShift中的CSI正式GA。
CSI是通过External CSI Controllers实现的,它是一个运行在Infra节点包含三个容器的Pod。如下图2-78所示:
external CSI attacher container:它将从OpenShift发过来的attach 和detach调用转换为对CSI driver的ControllerPublish和ControllerUnpublish调用。
external CSI provisioner container,它将从OpenShift发过来的provision 和 delete的调用转化为对CSI Driver的CreateVolume 和 DeleteVolume 的调用。
CSI driver container
图2-78 OpenShift CSI逻辑图
通过一个CSI Driver DaemonSet,在每个OpenShift节点上启动一个Driver容器。它允许OpenShift将CSI driver提供的存储挂载到OpenShift节点,并将其映射挂载到Pod中。
需要指出的是,从Ceph 社区版本v14开始,OpenShift访问Ceph必须要有CSI Driver,无法绕开CSI直接访问Ceph存储。
本文选自《OpenShift在企业中的实践》(第2版),经出版社授权发布。
扫码京东购买
推荐语:经典畅销书再次升级!红帽首席解决方案架构师联合撰写,20位全球知名企业IT负责人推荐,基于OpenShift v4,详述PaaS、DevOps、云原生、微服务治理。完整描绘企业数字化转型路线,为企业通过OpenShift实现IT转型给出具体建议和参考架构。
微信淘宝等平台要互通!?腾讯阿里字节回应
2021-09-14
一文详解 API 设计最佳实践
2021-09-12
12 种经典亿级流量架构之资源隔离思想与方法论
2021-09-09
拼夕夕订单超时未支付自动关闭实现方案!
2021-09-08
在腾讯,我们如何做 Code Review
2021-09-24
紫色飞猪:基于K8s的集群稳定架构
2021-09-23
2W 字详解设计模式!
2021-09-22
巨人大哥聊聊电商微服务体系中分层设计和领域的划分
2021-09-20
亿级流量架构怎么做资源隔离?口琴这篇写得太好了!
2021-09-17
蚂蚁集团于雨:万级规模 K8S 集群 Etcd 高可用建设之路
2021-09-16
干货丨千万流量大型分布式系统架构设计实战
2021-09-15
京东面试官:你是怎么理解 MySQL 的优化原理的?
2021-09-26
在腾讯,我们如何做 Code Review
2021-09-24
紫色飞猪:基于K8s的集群稳定架构
2021-09-23
2W 字详解设计模式!
2021-09-22
巨人大哥聊聊电商微服务体系中分层设计和领域的划分
2021-09-20
亿级流量架构怎么做资源隔离?口琴这篇写得太好了!
2021-09-17
蚂蚁集团于雨:万级规模 K8S 集群 Etcd 高可用建设之路
2021-09-16
干货丨千万流量大型分布式系统架构设计实战
2021-09-15
微信淘宝等平台要互通!?腾讯阿里字节回应
2021-09-14
一文详解 API 设计最佳实践
2021-09-12
12 种经典亿级流量架构之资源隔离思想与方法论
2021-09-09
美团技术:到店结算平台实践(胶片)
2021-09-06
Serverless实战之路
2021-09-03
柴华:DDD在哈啰交易中台的实践
2021-09-02
建设微服务API网关的一些实践
2021-10-13
新公司要上监控,我决定用Prometheus
2021-10-12
RabbitMQ 七战 Kafka,差异立现!
2021-09-30
分布领域驱动设计(DDD):领域接口化设计式缓存的选择
2021-09-27