Cloud + TiDB 技术解读

做者:邓栓
来源:细说云计算算法

做为一款定位在 Cloud-native 的数据库,现现在 TiDB 在云整合上已取得了阶段性的进展。日前 Cloud TiDB 产品在 UCloud 平台正式开启公测,TiDB 弹性伸缩的特性在 Cloud 提供的基础设施支持下发挥的淋漓尽致。在感觉云数据库魅力的同时,让咱们来一探究竟,看一下 TiDB 与 Cloud 背后的技术秘密。数据库

TiDB 的架构

首先仍是要先从 TiDB 的架构提及,TiDB 和传统的单机关系型数据库有什么不一样?相信长期以来一直关注 TiDB 的同窗都比较了解了,但这里仍是科普一下。TiDB 做为一个开源的分布式数据库产品,具备多副本强一致性的同时可以根据业务需求很是方便的进行弹性伸缩,而且扩缩容期间对上层业务无感知。TiDB 的主体架构包含三个模块,对应 GitHub 上面 PingCAP 组织下的三个开源项目,TiDB / TiKV / PD:后端

  • TiDB 主要是负责 SQL 的解析器和优化器,它至关于计算执行层,同时也负责客户端接入和交互;安全

  • TiKV 是一套分布式的 Key-Value 存储引擎,它承担整个数据库的存储层,数据的水平扩展和多副本高可用特性都是在这一层实现;网络

  • PD 至关于分布式数据库的大脑,一方面负责收集和维护数据在各个 TiKV 节点的分布状况,另外一方面 PD 承担调度器的角色,根据数据分布情况以及各个存储节点的负载来采起合适的调度策略,维持整个系统的平衡与稳定。架构

上面的这三个模块,每一个角色都是一个多节点组成的集群,因此最终 TiDB 的架构看起来是这样的。负载均衡

架构图.png

因而可知,分布式系统自己的复杂性致使手工部署和运维的成本是比较高的,而且容易出错。传统的自动化部署运维工具如 Puppet / Chef / SaltStack / Ansible 等,因为缺少状态管理,在节点出现问题时不能及时自动完成故障转移,须要运维人员人工干预。有些则须要写大量的 DSL 甚至与 Shell 脚本一块儿混合使用,可移植性较差,维护成本比较高。less

TiDB 与 Kubernetes 的整合历程

在云时代,容器成为应用分发部署的基本单位,而谷歌基于内部使用数十年的容器编排系统 Borg 经验推出的开源容器编排系统 Kubernetes 成为当前容器编排技术的主流。做为 Cloud Native Database,TiDB 选择拥抱容器技术,并与 Kubernetes 进行深度整合,使其能够很是方便地基于 Kubernetes 完成数据库的自动化管理。运维

Kubernetes 项目能够说是为 Cloud 而生,利用云平台的 IaaS 层提供的 API 能够很方便的和云进行整合。这样咱们要作的事情就很明确了,只要让 TiDB 与 Kubernetes 结合的更好,进而就实现了和各个云平台的整合, 使得 TiDB 在云上的快速部署和高效运维成为现实。分布式

Kubernetes 最先是做为一个纯粹的容器编排系统而诞生的,用户部署好 Kubernetes 集群以后,直接使用其内置的各类功能部署应用服务。因为这个 PaaS 平台使用起来很是便利,吸引了不少用户,不一样用户也提出了各类不一样的需求,有些特性需求 Kubernetes 直接在其核心代码里面实现了,可是有些特性并不适合合并到主干分支,为知足这类需求,Kubernetes 开放出一些 API 供用户本身扩展,实现本身的需求。当前 Kubernetes 已经发展到 v1.8,其内部的 API 变得愈来愈开放,使其更像是一个跑在云上的操做系统。用户能够把它看成一套云的 SDK 或 Framework 来使用,并且能够很方便地开发组件来扩展知足本身的业务需求。对有状态服务的支持就是一个颇有表明性的例子。

