在生产中使用Istio我们学到了什么

28次阅读

共计 3741 个字符,预计需要花费 10 分钟才能阅读完成。

首先,给大家简单介绍一下 Istio,Istio 是一个 Service Mesh 的开源框架,来自 Google,大部分使用 Go 语言来开发,是 Service Mesh 的集大成者。

Istio 数据层面主要使用 envoy,Istio 开发了一些 filter 扩展 envoy 的功能,这些功能主要集中在 mixer 上。Istio 是新鲜出炉的技术,2017 年 5 月 0.1 release 版本横空出世,不过版本更新迭代很快,最新的版本是今年 3 月发布的,1.1.3 版。

Istio 简介

Istio 在逻辑架构上由数据平面和控制平面组成。数据平面是经典的实现方式,由一组以 sidecar 方式部署的智能代理(Envoy)组成。这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。

在每一个容器中注入了一个 sidecar,Istio 的 sidecar 称之为 Istio proxy,相当于 envoy 加上 Istio 开发的 envoy filter。Istio proxy 可以拦截整个容器的入口和出口流量。根据 sidecar 从数据层面接受到的规则和策略,进行一系列对于网络流量的配置和管理,比如路由管理,加密,遥测数据收集。控制平面负责管理和配置代理来路由流量,此外,控制平面还配置 Mixer 以实施策略和收集遥测数据。

Istio 有几大块功能,首先是流量控制,这个基本上是通过 Istio 里的 pilot 组件来实现的。从图上我们可以看到,rules api 就是 pilot 提供的抽象 API,用户通过 api 来配置相应的规则。Paltform Adapter 主要是兼容不同的平台。用户针对不同的平台实现服务发现等功能。基于用户的规则和从平台收集的数据,通过 envoy api 把具体的规则下发到各个容器的 Istio proxy。这里实现了大部分我们需要的流量管理功能,例如负载均衡策略,路由控制。我们可以把本来发给服务 A 的请求,都导向另一个服务。故障注入,为测试进行模拟。以及熔断,url 重写,重试,超时等等。

Istio 的第二块,主要是安全机制。我们可以对网格内的微服务通讯进行 tls 加密。在发起请求的 Istio proxy 对请求使用 tls 证书加密,在接受请求的 Istio proxy 对这个请求解密,这对于应用的开发者是无感的。Istio 最优秀的地方就是对于业务开发者的基本无感。这些网络调用相关的功能和策略被抽离出来,不用再放在应用的代码里。

Istio 还提供了类似 k8s rbac 的权限实现方式。在 servicerole 定义使用 service api 的权限,通过 service role binding 绑定在 k8s service account 上,然后当应用通过 service accout 启动后, 该应用就有 service role 里定义的访问其他服务的权限。

下面一块是策略和遥测,这个是 Istio 中争议非常大的一个功能。好的方面是设计非常干净。Istio proxy 只需要把它收集到的 attribute 发给 mixer,mixer 的 Adapter 基于 attribute 来做相应的操作。一类 attribute 用来做策略检查,例如 Qouta,另一类用来做遥测数据收集,如 promothues。这其中体验不好之处就在策略检查上面。因为每个请求都需要做这个检查,这个检查目前看来会影响整体的性能。1.1 开始,Istio 提供了简单的方式可以关闭全局这个检查。如果你对性能要求比较高,目前最好还是先关闭。


Istio 1.1 的发布对性能进行了大幅优化。官方提供的数据是:1000 k8s service,2000 个 sidecar,每秒 70000 个请求在这个 mesh 网格中。这时候每个 proxy 使用 0.6 个 CPU,50M 内存来支持每秒 1000 个请求。Istio 的遥测,就是 mixer 需要 0.6 个 cpu 来支持每秒 1000 次的请求。这个地方并没有说是不是包括策略检查。我感觉是不包含的。pilot 需要 1 个 cpu 和 1.5G 的内存来做服务下发。最后,Istio proxy 对服务间调用的性能影响是 tp90 8ms。

微服务体系带来的问题

原来的微服务体系中都面临哪些难题呢?

首当其冲是定位和调试困难。当遇到 bug 或者性能问题,原来的方式基本都是逐级排查,从客户遇到问题的地方开始。因为一个深层次的微服务会引起一系列的上层微服务出现问题。如果发现两个服务直接之间的整体调用性能不好,这个时候哪怕你找到某一次性能差的日志或数据,基于这个数据和日志找出来的原因不一定是 root cause。这种排查问题的方式就像烽火台,你只看到了最近的烽火台,并不知道起点在哪。

