MySQL云原生方案在携程开发测试场景中的实践

2020 年 5 月 05 日

MySQL云原生方案在携程开发测试场景中的实践

一、背景与使用场景


随着 Kubernetes 平台在容器云计算领域的一统天下,云原生 (Cloud Native) 一词也被提的越来越频繁。各类应用纷纷走上了容器化、云原生化的道路,无状态服务应用在 Kubernetes 平台上的运行,已经得到了大规模生产级别的实践认可。


相比之下,有状态应用就没有那么顺利了,特别是那些十分重要却又"历史悠久"、不是按照分布式架构理念设计的有状态服务,尤其困难。MySQL 就是其中的代表,为此我们做了诸多尝试,从一开始的 MySQL 单实例容器化使用本地存储,到计算存储分离的方案,走了一些弯路。最终在开发测试场景下找了一个合适的切入点,实现了一套计算和存储分离,以 Kubernetes Operator 为核心,以 CEPH RBD 为后端存储,以数据库版本化管理为特性的可行方案。


我们典型的使用场景是这样的:测试人员需要构造一个生产环境批量订单数据异常的测试场景, 他使用安全工具从生产环境拉取大量脱敏后的数据写入测试数据库,但只运行一次测试用例, 数据库就"脏了"。特别是每次上新功能还要回归测试一次这种场景,又要重复耗时在构造新数据库,真的是“构造 2 小时,运行 5 分钟”。


而有了这一套完整的 MySQL 实例服务后,可以快速启动任意版本的数据库实例,前面所述的痛点就彻底消失了。同时有了 MySQL 实例服务,对 CPU 内存资源的使用也可以节省一大笔,毕竟大量的测试数据库都只要以快照的形式存储在集群中即可,实际使用时可以在一两分钟内快速启动。



二、可行性方案分析和性能评估


首先要解决的是计算和存储分离的问题,如果使用容器宿主机本地磁盘存储的话,MySQL 实例必须和宿主机绑定,这就丧失了资源的灵活性,而且使用本地存储, 对于磁盘容量的规划会是个不小的问题。


我们团队早在 2015 就开始使用 CEPH 存储服务,主要是对象存储和块存储,运维经验和集群稳定性方面相对有保证。结合这一实际情况,我们选择使用了 CEPH 块存储服务作为 MySQL 容器实例的存储。


