容器化 RDS:借助 CSI 扩展 Kubernetes 存储能力

容器化RDS系列文章:

RDS 并不是新生事物,新鲜的是通过容器技术和容器编排技术构建 RDS。对金融客户而言,他们有强烈拥抱 Docker 和 Kubernetes 的愿望,但可用性是尝试新技术的前提。存储是持久化应用的关键资源,它并不性感,却是 Monolithic 应用走向 Cloud-Native 架构的关键。Kubernetes 存储子系统已经非常强大,但是还欠缺一些基础功能,譬如支持 Expand Volume(部分 Storage Vendor 支持)和 SnapShot。本文尝试从我们的实现分享如下几个内容:

  • 现有 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

不可谓不丰富。Kubernetes 以插件化的方式支持存储厂商(后面简称 SP.)。SP. 通过实现 Kubernetes 存储插件接口(后面简称 Volume Plugin Interface)的方式提供自己的存储驱动(后面简称 Volume Driver)。

  • 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 中平滑迁移。

Note:Container Storage Interface 定义[2]。

还有一个附带的好处是,一旦 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,重点如下:

service Controller {
 rpc CreateVolume (CreateVolumeRequest)
   returns (CreateVolumeResponse) {}
……
 rpc RequiresFSResize (RequiresFSResizeRequest)
   returns (RequiresFSResizeResponse) {}
 rpc ControllerResizeVolume (ControllerResizeVolumeRequest)
   returns (ControllerResizeVolumeResponse) {}
}
service Node { rpc NodeStageVolume (NodeStageVolumeRequest)
   returns (NodeStageVolumeResponse) {}
……
 rpc NodeResizeVolume (NodeResizeVolumeRequest)
   returns (NodeResizeVolumeResponse) {}
}

原 csi.proto[4] 和设计文档[5]。

通过 CSI 提供的编译功能,用新编译出来的 csi.pb.go 替换 external-provisioner、external-attacher、csi-driver 和 kubernetes 中现有的 csi.pb.go。

Note:CSI 编译时默认使用最新版本的 protobuf 第三方包,这可能会导致 csi.pb.go 跟 Kubernetes 中依赖的 protobuf 第三方包不兼容,编译时需要切换到统一版本。

扩展 CSI Plugin

在现有 CSI Plugin 基础上,实现接口 ExpandableVolumePlugin:

type ExpandableVolumePlugin interface {
VolumePlugin
ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error)
RequiresFSResize() bool
}

仿照 csiMountMgr 的方式调用 CSI Driver 中的 ControllerResizeVolume。

基于 CSI Spec 实现 Storage Driver

  • 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”。

相关链接:

  1. https://github.com/kubernetes/kubernetes/blob/afa68cc28749c09f8655941b111e46d85689daf8/pkg/volume/plugins.go#L95
  2. https://github.com/container-storage-interface/spec/blob/master/spec.md
  3. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/container-storage-interface.md
  4. https://github.com/container-storage-interface/spec/blob/master/csi.proto
  5. https://docs.google.com/document/d/1kVrNwA2f4ite8_9QvCy-JQA_00hxGGMdER3I84dUDqQ/edit?usp=sharing

| 作者简介

熊中哲,沃趣科技产品及研发负责人

曾就职于阿里巴巴和百度,超过10年关系型数据库工作经验,目前致力于将云原生技术引入到关系型数据库服务中。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏nimomeng的自我进阶

《2016中国移动开发者大会》参会笔记

总的来说,2016年的综合场(第一天上午)感觉讲的一般,身边的人吐槽也比较多。不过相比之下,iOS场干货就比较多了,演讲者基本都是圈内大V,包括喵神,Sunny...

712
来自专栏SAP最佳业务实践

SAP最佳业务实践:SD–按计划协议的销售(231)-3发货

一、VL10E交货到期清单 在此活动中,可以处理交货到期清单,以便创建交货凭证。 角色仓库文员 后勤 → 后勤执行 → 外向处理→外向交货的发货→ 外向交货→ ...

3836
来自专栏Jerry的SAP技术分享

阿里云上到底能运行SAP哪些产品?

本文主要内容大部分来源于SAP已经发布的note:

1895
来自专栏SAP最佳业务实践

SAP最佳业务实践:按库存生产(145)-4分组件的生产订单处理

分组件的生产订单处理 1、MD04 CO40创建生产订单 日常的物料需求计划运行会为内部生产的零件创建计划订单。到达计划转换日期时,系统将计划订单转换为生产订单...

2024
来自专栏斑斓

大数据流处理平台的技术选型参考

选择太多,是一件好事情,不过也容易乱花渐欲迷人眼。倘若每个平台(技术)都去动手操练一下,似乎又太耗时间。通过阅读一些文档,可以帮我们快速做一次筛选。在将选择范围...

3075
来自专栏SDNLAB

OpenDaylight系列文章(三):OpenDaylight初窥(中篇)之OpenDaylight的系统架构

如果说前面的文章是铺垫的“地毯”,那么从本篇开始OpenDaylight就算是正式踩着地毯走来了。在本篇文章中将会给大家简要介绍一下OpenDaylight的系...

34910
来自专栏猿天地

Spring Cloud如何提供API给客户端

现在越来越多的公司开始拥抱Spring Cloud了,很多Java方向的同学也开始积极的学习Spring Cloud,其实这边还有一个问题就是说:虽然大家学了E...

3397
来自专栏phodal

Stepping.js——两步完成前后端分离架构设计

一周前,参加了公司的一个架构设计与建模的工作坊——『事件风暴』。从某种意义上来说,这是一个关于架构设计与软件建模的工作坊。于是便闪现了一个灵感,便有了 Step...

1999
来自专栏性能与架构

这个场景更适合使用NoSQL

NoSQL的一个主要类型就是文档型NoSQL,例如 MongoDB,使用 json 结构存储数据,不需要事先定义好记录结构,自由添加删除记录中的某项,非常灵活 ...

3824
来自专栏更流畅、简洁的软件开发方式

关于Int自增字段和GUID字段的性能测试。只有测试,没有分析,呵呵

      最近有两篇关于GUID和Int自增的文章,我是一直使用Int自增的,不习惯使用GUID,感觉GUID很麻烦,用着不方便,性能也比不上Int自增。但是...

20610

扫码关注云+社区