容器化 RDS:借助 CSI 扩展 Kubernetes 存储能力
容器化RDS:计算存储分离架构下的"Split-Brain"
容器化RDS:计算存储分离还是本地存储?
容器化RDS:你需要了解数据是如何被写"坏"的
容器化RDS:PersistentLocalVolumes和VolumeScheduling
现有 Kubernetes 存储插件系统问题
Container Storage Interface(CSI)
基于CSI 和分布式文件系统实现在 MySQL 的 Volume 动态扩展
对 CSI 的展望
原名 | 简称 |
容器编排系统 | CO. |
存储提供者 | SP. |
存储插件接口 | Volume Plugin Interface |
存储驱动 | Volume Driver |
容器存储接口 Container Storage Interface | CSI |
如有遗漏,不吝赐教。
现有 Kubernetes 存储插件系统问题
可供选的容器编排系统(后面简称 CO.)不少,除去 Kubernetes 还有 Mesos、Swarm、Cloud Foundry。以 Kubernetes 为例,其通过 PersistentVolume 抽象对以下存储的支持:
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
VolumePlugin
PersistentVolumePlugin
DeletableVolumePlugin
ProvisionableVolumePlugin
ExpandableVolumePlugin
Provisioner
Deleter
以上接口并不需要全部实现,其中 VolumePlugin[1] 是必须实现的接口。
系统架构图如下:
这种方式为 Kubernetes 提供了丰富的存储支持列表,但是在具体实现上,SP. Volume Driver 代码也在 Kubernetes 代码仓库(又叫in-tree),它带来几个显著的问题。
从 Kubernetes 的角度看:
需要在 Kubernetes 中给各个 SP. 赋权以便他们能够提交代码到仓库;
Volume Driver 由各个 SP. 提供,Kubernetes 的开发者并不了解每个细节,导致这些代码难于维护和测试;
Kubernetes 的发布节奏和各位 SP. Volume Driver 的节奏并不一致,随着支持的 SP. 增多,沟通、维护、测试成本会越来越高;
这些 SP. Volume Driver 并不是 Kubernetes 本身需要的。
从 SP. 的角度看:
提交一个新特性或者修复 bug,都需要提交代码到 Kubernetes 仓库,在本地编译 Kubernetes 的都知道,这个过程是很痛苦的,这对 SP. 而言是完全不必要的成本。
Container Storage Interface(CSI)
基于这些问题和挑战,CO 厂商提出 Container Storage Interface 用来定义容器存储标准,它独立于 Kubernetes Storage SIG,由 Kubernetes、Mesos、Cloud Foundry 三家一起推动。个人理解它有如下2个核心目标:
提供统一的 CO. 和 SP. 都遵循的容器存储接口。
一旦 SP. 基于 CSI 实现了自身的 Volume Driver,即可在所有支持 CSI 的 CO 中平滑迁移。
还有一个附带的好处是,一旦 CO. 和 SP. 都遵循 CSI,就便于将 Volume Driver 解耦到 Kubernetes 的外部(又叫 out-of-tree)。Kubernetes 只用维护 CSI 接口,不用再维护 SP. 的具体实现,维护成本大大降低。
CSI 优化下的架构图:
可以看到,Kubernetes 和 SP. 从此泾渭分明,但事情并没有那么简单,借用一位大神说的:The performance improvement does not materialize from the air, it comes with code complexity increase.和性能一样,扩展性和解耦也不是凭空出现。上图可进一步细化成下图:
明显的变化有:
Controller Plane、Kubelet 不再直接与 Volume Driver 交互,引入 external-provisioner 和 external-attacher 完成该工作;
SP. Volume Driver 会由独立的容器运行;
为了实现 external-provisioner、external-attacher 和 SP. Volume Driver 的交互引入 gRPC 协议(标红箭头)。
还有一些其他的变化:
在 Kubernetes 端 引入新的对象:
CSIPersistentVolumeSource:该类型 PV 由 CSI Driver 提供
VolumeAttachment:同步 Attach 和 Dettach 信息
引入新的名称:
mount/umount:NodePublishVolume/NodeUnpublishVolume
attach/dettach:ControllerPublishVolume/ControllerUnpublishVolume
Note:Kubernetes 适配 CSI 设计文档[3]。
可见,为了达到这个目标,相比原来复杂不少,但收益巨大,Kubernetes 和 SP. 的开发者可以专注于自身的业务。
即便如此,在现有的 CSI 上做扩展有时也在所难免。
基于 CSI 和分布式文件系统实现在 MySQL 上的 Dynamically Expand Volume
Kubernetes 存储子系统已经非常强大,但是还欠缺一些基础功能,譬如支持 Expand Volume (部分 Storage Vendor 支持)和 SnapShot,尤其是 Expand Volume,这是必须的功能,因为随着业务的变化,容量的增加在所难免,一旦容量接近阈值,若以迁库的方式扩展存储容量,成本太高。但现状并不乐观,Kubernetes 1.10.2 使用 CSI 0.2.0,其并不包含 Expand Volume 接口,这意味着即便底层的存储支持扩容,Kubernetes 也无法使用该功能,所以我们需要做点 hard code 实现该功能:
扩展 CSI Spec
扩展 CSI Plugin
基于 CSI Spec 实现 Storage Driver
演示
其他
扩展 CSI Spec
前面提到,在 CSI 中引入了 gRPC,个人理解在 CSI 的场景有如下3个优点:
基于 protobuf 定义强类型结构,便于阅读和理解
通过 stub 实现远程调用,编程逻辑更清晰
支持双工和流式,提供双向交互和实时交互
网络上介绍 gRPC 和 protobuf 的文章很多,这里不赘述。
通过 gRPC,实现 CSI 各个组件的交互。为支持 expand volume 接口,需要修改 csi.proto,在 CSI 0.2.0 的基础上添加需要的 rpc,重点如下:
CSI Driver 实现如下所有接口:
CreateVolume
DeleteVolume
ControllerPublishVolume
ControllerUnpublishVolume
ValidateVolumeCapabilities
ListVolumes
GetCapacity
ControllerGetCapabilities
RequiresFSResize
ControllerResizeVolume
这里涉及到比较复杂的调试和适配工作,还有一些其他的工作:
定义 CSI 对应的 StorageClass,并设置 allowVolumeExpansion 为 true
启用 Feature Gates:ExpandPersistentVolumes
新增 Admission Control:PersistentVolumeClaimResize
……
通过扩展 Container Storage Interface 0.2.0,我们在Kubernetes 1.10.2 上实现了在线扩容文件系统扩容。对 MySQL 实例制造两种类型工作负载:
通过键值更新和查询
批量数据加载数据
读数一:MySQL QPS 在正常波动范围内;
读数二:持续批量加载数据,MySQL 文件系统容量不断变大;
读数三:在20分钟内,在线动态扩容 Volume 和 Filesystem 2 次, 过程高效平滑。
其他
工作到这里基本结束,要改动的地方不少,客观上并不简单。如果没有 Kubernetes 和 CSI,难度会更大。
通过此方法可以完成对其他 Storage Vendor 的扩展,譬如 FCSan、iSCSI。
对 CSI 的展望
目前 CSI 已发展到 0.2.0,0.3.0 也发布在即。
0.3.0 中呼声最高的特性是 Snapshot。借助该功能,可以实现备份和异地容灾。但是为了实现该功能,在 Kubernetes 现有的 Control-Plane 上还要添加新的 Controller,客观上,复杂度会进一步提高。同时,部分 in-tree Volume Driver 已通过 CSI 迁移到外部,考虑到 Kubernetes 整体的发布节奏和 API 的稳定性,个人觉得节奏不会太快。
除此之外,CSI 可能还有更多工作要做。以一个高可用的场景为例,当一个 Node 发生故障时,CO 触发 Unmount->Dettach->Attach->Mount 的流程,配合 Pod 的漂移,借助 CSI 定义的接口,Unmount、Dettach、Attach、Mount 由SP. 自身现实,但是 Unmount->Dettach->Attach->Mount 的流程还是有 CO 控制,这个流程并不标准,但是对 workload 又至关重要。
又想起<人月神话>中的那句 "No Silver Bullet"。
相关链接:
https://github.com/kubernetes/kubernetes/blob/afa68cc28749c09f8655941b111e46d85689daf8/pkg/volume/plugins.go#L95
https://github.com/container-storage-interface/spec/blob/master/spec.md
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/container-storage-interface.md
https://github.com/container-storage-interface/spec/blob/master/csi.proto
https://docs.google.com/document/d/1kVrNwA2f4ite8_9QvCy-JQA_00hxGGMdER3I84dUDqQ/edit?usp=sharing
| 作者简介
熊中哲,沃趣科技产品及研发负责人
曾就职于阿里巴巴和百度,超过10年关系型数据库工作经验,目前致力于将云原生技术引入到关系型数据库服务中。