Kubernetes 项目最先期只支持无状态服务 (Stateless Service) 来管理的,无状态服务经过 ReplicationController 定义多个副本,由 Kubernetes 调度器来决定在不一样节点上启动多个 Pod,实现负载均衡和故障转移。对于无状态服务,多个副本对应的 Pod 是等价的,因此在节点出现故障时,在新节点上启动一个 Pod 与失效的 Pod 是等价的,不会涉及状态迁移问题,于是管理很是简单。可是对于有状态服务 (Stateful Service),因为须要将数据持久化到磁盘,使得不一样 Pod 之间不能再认为成等价,也就不能再像无状态服务那样随意进行调度迁移。Kubernetes v1.3 版本提出 PetSet 的概念用来管理有状态服务并于 v1.5 将其改名为 StatefulSet。StatefulSet 明肯定义一组 Pod 中每一个的身份,启动和升级都按特定顺序来操做。另外使用持久化卷存储 (PersistentVolume) 来做为存储数据的载体,当节点失效 Pod 须要迁移时,对应的 PV 也会从新挂载,而 PV 的底层依托于分布式文件系统,因此 Pod 仍然能访问到以前的数据。同时 Pod 在发生迁移时,其网络身份例如 IP 地址是会发生变化的,不少分布式系统不能接受这种状况。因此 StatefulSet 在迁移 Pod 时能够经过绑定域名的方式来保证 Pod 在集群中网络身份不发生变化。

然而现实中一些分布式系统更为复杂,StatefulSet 也显得捉襟见肘。举例来讲,某些分布式系统的节点在加入集群或下线时还须要作些额外的注册和清理操做,或者滚动升级要考量版本兼容性等。基于这个缘由 CoreOS 公司提出了 Operator 概念,并实现了 etcd-operator 和 prometheus-operator 来管理 Etcd 和 Prometheus 这样的复杂分布式系统。用户能够开发本身的 Operator,在 Kubernetes 之上实现自定义的 Controller,将有状态服务的领域特定的运维知识编码进去,从而实现对特定分布式系统的管理。同时 Operator 自己也是跑在 Kubernetes 中的一组 Pod(deployment),对 Kubernetes 系统并没有侵入性。

TiDB 系列组件及其做用

针对 TiDB 这种复杂的分布式服务,咱们开发了 tidb-operator 等一系列组件,来管理 TiDB 集群实例在 Kubernetes 平台上的建立、销毁、扩缩容、滚动升级和故障转移等运维操做。同时在上层封装一个 tidb-cloud-manager 组件,提供 RESTful 接口,实现与云平台的控制台打通。这样也就实现了一个 DBaaS (数据库即服务)架构的基本形态。

因为 TiDB 对磁盘 I/O 有比较高的要求,经过 PV 挂载网络盘性能上会有明显的性能损耗。另外 TiKV 自己维护了数据多副本,这点和分布式文件系统的多副本是有重复的。因此咱们要给 Pod 上挂载本地磁盘,而且在 Kubernetes 上面把 Local PV 管理起来,做为一种特定的资源来维护。Kubernetes 长期以来官方一直没有提供 Local PV 支持,本地存储只支持 hostPath 和 emptyDir 两种方式。其中 hostPath 的生命周期是脱离 Kubernetes 管理的,使用 hostPath 的 Pod 销毁后,里面的数据是不会被自动清理,下次再挂载 Pod 就会形成脏数据。而 emptyDir 更像一个临时磁盘,在 Pod 重建时会被清理重置,不能成为持久化 PV 来使用。为此咱们开发了一个 tidb-volume-manager 组件,用于管理 Kubernetes 集群中每台物理主机上的本地磁盘,而且将其暴露成一种特殊的 PV 资源。结合 Operator 在部署 TiDB 节点时会参考 Local PV 资源的状况来选择特定的节点来部署,分配一个空的 Local PV 和 Pod 绑定。而当 Pod 销毁时候会根据具体状况来决定是否结束 Local PV 的生命周期,释放掉的 Local PV 再经历一个 gc 周期后,被 tidb-volume-manager 回收,清理其盘上数据等待再次被分配使用。

2.png

将这些组件整合起来,就造成了上图描述了 Cloud TiDB 的整体架构,在 Kubenetes 管理的集群之上经过 tidb-operator 等组件来针对性的调配和使用集群资源,从而实现 TiDB 集群实例的生命周期管理。经过这种方式,来实现 TiDB 分布式数据库和云平台的整合。接下来,咱们再针对 Cloud TiDB 的关键特性和实现细节分别进行解读。

自动化运维

