黄东旭:When TiDB Meets Kubernetes

本文是我司 CTO 黄东旭同窗在 DTCC2017 上的《When TiDB Meets Kubernetes》演讲实录,主要分享了关于 TiDB 与 Kubernetes 整合的一些工做。文章较长,且干货满满。
640.jpg-26kBnode

如下为演讲实录:

今天给你们带来的分享是关于 TiDB 与 Kubernetes 整合的一些工做。在讲以前,想了解一下,在场的各位有据说过 Kubernetes 的同窗举个手;据说过 TiDB 的举个手,很是好。由于我也是 DTCC 的常客了,前几年来说的时候基本上没有人据说过 TiDB ,随着这个项目愈来愈成熟,很欣慰,有一些社区的小伙伴已经用起来了。算法

我先简单介绍一下我本身。我是 PingCAP 的联合创始人兼 CTO ,也是 TiDB 项目的码农 ,以前一直也是在作 Infrastructure 和分布式系统相关的一些工做。同时也是特别喜欢开源,基本上作的全部东西都是开源,包括像 Codis 、TiDB 、 TiKV 这些项目。比较喜欢的编程语言有 GO 、Rust 、Python。数据库

对于今天的话题,若是说在 40 分钟以内要去完整的介绍怎么作一个数据库或者说怎么去作一个集成调度的系统,我以为是不太可能的。编程

因此今天这个 Talk 我主要会分享 TiDB 做为一个分布式关系型数据库与云整合的经验,以及咱们遇到的一些问题,来帮你们在遇到类似的问题时提供一个解决思路。你们其实在存储系统上能够把 TiDB 换成任何东西,而后对于在 Kubernetes 上的融合,我以为都会有一些启发的意义。缓存

首先想强调的是,Cloud 才是将来。现场作一个小调研:有在线上生产环境中,正使用 Kubernetes 或者 Mesos 这样的容器化管理方案的同窗吗?好,一个两个三个。安全

我相信,若是在三到五年之后,再去问这个问题,应该是至少一半的同窗会举手。由于其实在可见的将来,数据量是一直在膨胀,业务会愈来愈复杂。包括如今不少微服务的这种思想,当你的业务比较大的时候,会把整个服务拆成很是细的模块,而后在众多的模块的拆分之下,怎么去高效的运维你的分布式集群,其实靠 SRE 或者运维人员手动去管理各个微服务或者说各个的系统,实际上是不太现实的。网络

可是你们也都知道,对于这种无状态的业务,好比像 Application 应用层,其实它并不会真正的存储数据,状态通常都持久化到数据库或者缓存中,因此它基本是无状态的。因此其实一直以来你们在使用容器化遇到的第一个问题就是,有状态的服务,特别是数据库或者分布式存储系统,怎么去运维。好比说我在 Docker 里面写的数据,这个容器销毁它直接就挂了,它实际上是很难去作这种数据层面上的东西。架构

而后另一方面就是数据库的运维,无论是放在云上作仍是 DBA 本身在物理机上作,同样的痛苦,因此怎样去设计一个面向云的环境下、或者说在分布式系统上去作数据库。其实这也是在作 TiDB 的过程当中我一直在想的。TiDB 这个项目一开始的设计就是:它必定会放在云上去运转。
640-1.jpg-36.3kBapp

上图是 Amazon 在它的云上选择建立一个数据库实例的一个界面,你们不用看具体的字是什么,只是给你们感觉下:就是说我做为一个业务的开发,我要去存储一个数据,启用一个 PG 或者 MySQL 的数据库,我还要去关心这么多个选项,或者说我必定要去关心个人这个物理机究竟是什么样的状况:磁盘有多大,什么机型,各式各样的配置等。由于毕竟不是全部人都是专业的 DBA ,也不是全部人都是操做系统的专家,当时看到这个页面的时候,基本上业务开发多是一脸懵逼的状态。less

如今全部人都跟你说,个人系统是一个分布式系统,在广告里写的很是漂亮。如今哪个数据库说本身不是分布式的,那基本上只能是落后于时代。可是,你们有没有想过,如今全部的这些分布式系统、分布式数据库,运维起来都是很是痛苦,没有办法去很好的把它用好。

以 Hadoop 为例,如今 setup 一个 Hadoop 的集群居然能成为一个生意,这个生意还让两个创业公司都上市了,一个是 Cloudera,一个是 Hortonworks。其实严格来讲,他们只是作 Hadoop 运维的创业公司。而后还有无数的公司在作 Spark 的维护、Spark 的管理。各类各样的数据库运维公司,靠这个都过的很是好。

