从服务治理到数据库服务治理
微服务治理
对于从服务治理到数据库服务治理,我们还是要先从微服务治理来切入。从单体到微服务,伴随业务越来越复杂、越来越多元,以及基础设施规模越来越大,服务之间的调用关系变得非常复杂。对于微服务的治理行为,还停留在流量控制、可观测性、安全访问、配置管理和高可用等几个方面。要想让微服务架构更快地上线和部署,一定要有一个自动化的框架。对于运维,我们也希望能够尽可能的标准化。
这时候就出现了像 Kubernetes 这样的容器编排框架。Kubernetes 源自于 Google 内部的 Borg,它带了很多 Google 的大厂属性。大厂不是说技术多么厉害,而是说它的规模非常大。只有在一个特别大的规模里,运维的复杂度才会体现出来,也就更加依赖于平台或者工具去提高自动化能力。
这种自动化的能力,我们把它叫做基础设施即代码,只要是 SRE 的同学去写一个这样的配置文件,然后把文件提交给 Kubernetes ,Kubernetes 就会根据里面描述的行为去做相应指令,只不过这个指令是声明式的 API,在这个过程中就完成了对应用的自动化上线。
讲到 Kubernetes,如果成千上万个节点,上面可能有十万个pod,我们怎么去做它的服务治理?如果还沿用之前的服务发现框架,在兼容 Kubernetes 时多少会有一些水土不服。有没有一种原生的方式去做 Kubernetes 之上的服务治理呢?这就是 Service Mesh。
Service Mesh
Service Mesh 是在2016年由 Linkerd 项目率先提出的。Service Mesh 把对服务治理的行为或者能力下沉到基础设施。Service Mesh 可以认为它是一个对于服务治理行为的编排框架,我们通过一些声明式的配置,让 Service Mesh 去交付给基础框架、基础设施,去代理我们执行相关任务。
服务网格有个特别典型的图,就是下边这个图。我们可以假设蓝色的就是我们的业务应用,而黄色的部分就是我们的服务网格代理。服务网格就是通过这样一个透明代理,去把应用的所有流量接管,再去对它治理。它通过这种接管的方式,实现各种服务治理的能力,比如服务发现等等。
Istio 简介
说到 Service Mesh,就必须提到 Istio 这个项目。2016年 Linkerd 对于 Service Mesh 的理解可能还不是特别成熟,我们把那个时候叫做第一代 Service Mesh。到 Istio 出现,才真正成为了现在我们所看到的 Service Mesh 架构, Istio 我们把它叫做第二代 Service Mesh。
Istio 的主要架构就是下边的这张图,它底下有一个 Control panel。Control panel 叫做控制面,这个控制面里面有 Istiod,在早期1.0的时候,Istiod 是由多个独立组件构成的,比如说 Pilot、Galley、Citadel,它们分别去负责一些证书,比如可观测性的聚合。它原来还有个 Mixer,还有各个 Pilot 相关的配置等等。
微服务治理和数据库服务治理的异同
或许有人要问:既然 Istio 可以代理应用的流量,那么为什么我们还要去谈一个数据库的服务治理?我们完全可以让 Istio 劫持我们数据库的协议或者让它去代理所有的数据库流量,来实现治理的效果。
如果我们把数据库看成微服务中的一个特殊微服务,它可能是服务链路的最后一环,我们确实是可以通过 Service Mesh 对数据库进行访问治理,包括服务发现这些能力。但是数据库也有一些特殊的治理属性或者要求,比如说数据库的协议,它的 MySQL 协议或者说 PG 的协议或者 Redis 协议有特殊性。比如 MySQL 协议我们在建连的时候,是服务端会先给我们发一个包,还有就是资源管理,可能对于微服务之间业务的应用之间,我们不用太关心它在运行时所消耗的资源,它应该是整个服务的一个性能指标。
但对于数据库来说,可能就会有些区别。它体现在可观测性上的时候,比如数据库的指标,它可能会涉及到慢 SQL 或者 SQL 延迟。
对于流量治理这部分,数据库也有它的特殊性,不管是 Redis、MongoDB 还是 MySQL,我们去做分片的时候,很难单纯只依靠于流量本身去做分发。如果我们随意地去转发相应请求,可能就会得到一个错误答案。
另外,对于像分库分表这样的场景来说,所谓的负载均衡是基于对数据属性的判断,不能粗暴的只是让它随机发到后面的不同数据源。
最后还有访问控制,一般来说对于业务应用来说,我们是一个微服务级别的访问控制,可能关联到一个全局的认证或者身份授权,很多情况下我们需要一个表级别甚至是列级别的访问控制,只有通过特殊的治理手段,才能够实现这样一些特殊的治理效果。
如何设计一个Mesh模型
从 Service Mesh 中学到的
我们还回到刚才所说的 Service Mesh,我们从 Service Mesh 中能学到什么呢?
第一,Service Mesh 是一个控制面和数据面分离的部署结构。
第二,如果我们把控制面和数据面的协议做了标准化,我们可以让数据面有更多的选择。
第三,我们可以通过 Kubernetes 机制去实现基础设施即代码的配置能力。
第四,可以通过插件去支持各种不同的协议扩展。
这个时候我们就会去想,数据库的 Database Mesh 和我们 Service Mesh 之间是一个包含关系吗?还是两个完全不同的场景呢?
Database Mesh 概念的提出
在2018年的时候,ShardingSphere 的创始人张亮在他的《未来架构》这本书里面,其实设想过,如果将 Service Mesh 治理的思路应用到数据库治理上,会是一个什么样的效果?
ShardingSphere 这个项目主要是去做了数据库流量的治理,比如说做分库分表、做数据的加密、做数据的Scaling 等等。是不是可以存在一个 ShardingSphere 的 Sidecar,跟业务的应用部署在一起,就像下边这张图一样,正常的业务请求通过 Service Mesh 去做微服务、业务之间的服务发现和相关的调用。对于数据库的流量,通过 Sharding Sidecar 把它代理到数据库,而整个 Sharding Sidecar 也是依赖于一个控制面去做相关的注册和发现。
Sharding Sidecar 和 ShardingSphere 已有的 Proxy 和 JDBC 能力应该是一样的,通过这种方式可以完美地屏蔽掉 JDBC 和 Proxy 的缺点。Sharding Sidecar 以 Sidecar 的方式部署在业务应用的旁边,业务应用如果是分布式的或多副本的,那么它就会有多个副本。业务的应用和它之间的通信也可以实现这种语言上的解耦,而不用受限于具体的语言栈。这种架构是不是就是一个弹性伸缩+零侵入+去中心的云上基础设施呢?
这是2018年的时候,关于 Database Mesh 的一个想法。
Database Mesh 的现状
从2018年到现在这短短四年的时间里,Database Mesh 这个概念其实已经在很多公司得到了充分的探索和实践。概括起来有三种典型的方案。
第一种,是 ShardingSphere Sidecar 的形式。把对数据库治理的特性,以 Sidecar 的形式提供给业务的 Pod。
第二种,可能在一些大厂里会见得比较多,就是统一的 Mesh 管控。
第三种,叫做分布式数据库。
这三种方式有一个共同点,就是它都关注于数据库服务治理中流量治理的那一部分。我们如果把这个阶段称之为1.0阶段,那么假设我们要去对它做一个升级,要做2.0,应该又有什么样的特性呢?
Database Mesh 1.0 vs 2.0
我们在经过了充分思考之后,觉得应该有更多的扩展性和面向更好的用户体验,所以我们提出了 Database Mesh 2.0的概念。我们现在专门做了一个网站,它的口号就是“通过可编程,实现高性能的扩展,来应对云上数据库的治理挑战”。
我们希望通过这种可配置、可插拔或者可编程的方式,能够实现一个覆盖数据库流量、运行时资源和稳定性保障等方面的治理框架。不管是什么类型的数据库,我们希望能够有一个标准界面,然后去把它治理起来。这就是 Database Mesh 2.0所希望的能力。
对于 Database Mesh 2.0来说,其实特别要强调的一点就是,我们在做设计的时候,一直在想 Mesh 到底代表的是什么含义。其实最开始的时候,我们就会觉得 Sidecar 就是 Mesh,我们通过 Sidecar 和别的东西交互了,就构成一个网格,这个网格就是 Mesh。但是后来我们发现其实不是这个样子,Mesh 的核心不是 Sidecar,Mesh 只是它的一种表现形式。
我们接下来就看 Database Mesh 2.0的全景图。这个全景图左半部分上面其实是按照用户来分的,上面是 Developers,就是开发人员对于 Database Mesh 的感受是什么。而右侧对于 SRE 或者 DBA 的同学来说,他们想看到的东西就比较多了,不管是访问控制、审计、风控、可观测性、事件。而右侧对于 SRE 或者 DBA 的同学来说,他们想看到的东西就比较多了,不管是访问控制、审计、风控、可观测性、事件。
总结一下:
第一,Database Mesh 2.0里边,我们认为数据库是一等公民,所有的这些抽象都是围绕数据库进行的,也就是刚才我们所说的 VirtualDatabase。
第二,我们希望它是能够面向工程师的体验,不管是开发人员还是 SRE 还是 DBA,用关心数据库实际的位置。
第三,云原生,它应该是一个天生面向云的环境,用户不用担心这个东西是厂商锁定的。
如何利用 Kubernetes 实现数据库 Mesh
最后这部分,我们就介绍一下我们如何利用 Kubernetes 去实现这样一个模型。
Kubernetes 的扩展模式
Kubernetes 被称为是平台的平台。我们在 Kubernetes 之上,之前也涌现出来好多创业公司和一些 PaaS 服务商,在它上面去构建各种各样的能力。
Kubernetes 的扩展能力是它生命力的关键,它的第一种扩展模式叫做 Sidecar;第二个扩展模式是 AdmissionWebhook;第三个扩展模式,也是它特别厉害的一个能力,就是用户自定义资源定义。
Pisanix 设计与实现
介绍完 Kubernetes 之后,我们就来看一下 Pisanix 这个项目。Pisanix 是 SphereEx 团队对 Database Mesh 提供的一个解决方案。Pisanix 是由 Go 和 Rust 两种语言编写的,我们去适配了 Kubernetes 环境,目前支持 MySQL 的协议。
它有三个比较主要的组件:
- Pisa-Controller:Pisanix 这个项目的核心组件,Pisa-Controller 承担的就是控制面,它是 Golang 实现的,是一个必选的组件。
- Pisa-Proxy:Pisa-Proxy 是负责代理流量的数据面,Pisa-Proxy 就是以 Sidecar 的形式部署在 Pod 里面,它是 Rust 实现的,它也是一个必选的组件。
- Pisa-Daemon(即将发布):它是负责资源优化的数据面,是一个 DaemonSet,是每一个 Kubernetes 集群的节点都会有一份 Daemon,它是一个可选的组件。
三个组件共同达到的效果:
第一,实现了SQL感知的流量治理。
第二,面向运行时的资源可编程。
第三,数据库可靠性工程,也叫DRE。
我们会重点讲一下 Pisa-Proxy。Pisa-Proxy 是我们最主要的一个核心组件,所有的能力其实都依赖于它,我们选择了 Rust 而去实现它的一个主要原因,是因为 Rust 的生命力是非常顽强的。
Pisa-Proxy 可以看作是面向数据库流量的负载均衡器,主要工作流程如下:
目前 Pisa-Proxy 支持 MySQL 协议,将自己伪装为数据库服务端,应用连接配置只需修改访问地址即可建连:
- Pisa-Proxy 通过读取应用发来的握手请求和数据包,得到应用希望执行的 SQL。
- 对该 SQL 进行词法和语法解析后得到对应 AST。
- 基于 AST 实现高级访问控制和 SQL 防火墙能力。
- 访问控制和防火墙通过后 SQL 提交执行。
- SQL 执行阶段的指标将采集为 Prometheus Metrics。
- 根据负载均衡策略获取执行该语句的后端数据库连接。
- 如果连接池为空将根据配置建立和后端数据库的连接。
- SQL 将从该连接发往后端数据库,并读取相应返回结果。
- SQL 执行结果进行组装后返回给业务应用。
最后讲到Pisa-Daemon。Pisa-Daemon 离不开 eBPF 技术。eBPF 现在比较火,eBPF 这个技术本身就像容器一样,也不是特别新鲜的东西。
eBPF 是 Extended BPF 的简称,可以在 Linux 内核中构建沙箱并运行各种程序。eBPF 是一种安全和高效的内核扩展机制,常被用于:
- 可观测性:eBPF 在内核态可以进行各种可观测指标的采集,生成多样化的图表。
- 系统安全:通过 eBPF 可以观测到所有的系统调用,配合对网络包或套接字的管理以及进程上下文追踪能力,实现更细粒度的安全控制。
- 网络:eBPF 的编程扩展能力,天然适合对网络流量包的处理过程,无需离开内核的网络处理上下文。
- 追踪和调优:通过对用户态进程以及系统调用的探测,实现零侵入的关联追踪和性能分析体验。
对于 Pisa-Daemon 来说,号称是做资源可编程,Pisa-Daemon 利用内核机制去提供资源方面的管理能力,这个资源就是运行时资源。通过 Pisa-Daemon 和 Pisa-Proxy 去做配合,我们可以标识不同优先级业务出来的数据库流量。
未来我们希望做成一种扩展机制,以可插拔或者可编程方式,支持更多种类型的运行时资源管理。
如何在 Kubernetes 中使用 Pisanix
最后我们可以看一下在 Kubernetes 里面如何去使用 Pisanix 这个项目。
Pisanix 既然是对于 Database Mesh 的一个实现,那么我们也沿用它的规范,首先我们使用了三种 CRD,这三种叫做 VirtualDatabase、TrafficStrategy 和 DatabaseEndpoint。
VirtualDatabase 就是我们前面介绍 Database Mesh 的时候,它的聚合根,它的所有对于 Database Mesh 治理的能力,都是体现在 VirtualDatabase 上面。
在 VirtualDatabase 里面有一个字段叫做 TrafficStrategy,标识对于这个数据库的访问来说,我们希望它以什么样的策略去治理它。
那它 Random 到谁呢?就是在 TrafficStrategy 里面它有 Selector。Selector 通过 Label 去选择后端的 DatabaseEndpoint。
这三个 CRD 就可以覆盖这个业务应用对于后端数据源和对于 MySQL 代理的相关配置信息。接下来就是 Pisa-Controller 拿到 CRD,然后根据里面的配置内容转化成 Pisa-Proxy 的配置,然后推给相应的 Sidecar。Pisa-Proxy 拿到这个配置,然后根据里面的内容将向后端实际的数据库去建连,然后同时前端去监听端口,接收请求。最后对应用来说,它只要访问里面配置的这个端口就可以实现了。
其实 Pisa-Proxy 我们也在思考它其实也是可以被单独使用的,有的 Kubernetes 不是适用于每一个公司或者每一个业务,如果说也需要这种对于数据库的治理,尤其对于流量这种统一接入的治理,其实是可以把 Pisa-Proxy 单独拿出来去部署。这个时候的用法,就跟我们用一个 Nginx 组成一个集群是一样的,不管后面你是什么样的数据库,比如你是 RDS 或者 TiDB、ShardingSphere,都可以由 Pisa-Proxy 做成数据库的统一接入层,来对它做代理,实现这种比如无感的高可用切换,实现面向 SQL 的可观测性等等。