数据库产品上云的一个先决条件是能实现自动化的运维管理,不然在云上靠手工运维几乎是不现实的。咱们首先用 Kubernetes 将云平台的主机资源管理起来,组成一个大的资源池。而后再经过 tidb-opeartor 及 tidb-cloud-manager 等组件来自动化完成 TiDB 实例的一键部署、扩容缩容、在线滚动升级、自动故障转移等运维操做。

  • 首先拿集群建立来讲。前面提到过,TiDB 包含三大核心组件:TiDB / TiKV / PD,每一个服务又都是一个多节点的分布式结构。服务和服务之间的启动顺序也存在依赖关系。此外,PD 节点的建立和加入集群方式和 etcd 相似,是须要先建立一个单节点的 initial 集群,后面加入的节点须要用特殊的 join 方式,启动命令上都有差异。有一些操做完成后还须要调用 API 进行通知。Kubernetes 自身提供的 StatefulSet 是很难应付这种复杂的部署,因此须要 tidb-operator 中实现特定的 Controller 来完成这样一系列的操做。而且结合 Kubernetese 强大的调度功能,合理的规划和分配整个集群资源,尽可能让新部署的 TiDB 实例节点在集群中均匀分布,最终经过 LB 暴露给对应的租户使用。

  • 在线升级也是相似。因为 TiKV / PD 的 Pod 挂载的是本地存储,并不能像云平台提供的块存储或网络文件系统那样能够随意挂载。若是 TiKV / PD 迁移到其它节点,至关于数据目录也被清空,因此必须保证 TiKV / PD 的 Pod 在升级完成后仍然可以调度在原地,这也是要由 tidb-operator 的 Controller 来保证。TiDB 的数据副本之间由 Raft 算法来保证一致性,所以当集群中某一个节点暂时断开能够不影响整个服务的。因此在集群升级的过程当中,必须严格按照服务的依赖关系,再依次对 Pod 进行升级。

  • 当节点出现故障时,一样是因为挂载本地数据盘的缘由,也不能像 StatefulSet 那样直接把 Pod 迁移走。当 TiDB Operator 检测到节点失效,首先要等必定的时间确认节点不会再恢复了,开始迁移恢复的操做。首先调度选择一个新节点启动一个 Pod, 而后通知 TiDB 将失效的节点放弃掉,并将新启的 Pod 加入集群。后面会由 TiDB 的 PD 模块来完成数据副本数的恢复,以及数据往新节点上进行搬移,从而从新维持集群内数据平衡。

以上只是列举了 TiDB 几种典型的运维操做流程,实际生产上运维还有不少 case 须要考虑,这些都以程序的方式实如今 tidb-operator 里面。借助 Kubernetes 和 tidb-operator 来代替人工,高效的完成 TiDB 数据库在云平台上的复杂运维管理。

动态扩缩容

弹性水平伸缩是 TiDB 数据库最主要的特性之一。在大数据时代,人们对数据存储的需求在快速膨胀。有时候用户很难预估本身的业务规模的增加速度,若是采用传统的存储方案,可能很快发现存储容量达到了瓶颈,而后不得不停机来作迁移和完成扩容。若是使用 Cloud TiDB 的方案,这个过程就很是简单,只须要在 Cloud 控制台上修改一下 TiDB 的节点数量,很快就能完成扩容操做,期间还不会影响业务的正常服务。

那么在 Cloud 后台,一样借助 Kubernetes 和 tidb-operator 的能力来完成 TiDB 增减节点操做。Kubernetes 自己的运做是基于一种 Reconcile 的机制。简单来讲当用户提交一个新的请求,好比指望集群里面跑 5 个 TiKV 节点,而目前正在跑的只有 3 个,那么 Reconcile 机制就会发现这个差别,首先由 Kubernetes 的调度器根据集群总体资源状况,并结合 TiDB 节点分配的亲和性原则和资源隔离原则来分配节点。另外很重要一点就是选择有空闲 Local PV 的机器来建立 Pod 并进行挂载。最终经过 tidb-operator 将 2 个节点加入 TiDB 集群。

对于缩容的过程也是相似。假如数据库存储的总数据量变少,须要减小节点以节省成本。首先用户经过云控制台向后端提交请求,在一个 Reconciling 周期内发现差别,tidb-operator 的 Controller 开始通知 TiDB 集群执行节点下线的操做。安全下线多是个比较长的过程,由于期间须要由 PD 模块将下线节点的数据搬移到其余节点,期间集群均可以正常服务。当下线完成,这些 TiKV 变成 tombstone 状态。而 tidb-operator 也会通知 Kubernetes 销毁这些 Pod,而且由 tidb-volume-manager 来回收 Local PV。

资源隔离