这个其实在我看来是很是不正常的,由于你们想,若是你去运维一两台机器那没有问题,我写一个脚本,轻轻松松的就能够搞定;而后三五十台机器,也还行,招一个 OPs 或者说招一个 DBA ,仍是能够人工的管理。

可是若是是在 100 台、1000 台甚至 10000 台规模之上,机器的故障会是天天每夜无时无刻都发生的,网络的抖动,磁盘 IO 的异常,一直都在发生,靠人是没有办法作的。总的来讲就是,当你去运维一个 single node 的系统时,基本上没有什么难度;可是若是要去运维一个特别大的 P2P 的 distributed system,尤为是节点数特别多的时候,你的状态和维护的成本就变得很是高。

以前我作过一个项目叫 Codis ,可能有不少同窗据说过,也可能不少同窗已经用在生产环境之中。还有另一个不是我作的项目,就是官方的 Redis Cluster。当时不少社区里面的 Redis Cluster 的粉丝一直喷我,说 Codis 的配置怎么这么复杂,一点都很差用,组件怎么这么多。如今这个事情又在重演。

不少系统作成了 P2P 的模型了之后,组件不多部署很方便,可是真正在去运维它的时候,好比说要去作一个滚动升级或者我想清楚的知道整个集群的数据分布和各个组件的状态,又或者说是个人分布式逻辑出了个bug,可是个人存储层没事,须要作个热更新这个时候,p2p 系统的运维复杂度就凸显了。Codis 它其实有一个 Proxy,一个存储层,这两层在逻辑上实际上是分离的;可是 Redis Cluster ,每个节点既是它的分布式调度模块,同时又是它的数据存储模块,这时候整个系统架构是混在一块儿的,对于运维的同窗来讲这就是一个恶梦。

若是我想清楚的知道个人数据究竟是在哪几台机器上,好比说这块数据特别热我想把它挪走,这时候像在 Redis Cluster 这种纯 P2P 的系统里面是很可贵到它的当前状态。因此这也是影响了我后来一系列的系统设计的想法,因此 operation 是一个很是困难的事情,在一个特别大的分布式系统里,是一个很是困难的事情。

由于你的服务和组件特别多,不一样的组件,不一样的模块,而后再加上一个分布式系统里边特别不稳定的网络状态,使得各类各样的异常状况,人是没有办法去掌控的。

还好,Google 是一个很是伟大的公司,像 TiDB 整个模型你们也知道是参考了Google Spanner/F1。Kubernetes 背后的系统的前身就是 Google 的 Borg。

Borg 实际上是 Google 内部一直在用着的大规模的集群调度器。Borg 这个单词就是星际迷航里面的一个角色,至关于它做为整个集群的一个大脑来去控制集群的业务的分布跟数据的均衡,因此 Google 给咱们带来了 Kubernetes 这个项目。

Kubernetes 主要的工做就是一个面向 Container 的集群管理的服务。它同时会去作服务编排,Auto deployment、 Auto scaling、Auto healing ,你的整个集群的这些服务的生命周期的管理,而后故障的转移、扩容...你能够认为它是一个集群的操做系统。你们可能认为操做系统就是单机上的一个概念,可是若是放到一个大规模的分布式系统里面,你有无数的 CPU 资源,无数的内存,无数的磁盘资源,怎么高效的去把你的服务在这些海量的资源上进行合理的分配,这个就是 Kubernetes 干的事情。

TiDB 你们也都很是熟悉了,我简单介绍一下吧。

咱们作 TiDB 的目标就是,但愿构建一个彻底弹性的,用户不须要去知道数据的分布信息,也不须要去作手工的数据分片,能够把它当作一个单机的数据库、 MySQL 的数据库在用,可是它背后是一个高度弹性和智能的分布式的数据库。对业务层你不须要再去想分库分表,也不须要再去想热点的 balance 这种事情。它支持百分之百的 OLTP 的功能,能够支持跨行事务,像 MySQL 同样:我开始一个 transaction,而后写写写,最后 commit,全成功或者全失败,没有第三种可能 。支持事务的前提之下还支持 80% 的 OLAP 。

因此 TiDB 是很是适合去作这种一边有实时写入,一边有复杂 Join 和实时分析的场景,它的 SQL 优化器其实也有从 Spark SQL 里面学到了不少东西。

固然对外的接口是完整的 MySQL 的接口,你能够直接用 MySQL 的客户端就连上了。另外,背后它支持高可用。由于底层的复制协议并非经过像这种主从模型去作数据冗余的,而是用 Raft,Raft 跟 multi-paxos 是比较接近的,都是基于选举的算法。

