作者介绍: 王天宜,TiDB Community 架构师。曾就职于 Fidelity Investment,Softbank Investment,拥有丰富的数据库高可用方案设计经验,对 TiDB、Oracle、PostgreSQL、MySQL 等数据库的高可用架构与数据库生态有深入研究。
如今数据库上云这一话题受到越来越多的关注,数据库作为重要的基础软件,在云原生时代将面临怎样的变革?当数据库遇上云原生,又将碰撞出什么火花?近日,在亚马逊云开发者 Meetup 上,TiDB Community 架构师王天宜分享了 TiDB 与云原生实践开发经验。
本文将从以下三个方面解读 TiDB Operator 与云原生:
- 什么是云原生数据库;
- 为什么云原生数据库 TiDB 要拥抱 Kubernetes;
- TiDB 在 AWS 上的最佳实践。
什么是云原生数据库
什么是云原生
任何技术的变革,一定是思想先行的。我认为云原生是一套应用程序在云上运行的方法论。
我们可以将云原生拆成云和原生两部分,所谓的云,必然是指应用位于云中,而不再是传统的数据中心中。比如云盘中的文件就在云中,而不是存储在本地的硬盘中。对于原生的解读,我认为是生而为云,应用程序从设计之初就需要考虑云环境,在云上以最佳姿态运行。
总而言之,云原生就是充分利用和发挥云平台的弹性 + 分布式的优势,是生在云上,长在云上,用在云上的技术。
云原生的特点
自 2013 年云原生这一概念诞生开始,其定义在不断完善。云原生有以下四个特点:
- 持续交付:大概在十年前就有了敏捷开发的概念。敏捷开发的目的是要应对用户的需求改变,做到频繁的发布,快速的交付。在某些灰度发布,金丝雀发布的场景中,可能要有几个不同的版本同时提供服务。
- 容器化:容器化使我们的开发、测试、生产环境高度统一,在调研阶段,我们也可以使用 docker compose 等命令快速搭建一个调研环境。
- 微服务:微服务是一个独立发布的应用服务,应用之间通过 rest API 进行通信,可以被独立部署、更新、扩容和重启。
- DevOps:DevOps 强调高效地协调开发与运维的关系。通过自动化部署、CI 工具快速地将应用部署到生产环境中。
云原生的本质就是发挥云计算资源池化,平台规模化等技术红利优势,创造更多业务价值。
什么是云原生数据库
云原生数据库,是一种通过云平台构建、部署和分发的数据库服务。它以 PaaS 的形式进行分发,也经常被叫做 DBaaS。相比于传统数据库,云原生数据库提供了更好的访问性和可伸缩性。
云原生数据库的特点
- 云原生数据库要有自动容错的机制,做到宕机自动迁移,故障自动隔离,保证应用的高可用性。这是云原生数据库最基本的特点。
- 云原生数据库要有很好的弹性伸缩能力,可以根据 CPU load,memory 使用率等做到自动伸缩,秒级扩容。
- 针对于弹性伸缩,要有弹性的计费方法,可以按流量支付,按资源支付,也可以是组合的套餐服务,多种定价策略,为用户实现降本增效的目标。
- 云原生数据库应该是易于管理的,提供了很好的告警监控服务,运维操作简单化,由自助运维向自动运维转化。
- 云原生数据库也应该有很好的安全隔离机制。既包含了多租户的资源隔离,也包括网络安全隔离。
这一切的特性都是为了用户的极致体验,低学习成本,低运维成本,低价格成本使用云原生数据库。
为什么说 TiDB 是一款云原生数据库
我们先来看一下TiDB 的基本架构:
- TiDB Server:负责接受客户端的连接,执行 SQL 解析和优化,最终生成分布式执行计划 。
- TiKV Server 和 TiFlash Server:用来存储数据,TiKV 是行式的存储引擎,TiFlash 是列式的存储引擎。
- PD:整个 TiDB 存储集群的源信息,负责集群的调度操作,是集群的大脑。
存储结点:
- TiKV:提供分布式事务的 key-value 行式存储引擎 。
- TiFlash:列式存储引擎,主要是为了分析类型场景进行加速。
TiDB Cluster 本身就具有高可用性与高度可扩展性,和云原生的概念是非常契合的。
为何拥抱 Kubernetes
Kubernetes 发展史
提到云原生有两个话题是无法避开的,一个是容器化,另一个是容器编排。TiDB 要以什么样的方式上云呢?这里不得不提 Kubernetes。从 Kubernetes 的发展史来看,Docker 解决了容器化的问题,而 Kubernetes 解决了容器编排的问题:
- 2013 年,Docker 项目发布,使得全操作系统的沙盒技术唾手可得;
- 2014 年,Kubernetes 项目发布,容器设计模式正式确立;
- 2017 年,Kubernetes 项目实施标准确立;
- 2018 年,Kubernetes 已经成为了云原生的操作系统。
初探 Database × Kubernetes
我在上一家公司曾经设计了这样一种架构:底层数据库使用 MariaDB、Galera、Cluster 三主的结构。MariaDB、Galera、Cluster 是一个无状态实例组成的主主复制。上层使用无状态的 ProxySQL 进行 SQL 路由,然后到不同的 MariaDB 中。中间层使用 ZK 做服务注册与发现,控制上层的 ProxySQL 和下层的 MariaDB。那么这样的结构天然就适合部署在 Kubernetes 上。
起初,我们对 DataBase 上 Kubernetes 是有所担心的,所以我们将相对轻量级的 Proxy 结点放在了 Kubernetes 中,DataBase 层还是放在物理机上管理。为了应对意外的发生,除了 Kubernetes 上部署的 proxy,实体机上也部署了 proxy 结点。所以即便是 Kubernetes 集群发生了全故障,我们也能轻松的应对这种情况。
积累了半年多的 Kubernetes 维护经验,我们决定将一套比较边缘化的集群放在 Kubernetes 上。自此,既完成了 proxy 上 Kubernetes,又完成了 DataBase 上 Kubernetes。
TiDB 要以什么样的姿态拥抱 Kubernetes
将无状态的 TiDB 结点放到 Kubernetes 上是否合适呢?其实这跟将 proxy 放到 Kubernetes 上的思路是一致的。如果有 TiDB 上 Kubernetes 的打算,那么维护一套这样的 TiDB + Kubernetes 集群应该是应对陌生环境的最佳手段,尤其是对容灾的学习以及网络的配置,都是非常有帮助的。有很多公司在上 Kubernetes 之前是这样使用 TiDB 的。
了解 TiDB 的同学应该知道,PD 底层是基于 ETCD 实现的。对于 ETCD 有没有什么好的方式上 Kubernetes 呢?因为 ETCD 是一个有状态的服务,可能不适合裸布在 Kubernetes 上。我们可以学习业内比较知名的 etcd-operator 来开发一套 pd-operator。
针对 TiKV 可能还要更复杂一些。作为一款数据库产品,存储层上 Kubernetes 一定要非常慎重。在上 Kubernetes 之前,我们可以看一下 TiKV 是如何实现的。
我们将存储层 TiKV 中的数据拆成一个个的小数据块,这些数据块在 TiKV 中称之为 Region。
为了保证正确性和可用性,每个 region 都对应一个 raft group,通过 raft log 复制实现每个 region 至少有三个副本。可以看到,至少在 TiKV 这一层,是一个有状态的服务,当一个 TiKV 结点挂掉的时候,我们没有办法简单的使用故障结点的 PV 新建一个 pod。
我们都知道,Kubernetes 判断结点故障是基于 Kubelet 服务是否能够正常上报结点状态。试想一下,如果 Kubelet 无法正常启动,但结点内的容器还是正常运,此时再将 pv 挂到其他的结点上,就会出现双写的问题。
因此,使用 Operator 管理 TiDB 的状态势在必行。
TiDB 在 AWS 上的最佳实践
为什么选择 TiDB Operator
什么是 TiDB Operator?简单的说,TiDB Operator 就是 CRD + Controller 的组合。其中 CRD 负责声明式管理,Controller 负责驱动 TiDB cluster 的显示状态向期望状态转化。比如我们对 TiDB 的集群定义是 3 个 TiDB 和 3 个 TiKV,由于某些原因 ,挂掉了一个 TiDB 结点。那么现实状态是 2 个 TiDB 结点,期望状态是 3 个 TiDB 结点。这个时候可以由 controller 去起一个新的 TiDB 结点。
CRD 中定义了很多 TiDB Cluster 的组件和生态组件的类型。比如对于 TiDB 集群定义了 TiDB Cluster 类型,对于监控定义了 TiDB Monitor 类型,对于 DM 同步组件定义了 DM Cluster 类型。
接下来看一下 TiDB-Operator 是怎么自动化维护 TiDB 集群的。
TiDB-Operator 应用实践
在部署的时候,TiDB Operator 会为每个组件选择最佳的 Kubernetes 原生对象 。PD 本质上是一个 ETCD 的组件,TiDB 是一个无状态的服务,不需要关心用什么部署最合适,TiDB Operator 已经帮我们隐藏了这些信息。PD 是基于 ETCD 开发的,需要做 peer discovery,而 Operator 会打散 TiKV 容器并且自动添加 store-label,把每个容器出自哪个可用区,哪个机房设定好,辅助 PD 实现 region 的高可用拓扑。有了 TiDB Operator,我们可以将原厂的运维经验通过代码的方式复制传播出去。只需要通过 yaml 格式的声明,就可以在 Kubernetes 上快速部署一套 TiDB Cluster。当然,仅仅创建是不够的,Operator 也提供了运维的最佳实践。
在升级的时候,我们给 TiKV 发送一个升级的请求,此时 TiKV 结点需要 shutdown 然后替换指定版本的 image。因为 TiKV 是一个有状态的服务,我们在 TiKV 结点 shutdown 之前还需要做一些操作。比如调用 PD 接口,将 region leader 从当前的 TiKV 结点驱逐,此时的 TiKV 就不接受读写请求。之后就可以顺利的重建 TiKV 容器,升级 TiKV 到指定的版本。当然,在升级结束后,我们又要调用 PD 接口将 region leader 迁回,以此往复,滚动升级。
这是一个工作正常的三副本 TiKV 结点,根据 raft 协议,需要半数以上结点存活才能对外提供服务,即最多能够容忍一个结点出现故障。
当 TiKV 1 的服务不正常,结合 PD 的 store 信息和 Kubernetes 的容器信息,通过一些 probe 的手段,我们可以获知 TiKV 是否是异常的。
那么是不是检测到故障就进行故障转移呢?其实并非如此。故障转移太快可能会因为网络资源或者 CPU 资源的抖动导致频繁的发生切换,故障转移太慢有可能降低集群的高可用性与吞吐。
此时 TiDB Operator 会进行自动化故障转移。什么情况下需要故障转移、间隔多久进行故障转移,这些都交给 TiDB Operator 进行判断, 至于如何进行故障转移,也交给 TiDB Operator 执行。
当然,在业界也流传了很多的反对声音,我们到底要不要在 Kubernetes 上部署 Database,还需要「三思」:
- 思退。我们不仅要考虑如何将实体机上的数据迁到 Kubernetes 上,也要想好退路,当 Kubernete 运维操作过于复杂,或者暂时因为一些其他原因我们无法覆盖太多的技术栈,如何从 Kubernetes 平台上撤下来, Binlog 和 TiCDC 工具是可以帮助我们的。
- 思危。多加了一层 Kubernetes 与 Operator 的技术栈,风险点也随之变高。那就意味着我们要做更多的测试。Kubernetes master 结点挂了,我们可以依然对外保持服务。当然,master 结点本身也是支持 Keepalived + Haproxy 高可用的。Node 结点挂掉了,我们可以将该结点上的 Pod 迁到其他的 Node 结点上。Kubernetes 全部挂掉了,还有本地保存的 PV 文件。即便是真的灾难性的故障导致了 PV 文件的损坏,我们还有定期的备份可供恢复。
- 思变。Kubernetes 本身是一款为变化而生的平台,在 Kubernetes 上,升级操作、扩缩容,变得更简单了。
除此之外,还请再思考两个问题:
- 你愿意相信 Kubernetes 是未来吗?
- 你准备好了吗?
如果你现在还没有准备好相关的技术栈,或者你想提前去感受 TiDB 在 Kubernetes 平台上的魅力,感受云原生的魅力,我们为你提供了基于 AWS 的 DBasS 服务 —— TiDB Cloud。使用 TiDB Cloud,对于用户来说,既可以得到可靠的数据库服务,又能享受到专业的运维服务,同时也避免了高昂的学习成本。
用户在 TiDB Cloud 上可以自由的选择用于计算的 TiDB 结点和用于存储的 TiKV & TiFlash 结点,可以真正实现按需按量使用,按需按量付款,将云原生的优势发挥到极致,降本增效。
2014 年, Gartner 在一份报告中使用混合事务分析处理 —— HTAP 一词描述新型的应用程序框架,以打破 OLTP 和 OLAP 之间的隔阂,既可以应用于事务型数据库场景,也可以应用于分析型数据库场景。
HTAP 能够实现实时业务决策。这种架构具有显而易见的优势:不但避免了繁琐且昂贵的 ETL 操作,而且可以更快地对最新数据进行分析。这种快速分析数据的能力将成为未来企业的核心竞争力之一。
有一些用户,每隔固定的时间可能需要运营人员做一些查询类的分析。这个时候我们可以分析前一天的上一个 TiFlash 结点,将数据从 TiKV 中同步到 TiFlash 结点,以供运营人员在第二天做分析。因此,在云上弹性扩缩容的特性和 HTAP 的场景天生就是高度匹配的。
以上就是关于 TiDB Operator 与 Amazon Web Service 云原生相关的实践经验,希望能够对大家有所帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。