资源隔离也是云上用户关心的一个问题。尤为是数据库这类应用,不一样租户的数据库实例,甚至一个租户的多套数据库实例,都跑在一套大的 Kubernetes 管理的集群上,相互间会不会有资源的争抢问题,某个实例执行高负载的计算任务时,CPU、内存、I/O 等会不会对同台机器上部署的其余实例产生影响。其实容器自己就是资源隔离的一个解决方案,容器的底层是 Linux 内核提供的 cgroups 技术,用于限制容器内的 CPU、内存以及 IO 等资源的使用,并经过 namespace 技术实现隔离。而 Kubernetes 做为容器编排系统,可以根据集群中各个节点的资源情况,选择最优的策略来调度容器。同时 tidb-operator 会根据 TiDB 自身的特性和约束,来综合决策 TiDB 节点的调度分配。举例来讲,当一个 Kubernetes 集群横跨多个可用区,用户申请建立一个 TiDB 集群,那么首先根据高可用性原则,将存储节点尽可能分配到不一样的可用区,并给 TiKV 打上 label。那么同一个可用区内也尽可能不把多个 TiKV 部署到相同的物理节点上,以保证集群资源最大化利用。此外,每一个 Local PV 也是一块独立的磁盘,每一个 TiKV 的 Pod 分别挂载不一样的盘,因此 I/O 上也是彻底隔离的。Kubernetes 还能够配置 Pod 之间的亲和性(affinity)和反亲和性(anti-affinity),例如 TiKV 和 TiDB 之间咱们能够经过亲和性使其调度到网络延时较小的节点之上,提升网络传输效率,TiKV 之间借助反亲和性,使其分散部署到不一样的主机、机架和可用区上,下降因硬件或机房故障形成的丢数据的风险。

上面解释了容器层面的隔离,能够看做是物理层面的隔离。那么数据层面的隔离,TiDB 的调度体系也是有所考虑的。好比一个大的 TiDB 集群,节点分布在不少台主机,跨越多个机架、可用区。那么用户能够定义 Namespace,这是一个逻辑概念,不一样业务的数据库和表放置在不一样的 Namespace。再经过配置 Namespace 和 TiKV 节点以及区域的对应关系,由 PD 模块来进行调度,从而实现不一样业务的数据在物理上的隔离。

高可用性

TiDB 做为一个分布式数据库自己就具备高可用性,每一个核心组件均可以独立的扩缩容,任意一个模块在部署多份副本时若是有一个挂掉,总体仍然能够正常对外提供服务,这是由 Raft 协议保证的。可是若是对数据库节点的调度不加任何限制,包含一份数据的多个副本的节点可能会被调度到同一台主机。这时若是主机发生故障,就会同时失去多个副本,一个 Raft 分组内在失去多数派节点就会使整个集群处于不可用的状态。所以 tidb-operator 在调度 TiKV 节点时须要避免出现这种状况。

另外 TiDB 支持基于 label 的数据调度的,给不一样的 TiKV 实例加上描述物理信息的 label,例如地域(Region)、可用区(AZ)、机架(Rack)、主机(Host),这样 PD 在对数据进行调度时就会参考这些信息更加智能的制定调度策略,尽最大可能保证数据的可用性。例如 PD 会基于 label 信息尽可能把相同数据的副本分散调度到不一样的主机、机架、可用区、地域上,这样在物理节点挂掉或机架掉电或机房出故障时,其它地方仍然有该数据足够的副本数。借助 tidb-operator 中 controller-manager 组件咱们能够自动给 TiKV 实例加上物理拓扑位置标签,充分发挥 PD 对数据的智能调度能力,实现数据层面的高可用性。

同时咱们还能够实现实例级别的高可用性,经过 Kubernetes 强大的调度规则和咱们扩展的调度器,咱们按优先级会尽可能选择让 TiKV 部署到不一样的主机、机架和可用区上,把因主机、机架、机房出问题形成的影响降到最低,使数据具备最大的高可用性。
另外运行在 Kubernetes 之上咱们能实时监测到 TiDB 各组件的运行状况,当出现问题时,咱们也能第一时间让 tidb-operator 对集群进行自动修复 (self-healing)。具体表现为 TiDB / TiKV / PD 实例出现故障时,执行安全的下线操做。同时增长新的实例,来保证集群的规模和以前一致。

总结

TiDB 做为一款 Cloud Native Database,经过 tidb-operator 的方式充分发挥 Kubernetes 平台的强大能力,实现云上自动化管理,极大下降人力运维成本。用户能够根据业务须要进行动态扩容缩容,多租户隔离特性让不一样租户的实例能够共享计算和存储资源,互不干扰,同时最大程度充分使用云上资源。Raft 算法和 tidb-operator 自动修复能力以及两层调度机制保证了 Cloud TiDB 的高可用性。UCloud 和 PingCAP 公司深度合做,推出 Cloud TiDB 产品现已开启公测,欢迎你们来体验云时代的新一代数据库。

相关文章
相关标签/搜索