另一个考量则是受益于 Kubernetes 这个强大的平台基座,社区已经定义好了容器存储接口 (CSI),且实现了 CSI driver for CEPH (https://github.com/ceph/ceph-csi),其中 RBD 部分早已 GA,还有提供了 snapshot,resize 等功能,完全满足我们的使用场景。


为了验证 MySQL 实例后端挂载 CEPH 块存储服务能否满足开发测试环境的数据库基本使用需求,我们基于已有的硬件情况, 做了两个场景的性能压测。主要是对比使用本地 SAS 磁盘存储的 MySQL 实例和使用 CEPHRBD 的 MySQL 实例,在性能方面是否有明显差异。其次则是测试 MySQL 实例后端挂载 CEPH RDB 存储的性能上限。


基本硬件信息如下 :


使用本地磁盘的容器宿主机CEPH 集群所用物理机
CPU2Socket / 32Core / 64Thread Intel® Xeon® CPU E5-2650 v3 @ 2。30GHz2Socket / 32Core / 64Thread Intel® Xeon® Gold 6130 CPU @ 2。10GHz
Memory188GB256GB
Disk2*300G,10K(1)
8*900G,10K(10)
2*240G,SSD(10)
12*8T,7。2K(JBOD)


构造了两个测试场景,使用 sysbench 执行压测:


sysbench 参数如下:


参数名参数值备注
oltp-tables-count64测试表的个数64张
oltp-table-size1000000单表数据量100W
num-threads[8,16,32,64,128,256]测试线程数
times of test3每个线程下的测试次数
warmup time120预热时间,避免冷数据对测试结果的影响
max-time120每次压测耗时120s,重复3次


测试场景 A:分别压测 MySQL docker with CEPH RBD 和 MySQL docker with local disk,并发数 threads 从低到高,8→256,对比 QPS。


测试场景 B:同时压测五个 MySQL docker with CEPH RBD,保持并发数恒定在 256,观察 CEPH 集群 IOPS 和最终 MySQL QPS。



测试场景 A 结果:


OLTP 模式压测 MySQL,对于磁盘主要是随机读写操作,CEPH RBD 使用了 SSD 作为缓存盘,随机写速度约 110MB/s,而本地机械磁盘随机写速度只有 48.6MB/s,所以最终性能指标 QPS,使用了 CEPH RBD 的容器实例反而更好。


测试场景 B 结果:


压测 CEPH RBD 集群的磁盘 IO 上限,约算测试环境的集群能提供的 QPS 上限为 80K。


结论是在开发测试环境使用 CEPH RBD 为后端存储的 MySQL 实例服务,不会比使用本地磁盘更差,可以满足应用功能测试的性能需求。


三、MySQL 容器化实例方案及实现细节


介绍一下这套方案的简单架构设计和基本工作原理,如下图:



所有相关服务都部署在 Kubernetes 集群上,这里只重点描述我们开发的 MySQL-Operator 和自定义资源 CRD。关于 CSI driver 以及 provisioner,attacher, snapshotter 等组件都是使用原生官方镜像,在这里不做详细表述,可以参考文档(https://kubernetes-csi.github.io/docs/)。


MySQL-Operator 作为自定义的控制器,管理两种自定义资源(CRD),通过 Kube-api 为上层的 PAAS 平台和 CI 等系统提供 MySQL 实例服务。两个 CRD 分别是 MySQLInstance 和 DatabaseSnapshot。其中 MySQLInstance 是基于 StatefulSet 的一层封装,添加了一些 metadata,MySQL-Operator 只需要根据 MySQLInstance 的声明来创建对应的 StatefulSet 和 PVC 即可, 所以 MySQLInstance 暴露出来的 spec 并不多,大致如下:



根据 spec.init 的类型,MySQLInstance 既可以是基于生产数据库 Schema 生成的空数据库实例,也可以是基于已有的 DatabaseSnapshot 生成的带有基准数据的实例。


在创建的过程中,MySQL-Operator 会为这个 MySQLInstance 申请域名,同步账户密码以及 Schema 等。一个 MySQLInstance 的整个生命周期在有限的七个状态之间跳转。需要特别提一下 Paused 状态,当基于该实例的 DatabaseSnapshot 创建时,MySQLInstance 会进入 Paused 状态。状态机如下:



另一个 CRD,DatabaseSnapshot 则是基于 VolumeSnapshot 的封装,其中 VolumeSnapshot 是 Kubernetes 官方定义的持久卷快照声明(https://kubernetes.io/zh/docs/concepts/storage/volume-snapshots/)。MySQL-Operator 根据它的声明来关联 MySQLInstance 和 PVC 即可。



由于 CEPH RBD 的读写独占模式 RWO(read write once), 我们为 DatabaseSnapshot 定义了两个常态 InUse 和 Ready。简单来讲就是一个数据库快照同一时间只允许一个数据库实例使用,并且 DatabaseSnapshot 在创建过程中需要暂停对应的 MySQLInstance,状态机如下:



四、小结与展望


在有了 MySQLIntance 服务之后,数据库的版本管理变得和代码版本管理一样灵活。特别是重复构造测试数据的场景,节省了大量的时间和管理成本。另外用户也不再需要长期占用计算资源,仅在有使用需求时即可快速创建 MySQLInstance,有效提高了整体容器宿主机资源的使用率。除此之外,上层 CI/CD 平台服务也可以通过 Kube API 调用的方式来管理这两种 CRD,进一步提升测试自动化程度。


一般来说,应用云原生化完成后最重要的是获得两个能力:弹性和分布式,目前我们的这套方案落地于 Kubernetes 平台,释放出了一部分平台计算和存储的弹性,让用户对于数据库实例有了更多的选择和灵活管理的能力。


何谓云原生(Cloud Native), 字面上早已经有了明确的定义(https://github.com/cncf/toc/blob/master/DEFINITION.md),但是在工程实践中,基于 Kubernetes 这个巨大的平台,仍然有大量的宝藏等着我们去持续探索挖掘。


作者介绍


Alex,专注于云计算领域数年,目前主要从事容器云平台的建设,推进各类基础设施服务的云原生化。


小石川,目前主要从事容器云平台监控系统建设,对分布式、性能以及优化感兴趣。


本文转载自公众号携程技术(ID:ctriptech)。


原文链接


https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&mid=2697269592&idx=2&sn=2ebc6e51909422ae573fafcf943500c7&chksm=8376ee6cb401677a803bd57cb8e3859ee0a635ce965449d7cf3b81b6a2b7cb1f14b973580da2&scene=27#wechat_redirect


2020 年 5 月 05 日 14:051608

评论 1 条评论

发布
用户头像
作者你好,有些问题想问下你,可以有你的联系方式吗
2020 年 10 月 12 日 11:48
回复
没有更多评论了
发现更多内容

关于 Windows 10 2020 年 5 月更新

FeiLong

工作那么久,你还具备学习能力么?

夜来妖

学习 程序员成长

架构之路

强哥

极客大学架构师训练营

戒掉手机吧

鼎玉谷

人生 手机 时间 浪费 控制

ARTS(2020-05-25/2020-05-31)

天行者

正确阅读

RocketMQ - 如何实现事务消息

Java收录阁

RocketMQ

直面一个复杂世界

史方远

读书笔记 个人成长 随笔杂谈

你想活出怎样的人生

Janenesome

读书笔记 思考

N皇后问题

孙苏勇

算法 DFS 深度优先搜索

YARN的架构设计和工作原理(通俗易懂)

Shockang

hadoop YARN

draw.io-取代visio的流程图绘制工具

Rice嵌入式开发技术分享

chrome vscode 写文章神器 draw.io

不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...

JackTian

GitHub 学习 程序员 编码 开源项目

一款霸榜 GitHub 的开源 Linux 资源监视器!

JackTian

GitHub Linux 开源项目 bashtop 资源监视器

Go: 使用GODEBUG改善Goroutine的使用

陈思敏捷

go golang debug 协程

B端产品经理养成记(1):业务场景

涛哥

产品经理 需求 产品开发

钢铁侠马斯克之仰望星空

池建强

创业 马斯克 Space X

使用Kotlin语言初始化数组

mengxn

数组 kotlin 初始化

1 ARTS 2020-05-31

3.141516

LeetCode

Linux命令-df

一周思进

浅说Docker基础知识与核心原理

岿然独存5

go Docker 软件

程序猿邂逅相亲妹,默默无语两行泪

码农神说

程序员 相亲

Kafka系列9:面试题是否有必要深入了解其背后的原理?我觉得应该刨根究底(上)

z小赵

大数据 kafka 实时计算

ARTS Week2

丽子

ARTS打卡第一周5.25-5.31

我笔盒呢

RocketMQ - 高可用设计

Java收录阁

RocketMQ

除了直接看余额,谁更有钱还能怎么比(二)

石君

去中心 零知识证明

B端产品经理养成记(2):用户故事

涛哥

产品经理 需求 产品开发

【5月】本月读书学到了什么

Neco.W

创业 读书感悟 阅读量

做PO难,难于上青天

刘华Kenneth

敏捷 产品经理 决策 PO

转行程序员浅谈进程间的socket通信

WB

Linux socket 转行程序员

工厂模式(四)泛型工厂之MyBatis Mapper代理

LSJ

Java 设计模式 泛型 工厂注册中心

MySQL云原生方案在携程开发测试场景中的实践-InfoQ