第二,测试时数据会有遗漏,缺少完整的测试数据。

最好的测试数据是线上的真实数据。但是线上的请求采集下来还需要独立开发相应的程序,整体实现很麻烦。另外,如果测试微服务的错误处理,对于 QA 的黑盒测试来说,这还需要一个可配置的错误生成器。这点对于测试也是一个独立的工作。

第三,缺少上线流程。

我们原来使用独立的微服务作为开关,来判断是否加载新功能。在新的功能代码外层加上调用该微服务的代码,根据返回值来判断是否执行新功能代码,上线完成后再把开关代码删掉,的确有点麻烦。上线前需要修改源码增加控制,上线完成后还需要在源码中删除这些逻辑。没有简单、无侵入的金丝雀和灰度发布的实现方式。

第四,微服务间的网络调用策略配置不灵活。

不同的客户环境需要使用不同的网络策略,例如重试,超时等等设置,如果对应的设置没有通过配置文件暴露出来,就只能对代码进行修改。而且这些代码在业务代码里,统一的维护和升级都需要独立的流程。

灵雀云 ASM 如何解决上述问题?

灵雀云从去年就开始有针对性地在 ASM 产品中解决这些问题。下面是我们 ASM 的总体架构图:

ASM controller 是我们开发的 K8s controller,主要处理我们自己定义的 crd,以及做一些自动化的 K8s 资源处理;Dablo 是 ASM 的前端界面;controller 和 diablo 都会直接和 K8s api server 来通讯;Istio gateway 是 Istio 用来服务南北流量的接口,主要用来暴露集群里的微服务;jaeger 分为 collector 和 query;collector 直接从 Istio proxy 收集上报的调用链路数据,目前数据格式还是用的 zipkin,jqeger query 是用来显示调用链路的;为了做 namespace 的数据隔离,我们对 jaeger 的组件都做了相应的改造;存储方式是 ES;遥测数据用 prometheus 来存储,数据从 mixer 收集而来;diablo 通过直接调用 prometheus api 来获取。

关于定位和调试,灵雀云主要通过两方面来解决:一方面使用 promothues 里的遥测数据来绘制实时的拓扑图。展示调用关系,以及调用数据,包括流量,性能,错误率等。另一方面,通过 jaeger 的调用链来解决。我们可以查看完整的调用链路,具体到某次调用的时候,请求的内容,以及返回的状态码。

下面是我们预览版产品中的一些截图。这个截图来自于灵雀云 PaaS 平台的数据。我们可以看到整个平台所有组件之间的调用关系,右边是某两个组件之间的调用数据,包含调用次数,rps,以及响应时间这样的性能指标。这些可以帮助我们快速做定性的判断。



后面两张图是我们嵌入的 jaeger query 组件。当前面做了定性判断后,这里可以查询具体的问题是什么,通过过滤条件来缩小范围,然后具体看某一个请求的详细信息。

为了使用 Istio 这些功能,需要做些什么配置呢?
1.所有的微服务中注入 sidecar;
2.pod 里的 container 声明了自己监听的端口,保证能够拦截入口流量;
3.pod 的 label 需要 app 和 version 两个 key;

  1. K8s service 里声明的 port 都必须包含 name 字段,根据使用的协议 name 的格式有一定的规则。例如使用是 http 协议,name 可以为 http 或者以“http-”开头;

5.服务调用的代码需要做稍微的改造,需要获取上一个请求 header 里的一些字段,包括 request id,trace_id, span_id。把他们设置在 header 中传递给下一个调用。这个在 Istio 官方的文档里可以找到。
除了最后需要对代码做少许的修改,前面都只是需要修改服务部署的 yaml。

通过 Istio 提供的流量镜像功能,我们可以很容易的使用生产环境来测试新的代码。只需要把测试代码通过一个独立的应用直接发布到生产环境,然后通过配置把流量拷贝一份调用这个测试代码的应用就好了。

Istio 的错误注入功能很容易模拟返回错误的状态码,增加请求返回的延迟。

在安全上线方面,在生产环境同时发布新、老版本,通过拓扑图和调用链的数据,来观测新版本是否可以正常工作。我们通过流量的权重来实现灰度发布,通过一些规则设置来实现金丝雀发布。加上前面的生产环境测试,对于安全上线提供了很大的保证。

最后,灵活的网络策略。通过 istio 的 Virtual Service 和 Destination Rule 这两种资源,实现灵活的配置微服务间的网络访问策略。终于不用把这些策略的配置写到我们的代码里来,Istio 的 virtual service 和 destiinatio rule 就完全实现了。

正文完
 0