其他分享
首页 > 其他分享> > 扇贝 Service Mesh 发展历程

扇贝 Service Mesh 发展历程

作者:互联网

 李有才 分布式实验室 

图片

伴随 Kubernetes 的兴起,越来越多的公司开始实践 Service Mesh,以应对规模越来越大的微服务间通信问题。本文主要介绍了 Service Mesh 演进的常见形式,扇贝 Service Mesh 选型落地 Envoy 的过程,以及如何基于 go-control-plane 搭建自己的 Envoy xDS 服务端。希望能对在大家落地 Service Mesh 的过程中提供一点思路。


扇贝业务现状

图片


首先跟大家简单说明一下扇贝的业务状况,扇贝三年前就开始实行 Docker 化和微服务化来应对需求的快速变更。随着公司规模扩大,组织结构也相应的调整拆分,业务团队之间变得独立,微服务数量开始快速上升,因此 2018 年初我们的技术架构也开始转向 Kubernetes 来应对挑战,Service Mesh 的落地便是伴随着转型 Kubernetes 开始的。
目前扇贝的微服务有五百多个,整个 Kubernetes 集群有七十多个节点,运行了五千多 Pod,承担每天数十亿次的内外部 API 调用,峰值 QPS 过万。


Service Mesh 的演进形式

图片


Service Mesh 一词出现的时间虽然较晚,但宽泛的来讲,他所追求的通信与业务解耦的理念却有很长的发展的历程,通常会有下面几种演进形式:

扇贝 Service Mesh 的选型落地

图片


图片


扇贝早期的微服务架构采用的是传统的 Proxy 层方式,由 Nginx + Consul + Consul-Template 来实现。微服务启动时向 Consul 进行注册,同时提供健康检查入口。然后 Consul 会进行主动健康检测,Consul-Template 通过长链接到 Consul 监测服务变化,渲染 Nginx 的配置模板,在监测到发生变动时根据模板生成新的配置文件,发送 signal 热重启 Nginx。这种架构在服务变动较少时还是挺好用的。但是当我们需要蓝绿部署、灰度测试、流量镜像等高级一点的功能和更详细的统计指标时就比较难办了。
从早期的微服务模式演进到 Kubernetes 时,公司的技术方案是比较开放激进的,想要 Kubernetes 和 Service Mesh 同时推进。当时可选的方案有 Linked 和 Istio 两种,考虑到 Istio 背后的 Google 和社区繁荣程度,我们认为 Istio 会成为主流,所以在技术预研和测试的时候部署了 Istio,令人尴尬的是,当时 Istio 太早期了,版本还是 0 点几,我们发现服务非常不稳定,各种莫名其妙的 5xx 错误。但是我们又实在不想放弃这种符合未来趋势的服务通信层方式,所以我们做折中选择——只使用它的数据平面组件 Envoy。一方面我们获得的 Envoy 技术积累待 Istio 稳定后在切换过去也还是有用的,另一方面保留 Envoy 也使我们拥有了更先进的 Proxy 层,能够应对 Kubernetes 架构下服务频繁快速变化的状况。
之所以说 Envoy 是现代化的代理,是因为相比于比于传统的 Nginx 代理,Envoy 有原生的 GRPC/HTTP2(我们内部服务调用是基于 gRPC 的)支持,能够提供非常丰富的统计指标(比如基础的请求延时,重试次数等),以及我们后面会提到的能够运行时动态更新配置的 xDS 协议。


Envoy 在扇贝的部署架构

图片


图片


由上图可以看出,扇贝使用的并不是严格意义上 Service Mesh 所定义的 sidecar 模式,而是以daemonset 的形式部署的 Envoy,这是考虑到我们脱离了 Istio 控制平面,自己做 Sidecar 模式对技术团队来说难度过大。使用 DaemonSet 的部署形式降低了我们要管理的代理数量,能减少资源占用,升级 Envoy 也相对容易些。当然,缺点也是显而易见的,无法做到 Sidecar 那样细粒度的流量管控,好在目前我们还能接受这一点。
另外一个可以从图中看出的点是,我们的 Envoy 其实是分成两组的。一组承担了 API Gateway 的功能,代理集群南北流量。另一组则代理了集群内的东西流量。API Gateway 启用了 jwt 解析,ratelimit 等功能。内部服务代理则实现了服务鉴权功能。


xDS 协议介绍

图片


前面提到我们只保留了作为数据平面的 Envoy,没有控制平面的话看起来与传统的 Proxy 层也没有太大差别。这里就要提到注入灵魂的 xDS 协议了。xDS 是一组协议的统称,包括:

等,这些协议是 Envoy 为实现动态配置而的定义接口。xDS 协议有v1,v2两个版本。v1 基于RESTful JSON API,在 1.8 版本之后已被彻底淘汰。v2 版本则基于 gRPC 和 protobuf3(也可以通过 rest api 访问)。我们以 eds 为例来详细看一下 xDS 协议。
eds 期望我们实现如下 gRPC 接口:

  1. POST /envoy.api.v2.EndpointDiscoveryService/StreamEndpoints


  2. message DiscoveryRequest {

  3.  string version_info = 1;

  4.  core.Node node = 2;

  5.  repeated string resource_names = 3;

  6.  string type_url = 4;

  7.  string response_nonce = 5;

  8.  google.rpc.Status error_detail = 6;

  9. }


该接口应该返回类似下面的响应:image.png

xDS 使用的是双向流式 gRPC,每个 xDS 流以 DiscoveryRequest 开始,通过 type_url 来区分请求的不同资源,具体到 eds 的类型是 type.googleapis.com/envoy.api.v2.ClusterLoadAssignment。version_info 和 nonce 表示的是上一次成功应用的 response 的版本号和 nonce 值。如果 Envoy 拒绝配置更新 X,则回复 error_detail 及前一个的版本号。简化过的 Request 和 Response 的顺序可以用下图来表示:

图片


其中字母缩写的含义为:


基于 go-control-plane 实现xDS服务

图片


上面简略的示意图只展示了最基础的情况,如果有多个 xDS Server 端(响应不一致)和和多种类型的 xDS 资源时(有依赖关系),正确的处理请求响应是一件很有挑战性的工作。而 go-control-plane 则帮我们处理好了这些通信细节,我们只需要实现资源的更新即可。go-control-plane 是使用 Go 语言开发 Service Mesh 控制平面的基础设施(Istio 也用它), 主要包含了 Configuration Cache 和 API Server两个组件。
API Server 是 Envoy 定义的 data-plane-api 的一个实现,可以直接用于生产环境。Configuration Cache 用于存储 Envoy 的配置信息。比如 endpoints、clusters 等,是我们实现自定义 xDS server 时主要操作的部分。
下面我们通过几段代码展示一下如何实现一个简单的 eds server。
图片
这一部分首先实例化了一个 cache,这个 cache 的维护是要我们重点编码实现的地方。然后创建运行了一个 grcp server,我们向 server 注册了 eds 服务,当然我们也可以再注册 cds、rds等。

图片


这段代码示例了如何操作 snapshot cache。我们通过 Kubernetes 提供的 endpoints watch api 去监听集群内 service endpoints 的变化事件(ADDED,MODIFIED,DELETED),根据事件类型和包含的 objects,具体到这个场景就是 Kubernetes endpoints,转换成符合 envoy 格式的 endpoints,生成新的 snapshot,更新缓存。这样一个基本可用的 eds server 搭建起来了。


标签:服务,Kubernetes,Service,扇贝,Envoy,Mesh,xDS
来源: https://blog.51cto.com/u_15127630/2764301