在遇到 MySQL 的扩展性问题的时候,你们过去只能一脸懵逼,而后反回来去拆库拆表,或者去用 MyCat 或者依托 MySQL 的中间件去作 sharding 。其实这对于业务层来讲,侵入性很是大,因此 TiDB 的初衷就是解决这个问题,可是它没有用任何一行 MySQL 的代码。

TiDB 大概是这样一个架构:

640-2.jpg-15.8kB

TiDB 其实也是由不少的组件组成,它并非一个纯粹的 P2P 系统,若是看这个架构其实很是像 Codis 。

今天的主题是说咱们怎么在 Kubernetes 上去作 cluster 的 setup、rolling update,怎么去解决 Kubernetes 对于本地磁盘存储的绑定问题。

因此面临着一个与你们以前在 Kubernetes 上去部署带状态的服务很是接近的问题。由于 TiDB 自己是一个带状态的数据库,数据库没有状态那不可能。Kubernetes 的调度实际上是对这种 stateless applications 很是友好的,可是若是你是一个带状态的,好比像 MySQL、PG、TiDB,或者 Etcd、Zookeeper 等等,怎么去作?真正的困难并非 Kubernetes 作不了,而是每个不一样的系统都有本身的数据分布模型,同时每个不一样的系统它的运维方式也不太同样。因此做为 Kubernetes 的平台来讲,没有办法去针对每个不一样的存储系统去设计一套本身的调度策略。这是 Kubernetes 没有办法去作的一个事情。

举个例子,好比说你想去运维好一个 Redis Cluster ,那你必须得了解 Redis Cluster 一些原理,还有必须得去知道它怎么运维;若是你想要去运维 Codis ,你必须得知道 Codis 的一些原理方法才能把它运维好。但这些领域知识怎么去告诉 Kubernetes 说你帮我按照这个方法来去运维这个系统?

这时候有一个公司,叫作 CoreOS ,相信你们可能也都熟悉,就是 Etcd 背后的那个公司,也是咱们 PingCAP 的好伙伴。CoreOS 也是社区里面最大的 Kubernetes 的运营的公司,他们引入了一个新的 Kubernetes 的组件,叫作 Operator 。Operator的意义在于它实际上是至关于使用了 Kubernetes 的 TPR(third party resources)的 API,去把你的系统运维的一些领域知识,封装到 Operator 里面,而后把 Operator 这个模块注入到 Kubernetes 上面,整个这些集群是经过 Operator 这个模块来去作调度。

CoreOS 官方还提供了一个 Etcd 的 Operator 的实现。其实这思路也很简单,就是说把这个集群的建立滚动更新,而后各类运维的一些领域知识放到这个 Operator 里面,而后在 Operator 里面去调用 Kubernetes 原生的 API,来作集群的管理,至关因而 Kubernetes 的一个 Hook 。

通常来讲一个 Operator 它其实有这样一些对外暴露的接口或者是能力。它作的事情的就是:好比我想要让 Kubernetes 去创建一个 TiDB 的集群,好比说 deployment;好比我加入新的物理节点之后,我要想对现有的这个集群作扩容,而后 rebalance;好比说个人集群的 TiDB 自己的这些 binary须要升级,我要去作业务透明的滚动更新,好比说我有 100 个节点,要在上面去作升级,不可能手动去作,这个其实都是封装在咱们的 Operator 里面,而后包括自动化的 backup 跟 restore 这些功能。

本质上来讲你能够认为 Operator 是一个 Kubernetes 的批处理方案。我刚才也简单提到了一下,Kubernetes 能够做为一个集群的操做系统,可是这个操做系统总应该能让运维去写脚本的,这个脚本就是 Operator 机制。

其实它的原理很简单,就是它注入到 Kubernetes 里面,会实时不停的去观察集群的状态,去 Hook Kubernetes 的一些集群的状态,一些 API,而后获得整个集群的状态。把一些分析的东西放在 Operator 里面,它可能会有一些地方被触发,好比说我该扩容了或者我该去作 Failover ,而后去反馈。

而后 TiDB 的 Operator 目前来讲有这么几个功能:建立集群、滚动更新、Scale out, Failover、Backup/Restore。

