服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。通常会为每个服务实例提供一个“边车”(sidecar)代理实例。边车会处理服务间的通信、监控和安全相关的问题,以及任何可以从各服务中抽象出来的逻辑。它让服务之间的通信更安全、快速和可靠。apisix-mesh-agent 项目,它有一些能力将 Apache APISIX 扩展为服务网格场景中的边车程序,更重要的是,它使用 xDS 协议从 Istio、Kuma 等控制平面获取配置。
在 Apache 首次亚洲线上技术峰会 -- ApacheCon Asia 大会上,Apache APISIX PMC,支流科技系统工程师--张超分享了 Apache APISIX 扩展服务网格边车实践和 apisix-mesh-agent 项目,我们根据速记,不改变原意的基础上做了审校修改,现在分享出来,供大家参考。
首先做一下自我介绍,大家好,我叫张超,是 Apache APISIX 的 PMC 成员,也是 Tars 基金会的大使,同时还是一名开源爱好者,我为 Nginx、OpenResty、Kuberentes Ingress Controller 等项目都做过一些贡献。目前工作于服务网格和 API 网关领域。下面是我的 GitHub 链接,大家有兴趣的话可以关注一下我,也可以和我在 GitHub 上面进行交流。
接下来我为大家带来关于如何扩展 Apache APISIX 成为一个服务网格边车代理的主题分享。
这是我本次分享的大纲,我的分享由 4 部分组成。
首先第一部分我会为大家简单介绍一下什么是 Apache APISIX 以及它的架构。
接下来我会简单介绍一下,在将 Apache APISIX 扩展为一个边车代理的时候,我们需要面临的一些问题,以及克服这些问题的方法。
第三部分是关于 Apache APISIX 作为服务网格边车代理的优势。
最后会为大家简单介绍一下 Apache APISIX Mesh 的一些未来的发展和规划。
如何从架构的角度理解 Apache APISIX
首先是第一部分,什么是 Apache APISIX ?Apache APISIX 是一个高性能全动态的云原生 API 网关,因为它是基于 Nginx 跟 OpenResty 实现的,所以它天然继承了这两者高性能的能力。全动态意味着,当你对它做任何的配置变更的时候,不需要去重启它,配置就能即时生效。与之相比,Nginx 就涉及到服务重启的过程,服务重启的代价是比较高的,重启需要断开一些链接,而且在重启过程中可能会有高达两倍资源占用。
除了支持热加载配置变更之外,Apache APISIX 还提供了很多开箱即用的功能。比如说负载均衡,Apache APISIX 提供了轮询、一致性哈希、EWMA 等负载均衡算法;同时 Apache APISIX 还提供很多种服务发现的机制,比如说你可以使用 Consul 做服务发现中心,也可以使用 Eureka 这样注册中心来完成服务发现的功能。Apache APISIX 还提供了很多服务治理的功能,例如限流、限速、认证授权等这样的一些能力。最重要的一点是,Apache APISIX 是基于插件机制的,扩展性非常强,在大部分情况下,你不需要去修改它的核心代码,在它的插件系统上实现一个新的插件,就可以满足你的大部分需求。
Apache APISIX 有一个非常活跃且健康的社区,如果你在使用上有任何的问题,都可以在社区里面进行交流,也会很快得到大家的回复,社区目前也在逐渐的发展,下面是 Apache APISIX 的首页,大家如果有兴趣的话可以去关注一下,我们也会在上面登记很多新的事件,比如说新版本的发布都会在官网上面进行更新。
下面是 Apache APISIX 的架构图,它的架构主要分为数据面和控制面两部分,左边是它的数据面,右边是控制面。数据面的话其实就是 Apache APISIX 自身,这是流量代理的组件,包括了一些像前面提到的服务治理功能,例如限流、限速、认证等相关的功能;还有一些安全和可观测性的功能,比如说 logging、metrics、tracing 这样的功能。右边是它的控制面,控制面的话是偏管控性质的组件,比如说 Apache APISIX dashboard。我们可以在 dashboard 上面完成一些配置变更、配置下发等这样的事情。
实际上真实的配置变更和下发是通过控制面右下角的 etcd 集群去完成的。Apache APISIX 是通过 watch etcd 集群去获取到真实的配置变更的。控制面还包括了重要可观测性的组件,比如说像 Apache Skywalking、Prometheus 和 Grafana,还有像其他日志相关的一些组件,它们都可以和 Apache APISIX 进行对接,从而把可观测性能力暴露到控制面。
服务网格的优势判断和落地实践
我们先来考虑两个问题:使用 API 网关的目的是什么?为什么要用到服务网格?
第一个问题,在使用 API 网关以后,服务的调用方和被调用方的中心是解耦的,所有的调用都会经过 API 网关。所以 API 网关就可以在自身集成一些服务治理的能力,例如通过认证授权,限制调用方的身份;或者通过限流和限速,控制调用的频率;还有就是服务发现 API 网关可以及时地感知到服务的实例变更;这些就是 API 网关的能力。
第二个问题,为什么要用到服务网格?其实很大一个原因是因为公司的微服务可能不仅仅存在一种编程语言实现,比如说有 Java、Go 和 Python 等这样的语言。在最初的情况下,服务治理功能集成在框架中,这意味着一个框架可能同时有好几个语言的版本。维护这些语言版本同步都会比较困难,而且要在真实使用时考虑一些兼容性的问题,同时升级也变得非常的困难。而使用服务网格以后,这些服务治理的能力都被下沉到了进程外的边车组件,由这个边车去帮你完成这些事情。
负责业务的研发同学不再需要考虑这些服务治理的能力,他们只需要聚焦在自己的业务开发上,而这部分的基础功能都将由专门的团队来维护。第一,它的升级变得更加容易。第二,我们不再需要不再被多语言给绑死。我们只需要维护一个版本的服务治理的能力就可以了,而不需要去实现多版本。这就是服务网格所带来的其中一个特别大的优势。
那么除去这些优势以外,大家会发现服务网格边车完成的服务治理能力在大部分情况下又和 API 网关所提供的服务治理能力有重叠。例如认证授权、服务发现、服务治理、限流限速等这些能力都是类似的,所以这两者在很大程度上是有重叠的,甚至可以说他们是一类东西。
这也就是为什么我们会考虑说,Apache APISIX 是否能够在服务网格中发挥一些能力,而不仅仅局限于它原本所擅长的 API 网关领域呢?基于这样一个思考,我们做了一些调查,围绕的是中国国内十几家在做服务网格落地的公司。我们发现了三个问题:
服务网格在国内的使用阶段非常的原始,而且在这个阶段大家重点关注的是 HTTP 的代理,以及使用流量切分功能来实现一些金丝雀发布、灰度发布以及蓝绿部署这样的一些发布策略
在处理东西向流量的时候,大家可能连 mTLS 都没有使用,而是使用明文进行传输。
第三,大家比较关注可观测性。打个比方,现在有两个服务实例 a 和 b,它们进行交互的时候并不是直接通信,而是中间会有各自的一个边车代理去帮助他它们完成通信。如果没有强大的可观测性能力支持的话,那么整个服务的调用链路是不够清晰的。
恰好这三个问题都可以通过 Apache APISIX 解决。但是我们还需要考虑的是,Apache APISIX 自身是否足够硬核,能够支撑起服务网格的这样一些功能能力?
我们重点考察的是服务网格中需要用到的一些功能在 Apache APISIX 上是否存在。主要分为以下几个问题。
Apache APISIX 是否支持多种代理协议,比如说像 HTTP、gRPC、TCP、UDP 这样的一些代理?因为 Apache APISIX 基于 Nginx,很多功能 Nginx 也早已为我们提供好,所以像这类代理的话,Apache APISIX 能够完美支持。
Apache APISIX 是否支持流量切分,并支持用户通过流量切分来实现灰度发布、蓝绿发布、金丝雀发布等一些发布策略?Apache APISIX 通过了 traffic split 插件来完成流量切分功能,实现发布策略。Apache APISIX 也支持负载均衡和健康检查等这样的一些功能。比如说在 Kubernetes 场景中,Apache APISIX 支持主动和被动健康检查策略,Apache APISIX 的健康检查机制相比 Kubelet 的更加灵活一些,它的周期可能发现得也会比 Kubelet 更短一些。所以配合这样的主动健康检查能力,在服务网格中 Apache APISIX 能够更快速地发现一些健康的实例,从而更好地保证服务的质量。除此以外,还有认证授权。Apache APISIX 除了支持像 mTLS 这样的端到端的认证以外,它还支持一些细粒度的认证,比如 JWT Token 或者 basic auth 这样的一些认证方法。这也是通过 Apache APISIX 的插件来实现的。
大家会比较关注服务网格的可观测性。其实还是由三部分组成,第一部分是 metrics;第二部分是 logging;第三部分是 tracing。这三部分也是由 Apache APISIX 通过插件实现。
从以上三点看来,Apache APISIX 在功能上完全能够胜任服务网格的边车代理。
Apache APISIX 作为数据面带来的问题
既然理论上 Apache APISIX 可以作为一个边车代理,那么在实际使用的时候一定还会遇到一些其他的问题。我主要列了三个最主要的待解决的问题。
我们应该去怎么保证 Apache APISIX 在服务网格的场景下能够及时的去获取到变更?这其实是和前面我们那张 Apache APISIX 架构图中的 etcd 集群有很大的关联,原因会在后面进行解释。
服务网格除了数据面以外还有一个控制面,数据面既然使用了 Apache APISIX,那么控制面需要怎么去考量?是使用现有的去和它们对接,还是说我们自己造个轮子,然后做一个完完全全适用于 Apache APISIX 的控制面?
如果我们需要对 Apache APISIX 进行改造,那么我们必须要考虑语言的生态以及平台生态问题。因为 Apache APISIX 是基于 Lua 的,所以我们需要考虑 Lua 和 OpenResty 的问题。
那么就以上三个问题,我们来一一解答。
首先是第一个问题,怎么去保证在服务网格场景下,Apache APISIX 能够及时获取到配置变更?前面提到它是在网关的场景下,通过 watch etcd 去获取 etcd 中的配置。etcd 是一个强一致性的 kv 存储,使用 raft 分布式共识协议来保证数据的一致性。正是因为 etcd 使用了这个协议,导致 etcd 不具备高性能,也就是说,etcd 所能承载的连接数不能太多。而在一个服务网格场景中,尤其是在边车模式下, 每个服务实例都有一个边车代理,当服务网格里面的实例数量变得很庞大的时候,边车代理也就是 Apache APISIX 的数量会很大。那么这个时候可能 etcd 集群就会变成瓶颈。这不是我们想要的结果。所以就必须要通过一种方式来解决这个性能瓶颈的问题。
那么我们有两个方法,第一个是我们干脆不用 etcd 集群,我们去改 Apache APISIX,去换成另外一个更强大,更适合服务网络场景的 kv 存储,或者是其他的一些配置中心方案。虽然这个问题可以通过引入 etcd proxy 解决,etcd proxy 实际上也确实能够帮助去减少一些到 etcd 集群的连接数。
很遗憾的是,Apache APISIX 在和 etcd 通信的时候,并不是直接使用的 gRPC 协议。因为 Lua 的生态问题,Apache APISIX 使用的是 HTTP restful 的通信功能,由 etcd 集群每个服务实例本身的 gRPC gateway,去把 HTTP 请求映射到 gRPC 请求,来完成这样一步转换。etcd proxy 并没有 gRPC gateway 这个功能。如果我们想要使用 etcd proxy 的话,我们就必须对 etcd proxy 进行大量的改动。也就是说我们要么去改 etcd,要么去改 Apache APISIX。在回答这三个问题完之后,给大家看一下我们的解决方案。
第二个问题,怎么去选择一个服务网格的控制面?控制面的话有好几类,一类是实现了 Envoy 的 xDS 协议,然后使用 Envoy 作为数据面的,例如 Istio、Kuma 和 OpenServiceMesh,都是对接的 xDS 协议。第二类是像 Linkerd 和 Consul 这样的一些组件,相对来说是自己玩自己的, 数据面和控制面都是自己家的,然后专用性会比较强一些,但是普适性没有那么好。这两类都不太适合 Apache APISIX。还有一个方案是,我们可以再去造一个轮子出来,然后让它们跟 Apache APISIX 完美的结合。当然这样也是一个好的方案,只不过开发成本上可能会比较高一些,周期也会比较长。
然后是第三个问题,Lua 和 OpenResty 的生态问题。Apache APISIX 大量的代码都是使用 Lua 写的。虽然 Lua 很强大,但是它有很多工具都是缺乏支持的,比如说它没有很好的 gRPC 支持。由此引出了另外一个问题,OpenResty 的社区是不活跃的,导致我们刚才所提到的一个问题,如果要去改 Apache APISIX,对接另外一种配置中心,这个时候很有可能我们需要自己去造一套轮子去对接配置中心,去写一套配置中心的 OpenResty Lua 版本的 SDK。这样一来,我们背后没有社区支持,相当于我们出了任何问题都需要自己承担,所以这个过程会比较困难。
也就是说,如果我们仅仅是想通过改造 Apache APISIX,让它去完美的适配到服务网格体系中,那么可能会比较的困难,而且成本会比较高,有点得不偿失。所以得出一个结论,仅仅使用 Apache APISIX 并不足够在让它在服务网格领域中发挥出所有能力。
APISIX Mesh Agent 研发目的与架构设计
既然只靠 Apache APISIX 实现不了服务网格,我们是不是可以给它找一个帮手?计算机科学里面有一句名言,相信大家都听过,任何的问题都可以通过引入另外一个中间层来给它解决掉。这就是为什么有 APISIX Mesh Agent 这个项目,APISIX Mesh Agent 就是中间层。PPT 底下这个链接是 APISIX Mesh Agent 的项目主页,大家如果有兴趣的话可以关注一下,我们在积极地开发这个项目。
APISIX Mesh Agent 完成了什么功能?我们知道 APISIX Mesh Agent 是 Apache APISIX 和服务网格控制面的中间层。作为一个中间层,它完成了以下的一些事情,首先 APISIX Mesh Agent 会从控制面去获取配置变更;其次 APISIX Mesh Agent 还模拟了 etcd。
这里说的模拟 etcd 指的是,从 Apache APISIX 的角度来看, APISIX Mesh Agent 它就是一个 etcd 的 server,Apache APISIX 就可以从 APISIX Mesh Agent 这边通过标准的方式去获取到 etcd 的里面的一些配置变更。比如说像 Apache APISIX 的路由、上游、证书这些配置的变更。最后,APISIX Mesh Agent 帮助 Apache APISIX 拦截了出入流量。这就好比是 Istio 的 pilot agent 所做的事情,pilot agent 在一个服务的实例启动前会把 pod 里面的 iptables 规则给建立好,从而使得后面的 Envoy 能够能够拦截到所有的出入流量。
APISIX Mesh Agent 和控制面交互,需要有一套通信体系。按我们前面所提到过的几种现有的服务网格控制面来说的话,有基于 xDS 的,还有 Linkerd 和 Consul 这种普适性很低的。因为 xDS 这个协议现在很流行,普适性也比较强,所以我们最终选择的是使用 xDS 协议和控制面交互,而不是说和一个固定的控制面绑定。因此,只要是实现了 xDS 协议的控制面,例如 Istio、Kuma 和 OpenServiceMesh 都可以作为 Apache APISIX 的控制面。因为考虑到自研控制面的周期会比较长,所以我们也没有选择自研。
第一点是 APISIX Mesh Agent 会从控制面去获取配置变更并转换格式。当 APISIX Mesh Agent 通过 xDS 协议获取到配置以后,它会把这些数据格式从 xDS 所定义好的格式转换成 Apache APISIX 的格式。
比如说 APISIX Mesh Agent 会通过 RDS 这个路由发现协议里获取到的 route configuration,改成 Apache APISIX 的 route;以及把通过 CDS 这样的集群发现协议获取到的 cluster 转换成 Apache APISIX 的 upstream;或者通过 EDS endpoint 发现协议,把 endpoint 转换成 Apache APISIX upstream 后端的节点。
这些转化后的数据都会存在 APISIX Mesh Agent 的内存里面。因为 APISIX Mesh Agent 自身的数据格式相比 xDS 来说非常紧凑,所以这些数据转换以后再存在 APISIX Mesh Agent 内存里,相比于直接存储 xDS 的格式会占用更小的内存。
第二点是 APISIX Mesh Agent 模拟了 etcd。etcd 有 v2 和 v3 两个版本,而 Apache APISIX 现在只支持 v3 版本,因为 etcd v2 的功能比 etcd v3 较弱一些,所以已经被废弃了。APISIX Mesh Agent 模拟是 etcd v3 的 API,然后把它自身伪装成了一个 etcd 集群。那么 Apache APISIX 可以在零改动的情况下对接 APISIX Mesh Agent,然后从中获取到配置。
前文也提到过,数据源从 xDS 获取来的,或者说是从服务网格控制面获取来的。APISIX Mesh Agent 把这些数据转换成 Apache APISIX 的格式以后,存在 APISIX Mesh Agent 内存里面,然后再通过模拟的 etcd server,把数据传递到了 Apache APISIX,模拟起了一套 etcd 集群。因为现在是每个 Apache APISIX 边上都会有一个 APISIX Mesh Agent 中间层,那么就不需要再引入一个完整的、真正的 etcd 集群解决问题。
这样一来,就把前面所提到的,etcd 没法承受太多的客户端连接数的问题给解决掉了。我们通过分布式引入到每一个边车,引入到每一个 Apache APISIX 实例本地,解决了这个问题。
以下是一张使用了 APISIX Mesh Agent 和 Apache APISIX 作为服务网格边车代理以后的架构图。大家可以看到上下两层分别是控制面和数据面。在控制面这边,你可以使用 Istio 或 Kuma。重点看一下数据面这边,数据面这里的每一个方框都是一个服务实例,那么这里面会有像实例本身的容器,以及 Apache APISIX 的边车代理容器,在边车代理容器里面其实是有两个进程,一个是 Mesh Agent,一个是 Apache APISIX。
Apache APISIX 它实际会处理真实的流量,然后和本地的实例以及和实例所需要调用的目标服务的实例交互。可以看到这里有一条蓝线,APISIX Mesh Agent 和服务网格的控制面进行了交互,可以通过 xDS 协议把配置从控制面下发到的数据面,从而最后把这个数据传递给本地的 Apache APISIX,使 Apache APISIX 在服务网格中完美地运行起来。
使用 Apache APISIX 为一个服务网格的数据面边车代理的优势是什么?第一点是 Apache APISIX 的高性能特性。我们前面在讲到 Apache APISIX 的时候提到过它的高性能。既然 Apache APISIX 在网关层有这么高的性能,那么它在服务网格当中也能有这么高的性能,这一点是天然的。
第二点是扩展性。我们可以去复用 Apache APISIX 所有的插件,只需要做把这些插件的配置能够从 xDS 格式转换 Apache APISIX 的格式,就可以让这些插件跑起来。如果说现有的插件还是满足某些需求,那么我们可能需要对 Apache APISIX 做一些二次开发。
由于 Lua 简单易上手,可能花一个下午的时间就能学会它,所以你需要使用 Lua 去做二次扩展的时候就会变得很简单,二次开发的成本也会比较低。就算有人可能觉得 Lua 语言太 low,不想去学,那么那也没关系,Apache APISIX v2.6 开始支持多语言插件。你可以使用 Lua、 Go 、或者是 Java 去做一些插件开发,总有一款适合你。
所以一定能够找到一种合适的方法,对 Apache APISIX 进行扩展,满足你的业务需求。最后一点,如果使用 Apache APISIX 作为服务网格,那么你就可以统一南北向流量和东西向流量的技术选型。运维同学也不再需要去维护两套技术体系,降低运维成本。
最后一点,控制面对于数据面的第一选择不是 Apache APISIX。一定会存在一些情况,可能控制面和 Apache APISIX 是不太适配的。如果说后续我们发现,现有的组网和控制面,都没法很好地和 Apache APISIX 适配和兼容,我们也会考虑再从头造一个服务网格的控制面,使得它和 Apache APISIX 能够完美地结合起来。这个就是我们接下来 APISIX Mesh 发展的几个方向。
未来展望
首先是对 xDS 协议提供的更加完善的支持。比如,我们会支持更多的认证功能,支持更多的请求转换,支持更多的协议转换;以及支持跟服务鲁棒性有关系的能力,例如故障注入等。这些功能都属于 Apache APISIX 已有,只是需要把适配做好,使得 APISIX Mesh Agent 能够从控制面完美地传递到数据面 Apache APISIX 这边,那么我们做的完全就是一层翻译的工作。
第二是完善可观测性与 APISIX Mesh Agent 适配。和第一点一样,可观测性也属于 Apache APISIX 已有功能。我们要做是 APISIX Mesh Agent 适配的。可观测性也是 APISIX Mesh Agent 的一个非常大目标。我们需要把 APISIX Mesh Agent 的运行状态更好地展示给大家,展示到运维同学的眼中,从而对服务的运行有更好的理解。
今天的分享就到这里。谢谢大家!
评论 1 条评论