![Istio 在百度百亿级流量⽣产环境的研发实践](https://static001.infoq.cn/resource/image/87/b5/87f885373f42207379db1e3fc47fbbb5.jpeg)
Service Mesh 从诞生至今短短数年,就以席卷之势成为了云原生时代的通信设施标准,被奉为“下一代微服务”。然而由于像 Istio 这样 Service Mesh 中的佼佼者,其在对异构基础环境支持、私有协议支持、复杂流量调度策略、产品化和易用性、性能和可靠性等方面还存在诸多限制和缺陷,Service Mesh 的落地一直以来都令人谈虎色变。
2021 年 QCon(北京)全球软件开发者大会上,百度 陈鹏 分享了结合 Service Mesh 在百度公司 Feed、手机百度、百度地图等百亿量级流量的核心生产环境大规模落地的真实案例,分享了关于上述 Service Mesh 落地的诸多痛点问题的深入思考和实践经验。
以下是演讲内容整理,以飨读者。
决定业务有没有必要上 Service Mesh,在什么程度上使用 Service Mesh,最重要的依据是业务现状。所以今天我会先分享百度服务治理的现状,再分享百度在落地 Istio 时遇到的问题,以及百度是如何解决的。
百度的 Service Mesh 经历了三个阶段:内部探索、跟进探索、拥抱开源。
2013 年,百度研发了独立的 Sidecar 和 Proxy 代理一些流量,这是 Service Mesh 在百度的萌芽;2016 年,百度网盘大规模落地的 UFC 系统和现在 Service Mesh 的理念和架构功能已经非常相似了,也是用 Proxy 代理流量控制中心;2016 年的 9 月份,Service Mesh 的概念首次被提出后,百度立刻意识到了它会带来的变革。所以 2017 年,百度采用 Go 语言自研了一套 Service Mesh 系统—— BMesh 。
与此同时,社区发展迅速。2018 年的 7 月, Service Mesh 的明星产品可用于生产环境的 Istio 1.0 版本发布了。面临这个情况,百度最终决定拥抱开源。因此 2019 年,百度研发了基于社区的 Istio + Envoy 的解决方案,目前已经大规模落地了。
为什么要引入 Service Mesh?
Service Mesh 主要可以解决三种问题:
第一,多语言技术栈能力不统一。百度内部的语言生态非常复杂,内部应用最广泛的框架是基于 C++的 bRPC,还用了 Java 的 Spring Cloud 生态、PHP 的 RAL 框架、Go 的 GDP 等。同时使用了多个语言,但各个框架的使用方式、配置方式以及服务治理的能力天然是不统一的,这必然会损害用户体验和后端能力。
第二,服务治理周期长。分布式的能力总和业务进程耦合在一起,所以无论是技术库的维护升级,还是调整策略,服务治理的周期都非常长,成本也非常高。
第三,服务产品能力非常弱。大部分服务都是以配置文件为主,没有接入配置中心,可视化能力非常弱,没有平台化或产品化的功能,如配置变更版本追溯的能力,审查、审计的能力等,用户体验很差。
Service Mesh 进程隔离的特性,很好地解决了这些问题。很多公司都想落地 Service Mesh,但是落地的过程却很不顺利。我们以 Istio 为例,看看 Service Mesh 在生产环境落地面临的问题。
Istio 在生产环境落地面临的问题的百度解决方案
Istio 在生产环境落地面临很多问题首先,Istio 原生是为了弥补 Kuber-Proxy 流量调度上的缺陷而设计的,所以它在很多方面深度依赖 Kubernetes,而大多数公司还没有完全迁移到 Kubernetes 上,这必然影响 Istio 的落地;其次,Istio 只支持 HTTP 协议,但是很多公司使用的是内部私有协议,而且 Istio 支持的策略也很有限,因此 Istio 落地时,我们主要专门解决这些协议和策略的问题;最后,Istio 的稳定性、性能、产品能力也会影响业务方的使用规模。
那么这些问题,百度是如何解决的呢?我们先看第一个问题, Istio 对 Kubernetes 的深度依赖。
对 Kubernetes 的依赖问题
Istio 对 Kubernetes 依赖分为三部分,第一部分是 Sidecar 的注入和管理。
Kubernetes 的 PaaS 的可编程性和接口比较好,但是我们自己的 PaaS 或类似平台缺乏这样好的规范或接口,这样的话如何注入 Sidecar 呢?注入之后,如何大规模自动化管理 Sidecar 呢?面对这些问题,百度落地 Istio 时联合了内部的 PaaS 团队,进行了支持 Sidecar 模式的联动研发。
Istio 对 Kubernetes 依赖的第二部分,是 Istio 的各类配置依赖了 Kubernetes 各种组件,尤其是依赖了 API Server 和 etcd 做流量调度策略的存储。API Server 和 etcd 是可以进行独立部署的,所以百度保留了这部分组件。
最头疼的是第三个问题,Istio 依赖于 Kubernetes 的服务和流量模型,比如说服务调用基于虚拟的 ClusterIP,比如流量劫持使用了 Iptables 或 IPVS。
由于一些性能稳定性、可管控性等等的原因,百度的生产环境是无法使用 Iptables,内部的服务发现也没有用 Kubernetes 的方式,所以这个问题是最棘手的,决定了百度的流量到底能不能够让 Mesh 接管。面对这个问题,百度选择了研发新的流量劫持模式——基于 Naming 的流量劫持。
在传统的这种软硬件环境下如何做 Mesh 流量的接管
基于 Naming 的流量劫持
百度的流量劫持模式是基于百度微服务体系的私有 Naming 系统的。
![](https://static001.infoq.cn/resource/image/14/79/14db872cea2yyfdabf7954b21e984c79.jpg)
图中左侧是 Consumer ,右侧是 Provide。正常 Consumer 调用 Provide 时,会去注册中心或本地的 Agent 查找下游的服务实例列表。
接入 Mesh 以后,第一步,我们会在 Mesh 的上层用户产品—— Console 上定义一个连接关系。图中定义的连接关系是 Consumer 会访问 Provide。借鉴 Kubernetes 的 ClusterIP 的概念,我们会给它分配一个本地回环地址。图中举例的本地的回环地址是 127.1.1.1,代表了访问的连接关系。
接着我们会把这个连接关系下发给 Consumer 侧的 Sidecar,即代理 Envoy。Envoy 会监听 127.1.1.1 的地址,服务会监听另外的端口。
第三步我们会联动 Envoy 和 Naming Agent。Envoy 会把自己的监听的地址注册到 Naming Agent 里伪装自己。此时业务的 Naming Lib 去访问 Naming Agent 拿下游,拿到的是本地 Envoy 的地址。 图中 Envoy 把自己注册成了 Provide ,地址是 127.1.1.1:8000,不是真实下游 Provider 的 10.1.1.2:3000。
第四步 Consumer 会发出请求,而第五步返回的是 127.1.1.1 的 8000 端口。此时的核心思想是 Envoy “欺骗” Naming Agent 自己是下游的实例,把流量导到自己。 Consumer 以为它请求的是真正的 Provide ,实际上是把流量发给了本地的 Envoy。Envoy 收到以后,会去真正的 Naming Agent 里做解析。
这样可以实现在 Sidecar 侧做服务发现。这样做是因为策略配置变更频率低,而服务端点的这种实例状态信息的变化频率高,控制面做服务发现压力很大。
另外第 7 步里 Envoy 去请求下游时,Inbound 的流量是可选的。图中既可以请求 Provide 真实监听的 3000 端口,也可以请求 Envoy 监听的 3001 端口。这样,出口流量必须经过 Envoy ,但是入口流量却不一定。
这是因为业务方会担心多过一次 Envoy ,会影响耗时和性能。而 Envoy 的能力,比如说负载均衡、超时、重试、熔断等都集中在上游,下游的功能很少,所以我们把下游做成了可选的模式。当然,如果你希望拥有 Envoy 全部功能,而且对性能不那么敏感的话,可以在 Consumer 和 Provide 侧都接入 Sidecar。
原生 Istio 会给任何 Sidecar 推送全集群所有服务的信息,比如 Consumer 可能只需访问一个服务 Provide,而 Istio 会给它所有服务的信息,这对 Consumer 自身的 Sidecar 资源开销和控制面推送的压力都很大。
而我们的方案定义了 Link 模型,这样可以做到精准下发,Consumer 只会收到 Provide 相关的配置,XDS 的推送量可以从 O(N)降到 O(1),Envoy 的内存占用也会大幅度减少。
面对故障,我们在 Envoy 和 Naming Agent 之间设置了一个非常敏感的 HeartBeat——心跳探活机制(上图中红色的部分)。一旦检测到 Sidecar 故障,我们会及时地去掉 Envoy 注册的伪装实例,Consumer 服务会在下一次解析周期里近乎实时地回退到直连模式。因此业务方不必担心 Envoy 是否挂了。
因为 Naming Lib 适用范围很广,所以通过修改 Naming Lib 做流量劫持的方案适用范围也很广。而且在没有使用容器网络的主机网络下,我们通过收集由 PaaS 动态分配的端口,也可以使用这套方案。
融合 Envoy 和 bRPC 的性能优化
在毫秒级别的请求、资源紧张、CPU 内存敏感的场景下,Envoy 无法满足业务方的需求。所以我们基于 bRPC 和 Envoy 各自具有独到的优势,做了深度二次开发。
bRPC 实现了一个非常高性能的用户态的线程,不完全等同于 Go 语言的协程,但是和用户态很相像,有高性能的 IOBuf 库,可以高效地做 Socket 处理。它还有优秀的 IO 机制,擅长处理长尾等等场景。
而 Envoy 的扩展机制非常灵活,在 L3、L4、L7 层都提供了很多 Filter 扩展机制;此外, Envoy 本身也实现了非常多丰富的策略,再加上它有 XDS 协议,可以进行动态地配置分发。
我们融合了这两套系统的优点。我们使用 bRPC 的内核组件重构了 Envoy。融合之后,平均延时和 CPU 开销⼤幅降低,效果如下图所示:
![](https://static001.infoq.cn/resource/image/45/5a/45507584fd76f19376acd061a26d705a.jpg)
⽀持私有协议和⾼级策略
面对原生 Istio 不支持私有协议或一些高级策略的问题,百度也进行了相关优化。
因为 Istio 的扩展架构比较灵活,所以百度基于 Istio + Envoy 的扩展机制,全面支持了大多数私有协议,包括百度内部标准协议 Baidu STD、一些比较老的 Nshead、HTTP、Java 生态的 Dubbo 协议等。
Istio 每新增一种协议,都需要制定一种语法,描述该协议流量分发方式。百度把这些协议统一为了标准的协议框架,减少了很多的重复开发。真正在数据面做流量转发时,我们可以根据请求 HEAD 的特性做协议嗅探。
高级策略方面,百度在 Envoy 里做了加强版的 BackupRequest——DynamicBP。
BackupRequest 的核心是快速重试:在一个请求还没有回来时,就发出一个新的请求,优先使用先回来的请求结果。重试在很多场景下是高危的,而且在延时不太稳定的场景下,很难确定该在什么时候进行重试。
DynamicBP 会动态计算一段时间间隔的超时比例,借此设置重试时间。比如说对超过 99 分位值的长尾 Request ,才进行 BackupRequest。这能避免雪崩,更安全。
由于 Mesh 的控制面可以对网络行为进行动态地编程、修改配置、实施生效,服务治理效率会大幅提升。之前大规模的服务治理调整需要耗费半年时间,现在只需要几天,明显提升了故障容忍的可用性。
除此之外,百度实现了和测试、混沌相关的流量复制、流量镜像和一些其它高级负载均衡策略。这样业务方接入 Mesh 后,不仅能力不会变少,还获得了一些新的策略。线上实际效果如下图所示:
![](https://static001.infoq.cn/resource/image/f4/7f/f4fe77178a40f763d1445252733a567f.jpg)
此外,Mesh 与框架语言无关的特点可以赋能其它的框架和语言。所有框架和语言可以共享在 Mesh 里实现的策略。比如下图中的框架原先不具备某种高级的能力,所以前面抖动比较厉害,接入 Mesh 以后复用在 Mesh 里移植的能力,稳定性大幅度提升。
![](https://static001.infoq.cn/resource/image/f0/13/f09126a3468aedb9fc2a396616eb7313.jpg)
如何解决 Istio 原⽣产品能⼒缺失
Istio 原生产品的能力本来就有缺失。比如 CRD 接口非常多,但是官方管理接口的 Kiali 基本上不可用;还有配置动态分发的安全问题,以前改配置有和 PaaS 结合的审计系统,接入 Mesh 后的监管问题。
面对这些问题,百度聚合了丰富的服务治理策略,可以让不了解 Mesh 的人,在良好的 UI 体验下完成服务治理。
一方面,百度集成了很多运维场景问题的解决方案,比如说跨机房切流,如何实时把流量切到其它机房,甚至按比例切流;再比如实时地关掉某一个链路的服务网格的开关;还有如何快速故障⽌损等等。
另一方面,如果 Mesh 上线后有任何问题,业务方可以一键回滚到对任何指定版本,及时地规避在线上遇到的问题。
当然,百度对任何一次服务变更都有详细的记录,会清晰地展示出操作者、操作时间、操作详情以及概述,便于审计或追查。
百度在 Mesh 上还做了很多平台化的工作。比如混沌平台可以和控制平面对接,负责调度任务,进行效果评估,而服务网格负责执行策略。图中 App1 调用 App2 边车 时,需要注入一个延时故障或一个百分比故障验证特定的 App2-3 实例。这时混沌平台可以通过监控平台收集反馈指标。
![](https://static001.infoq.cn/resource/image/60/c8/60cf8ef347042754d35444a679662cc8.jpg)
再比如线上运维。线上运维最头疼的就是容量压测,直接压测可能会把服务压垮,所以常常需要在大半夜流量低时去压测,运维人员也会非常辛苦。有了 Mesh 以后,可以利用 Mesh 精准的动态分流能力,让容量压测平台对接 Mesh。如下图所示,可以对下游某一个服务的实例,按百分比进行导流,让它接受比较大的流量。根据小部分实例的容量上限和实例的占比,就可以计算出服务在线上真实环境的容量水平。
![](https://static001.infoq.cn/resource/image/b4/5f/b435a358844395b12b663bc8be28565f.jpg)
百度智能云云原⽣微服务平台支持以上所有功能、协议扩展、性能优化,还支持公有云和私有化部署,感兴趣的同学可以去看一下。
百度 Service Mesh 落地经验总结
这里是一张百度 Service Mesh 的全景图,要想做 Service Mesh 落地,就要打通图中的所有环节。
![](https://static001.infoq.cn/resource/image/04/97/040bc283558fe8d4396c4d8yy9505e97.jpg)
当前百度使用 Mesh 的业务很多,包括手机百度/Feed、百度地图、小程序、好看视频等。切入 Mesh 的服务体量有几千个,实例数在 10 万左右,每天流量的 PV 是百亿级别。
通过接入 Mesh ,百度实现了跨语言统一的服务治理,提升了服务的可用性,微服务治理的周期从月级缩短到了分钟级。同时,服务变更的效率提升了 10 倍以上,再加上产品化、平台化等等的产品,运维人员的体验也明显提升了。
Istio 落地的核心要素总共有四个方面:务实、性能、稳定性和体验。这四个方面中最重要的是务实,也就是必须要适配公司的基础设施,实现公司生产环境里使用的策略或协议。这决定了能不能接 Mesh,后面三个方面不过是接入规模或体验的问题。
Istio 社区关注的是透明、安全性、可观察性、可扩展性等特性,百度关注的是 Istio 落地时的性能、流量治理策略、Sidecar 管理的可管控性、以及复杂网络或底层基础设施环境的支持。
未来,百度希望和社区继续融合,持续地跟进社区的新版本,引⼊社区⾼级扩展特性,同时百度也会向社区贡献性能优化和策略扩展相关的经验,和社区共建 Istio 生态。
嘉宾介绍
陈鹏,百度云原⽣技术专家,现负责百度 Service Mesh 的研发和落地,对服务网格以及云原生有深入研究。见证了百度 Service Mesh 从无到有,从摸索到大规模落地演变过程。致力于推动 Service Mesh 在多种场景下的落地。
ArchSummit 全球架构师峰会,将于今年 12 月 3 日与 4 日在北京国际会议中心举办,为此我们邀请国内一线技术专家来分享,更有经纬中国带来投资人视角下的中国开源与基础软件机会与挑战,微服务、云原生、低代码、机器学习等众多热门专题已经上线,希望对你有启发~
![](https://static001.infoq.cn/resource/image/2c/a0/2c64ea4eeca3704fa4c3e324957c75a0.jpg)
评论