由于其实今天的这个话题是说怎么去跟云作结合。咱们如今在跟一些公有云的提供方在作合做。可是不可能说每个公有云都本身去接入它的资源管理的 API,由于每一个公有云可能都用的是不同的 API database,因此咱们至关于作的一个方案就是说,无论你是公有云也好仍是私有云也好,你给我一堆物理的机器,而后在这一堆物理的机器上面去部署 Kubernetes ,在这个 Kubernetes 上面,我至关于把个人 TiDB Operator 给放进去,当某个公有云客户要它去建立一个集群的时候,会通知 Operator 去建立,好比说划出一些机器,去作物理隔离。

这在私有云里边也是一个比较常见的场景了。用户他其实想要去作这种业务之间的租户隔离,TiDB Operator 是作一个比较简单的物理隔离。

可是作这个 Operator 最难的一个部分其实刚才也简单讲了一下,就是存储的问题。若是你们关注 Kubernetes 社区的话,通常都会注意到 persistend local storage 的这个方案一直在社区里边扯皮和吵架。如今你们认为 Kubernetes 本地的磁盘是无法用的,或者说没有办法直接当作一个资源来使用的。
640-3.jpg-29.5kB

上图是放在 Kubernetes 的 issues 里面的一个问题,就是 persistent local storage ,这个看上去很是难以想象,这么简单的功能为何一直到如今没有支持。

我我的感受 Google 之因此迟迟不去作这个功能,它背后有一个缘由多是在 Google 内部,它的网络存储是很是强的,它有本身很是好的网络设备。你在同一个数据中内心,去换一块网络盘,它的这个 latency 基本上不少业务能够接受的,因此这样的话,持久化存储的问题基本上是靠网络的磁盘来解决。

想像你跑一个 MySQL 的业务,MySQL 的业务自己它写入的磁盘并非你的物理机的本地盘,而是一块网络盘,这个网络盘我能给你保证它的 IOPS 跟 latency 都是很是好的状态,这个时候你的业务挂掉了,我再从新启一个容器把这个网络盘再挂到那个 pod 的后边,这时候你的业务是几乎无感知的。这也是 Google 比较推崇的使用存储的一个模式,因此这就是 Kubernetes 背后的那个 persistent volumes 的这个方案。

它如今是有这个方案的,可是对于像咱们这样的分布式数据库或者说对这种本地磁盘有特别强要求的( TiDB 底层的存储引擎对单机的 SSD 作了很是多的优化),并无办法去容忍我底下写入的一个磁盘是网络盘。由于自己好比说 TiDB 这一层,已经作了三个副本,甚至五个副本的复制,可是在底下网络盘又要去作这个复制实际上是没有太多必要的,因此 Google 一直迟迟没有推 Local Storage Resource。若是在 Google Cloud 上它能更好的去卖它的云盘,或者说对于这些公有云厂商来讲,这是更友好的。

固然它也不是没作,它是要在 1.9 里面才会去作这个支持,但以 Kubernetes社区的迭代速度,我估计 1.9 可能还要等个两三年,因此这是彻底不能忍的一个状态。那既然咱们又须要这个本地磁盘的支持,可是官方又没有,那该怎么办呢?这时咱们就发挥主观能动性了。

咱们给 Kubernetes 作了一个 patch。这个 patch 也是经过 Kubernetes resource 的方案去作的一个本地磁盘资源的管理模块,这个怎么作的呢?也比较简单。这部份内容就比较干了,须要你们对 Kubernetes 整个架构有一点点了解。

第一步,先会去建立一个 Kubernetes 的 Configuration map,咱们称之为 TiDB 的 storage ,就是针对 storage 的物理资源写在配置的文件里边。好比说机器的 IP,它的不一样盘对应的文件夹在哪儿,至关因而一个配置阶段的东西。

第二步,建立一个利用 Kubernetes 的 Third Party Resources(TPR) 的 API,去建立一个叫 tidb-volume 的第三方资源,而后这个资源去刚才 Configuration map 里面去读它去注册的那些物理磁盘分布的状态资源,至关于 TPR 会把那个配置里面的磁盘资源 load 出来,变成在 Kubernetes 里的一个第三方 resource,这个对象大概是这样一个状态。

第三步,咱们在这边会去写一个 controller,咱们写这个 controller 是干吗呢?叫 volume-controller 。好比说咱们的一个磁盘的资源分配给了一个 pod,而后这个 pod 如今在占用着这个资源,我须要有一个 controller 的模块来标记这个资源的使用状况,不能说我新的业务在起来的时候,我把资源分配给了两个正在用的业务,这是不行的。这里的对应关系实际上是由 volume controller 去维护的。而后另一方面它还实时的监盯着刚才 Configuration map 里面的物理资源,我能够动态的添加物理的磁盘资源的一些状态。

第四步,刚才咱们说到 opreator 实际上是一个运维的工具,去作建立集群还有滚动升级,至关于总的入口。在这里面在去建立集群,在启动进程的时候,把刚才咱们建立的本地磁盘资源启动实例绑定在一块儿,让它可以成功的建立这个资源。

第五步,就是建立一个 DaemonSet。DaemonSet 在 Kubernetes 里就是在每个物理节点上每个长度的进程,这个进程是用来去维护磁盘上资源的使用情况。好比说一个 TiKV 的节点下线了,这个物理的磁盘资源就要被回收。回收了之后你要去作删除数据、清空数据的操做,或者这个物理机就宕机了,你须要有一个东西来去通知 controllor 把这个资源给下线掉,这个是 DaemonSet 在干的事情。

这一整套加起来就是至关于只经过 Third Party Resources 来嵌入 Kubernetes,总体来讲在旁路却实现了一种本地的 local 磁盘的管理方案。

由于这个代码其实还蛮多的,如今尚未办法直接 push 回 Kubernetes 社区。虽然如今这仍是一个 private 的项目,可是在后续把代码整理之后咱们仍是会开源出来。因此这其实也是为你们之后在 Kubernetes 去调度这种单双向服务的方案,提供了一套可行的路,至少咱们用这个还挺爽的。

而后,想稍微展望一下将来的数据库应该是什么样的。

在我看来,将来的数据库不会再须要 DBA ,或者说将来你在写业务的时候,并不须要去关心底下数据该去怎么分片,怎么去 sharding,一切都应该是在后面的云服务自己或者基础设施去提供的。全部的东西都应该是 self-driving,至关于自动驾驶。就像在将来你们以为自动驾驶应该是一个方向,在基础软件里我以为也是愈来愈多的自动化致使你们对运维的依赖变得愈来愈轻。可是在不少的极端的状况下,circuit-breaker(断路器)仍是要有的。好比说个人业务产品如今忽然出现了一个特别热的热点,我须要业务这边去紧急的作手动的数据切分、移动,把负载手动的均衡出来,因此手动模式仍是仍然要有的。相似于你自动驾驶的汽车,忽然来一我的加塞,你们仍是很是但愿能保证本身的安全,那就必须得有一个手动的模式。

第二点是 database as a service ,前面也说到了,serverless 可能会在 database 里面,有一种新的形态的数据的数据库。你们若是关注数据库领域的话,最近出了一个新的数据库叫 FaunaDB 。FaunaDB 很是有意思,它实际上是跟公有云绑定在一块儿对外提供服务的,你看不见它实际的进程和部署,也看不见它物理的进程在什么地方,整个数据库对外的展示形式就是你去买个人服务,我给你多少的 QPS。好比说你买一万个 QPS,这个服务就能保证一万个 QPS,你买十万就是十万,按这个价格来去付费,至于你的容量全都是在背后隐藏着,全部业务的开发者其实是看不见。

第三点就是 Local storage isn’t necessary。为何 Google 它一直没有在 Kubernetes 里面去作 Local storage,其实仔细想一下也是有道理的,就是说随着将来硬件的发展,当网络社会的速度和分布跟你的磁盘的存储差很少的时候,那它究竟是不是网络的,已经对业务层没有什么意义了,因此这多是一个将来的趋势。

这条是一个比较极端的路,就是把整个磁盘放到网络上,用网络盘。还有另一条反向的特别极端的路,也是我如今正在尝试的一个东西,就是更加去对硬件作定制。好比说我如今尝试把一些 TiDB 的数据库的一些逻辑放到 FPGA 上面,或者是放在 SSD 的控制芯片里面,这实际上是更深的定制,在将来我以为二者可能会融合。就是说我虽然多是挂了一个网络盘,可是对于数据库来讲我有了这个计算逻辑可能直接的去操做硬件,而不须要去例如经过标准的 POSIX API 来转换内核走 本地 IO 的接口。

总结一下,分布式系统的运维特别的痛苦,而后 Kubernetes 是一个将来集群调度必然的趋势,可是在存储层它如今尚未太多的好办法。目前来讲咱们在作的一个事情就是 Operator,把整个存储层的运维的领域知识放在 Operator 里边,而后让 Kubernetes 能去调度咱们的东西。这个有点像 DCOS 的 batch script。

TiDB-Operator,实际上是把运维干的事情,全都经过 Operator 的形式来封装在程序里边,而后自动的去运维。

Local Storage 的问题咱们是解决了。虽然 Kubernetes 没有办法去提供这个能力,可是咱们暂时解决了。

相关文章
相关标签/搜索