本文整理自 GIAC(GLOBAL INTERNET ARCHITECTURE CONFERENCE)全球互联网架构大会,蚂蚁金服平台数据技术事业群技术专家石建伟(花名:卓与)的分享。分享基于 Service Mesh 的理念,结合蚂蚁金服内部实际场景,将中间件、数据层、安全层等能力从应用中剥离出来后下沉至独立的 Sidecar SOFAMosn 中,结合 Kubernetes 运维体系,提供应用无感知的情况下升级基础设施层能力的案例。
本次分享将从以如下次序展开进行:
蚂蚁金服当前的服务化现状
在看蚂蚁金服的服务化架构之前我们先从一个简单的服务化调用示例说起,下图是 SOFARPC 基本原理:
图1. SOFARPC 基本原理
我们从上图可以看出,构建一个服务化框架需要有服务注册中心,有服务定义,调用方和服务提供方使用相同的服务定义来互相通讯。通过服务注册中心,调用方可以直接订阅到服务提供方的地址,采用点对点的方式直接发起请求。客户端内可实现服务发现、路由寻址、负载均衡、限流熔断等能力来增强服务通讯能力。通过我们开源的 SOFARPC、SOFARegistry、SOFABoot,用户已经可以直接构建起微服务体系,助力业务发展。
蚂蚁金服发展至今,双 11 系统需要应对的交易洪峰逐年递增:
图2. 历年双 11 交易额与峰值数据
每秒 26.5 万笔交易是 2017 年双 11 的峰值数据,这个数据背后有非常复杂的架构支持,LDC 单元化架构是蚂蚁金服沉淀多年的核心架构,依靠这个架构实现每年峰值交易量飞速增长下系统依然能平滑渡过。我们来简要看下 LDC 架构:
图3. LDC 架构示例
上图摘自 金融级分布式架构 中的 素描单元化 一文,这里不详细展开。LDC 的单元化架构给应用的服务化带来更多的规范与抽象,服务路由中需要考虑单元间的调用,跨机房调用等更多场景。这里主要希望表达的是 LDC 架构给 RPC 调用带来更高的复杂度。
服务化痛点
中间件版本升级
在上面介绍背景时,有介绍到目前 LDC 架构下服务调用的复杂度,这些复杂度目前是直接体现在应用的代码中。对于业务同学来讲,一个应用的关注重点是如何实现业务逻辑,至于高可用、容灾等能力更多是整体架构层面会考虑的点。应用内通过引入 RPC 的 jar 包即可获得 LDC 架构下服务调用各种能力的支撑,带来便利的同时也可以看到这种模式的缺点:
图4. APP 业务与 SDK 组成部分
应用内除业务逻辑之外,由中间件的 SDK 引入大量外部依赖,来完成服务发现、路由寻址、负载均衡、限流熔断、序列化、通讯等能力,每个组件的引入都可能带来稳定性风险,以及更高的升级成本。
图5. SDK 内不同能力对应的依赖
根据目前 SOFARPC 在内部的版本举例,服务发现依赖 SOFARegistry 的客户端做 IDC 内的服务发现,依赖 Antvip 做跨 IDC 的服务发现,ZoneClient 集成 LDC 架构的单元化信息,路由寻址需要根据请求的入参计算目前 Zone 然后确定调用目标,限流熔断依赖 Guardian 组件,通讯协议与序列化协议相对稳定,变更较少。仅为了完成服务调用,应用需要额外引入 7+ 客户端包。
每年双 11 需要涉及到架构调整时:比如支持弹性架构,需要做很多中间件客户端的版本升级来支撑更优的架构,对于业务同学来讲,这些升级是很耗费精力的,拿 200 个核心应用举例,每个应用升级中间件版本经过研发、测试、再到部署预发、灰度、生产等环境需要 5个人日的话,200 个核心应用中间件升级需要耗费 1000 人日,如果这部分时间可以节省出来,每年架构升级可以节约大量人力资源。
跨语言通讯
蚂蚁金服发展至今,内部业务百花齐放,搜索推荐、人工智能、安全等各种业务使用到的技术栈非常多样化,跨语言的服务通讯能力也十分重要。早在几年前,Java 之外规模最大的就是 NodeJS 应用,为了让 Java 和 NodeJS 应用之间可以复用蚂蚁金服内部的各种中间件和基础设施,前端团队使用 NodeJS 逐步重写了各种中间件客户端,让整个 NodeJS 和 Java 体系可以完美互通。
图6. Java 与 NodeJS 互调
中间件 SDK 跨语言重写与维护成本极高,随着语言种类的增多,跨语言通讯的诉求也越来越多。
图7. 多语言场景
Java, NodeJS, Go, Python, C++ 等,5+ 语言,中间件 SDK 全部重写成本极高。这些问题不得不激励我们寻找更优的解法。
解决痛点
SDK 能力下沉
图8. SDK 瘦身并下沉能力至 Sidecar
依然以上述 RPC SDK 举例,SDK 中的能力我们可以根据稳定性与不可剥离等特性来看,哪些是可以从应用中抽出来的,尽量把 SDK 做薄,做的足够稳定无需变更,那么升级成本将不复存在。
图9. SDK 与 Sidecar 对应的依赖
RPC SDK 中的服务发现、路由寻址、限流熔断等特性,是更易于变更的,我们将这部分能力下沉至独立的 Sidecar 中,可以复用这部分能力,让多语言的 SDK 只实现最基本的序列化与通讯协议,而这些能力是很轻量且易于实现的。这里的 Sidecar 我们是希望它作为独立进程存在,和业务应用的进程剥离,并和业务应用的升级解耦开来,实现业务和基础设施并行发展,互不干扰的愿景。
图10. RPC 消息与数据源能力下沉
除了 RPC 通讯,我们还可以下沉消息、数据源等能力至 Sidecar 中,业务应用可以越来越薄,SDK 实现成本也降低到可接受的程度,基础设施层与业务剥离,双方均可独立演进。
落地架构
整体架构
图11. Mesh 落地架构
不同于开源的 Istio 体系,蚂蚁金服内部版 Service Mesh 落地优先考虑数据面的实现与落地,控制面在逐步建设中,整体的架构上看,我们使用数据面直接和内部的各种中间件服务端对接,来完成 RPC、消息等能力的下沉,给业务应用减负。由上图可以看出,我们将不同的 Sidecar 与业务应用编排在同一个 Pod 中,App 与 Mosn 直接通讯,Mosn 来负责目标接口的服务发现、路由寻址,并且由 Mosn 内置的安全模块来做应用间调用的加密鉴权。通过 DBMesh 的 Sidecar 来实现数据层的下沉,App 不在需要与多个数据源建立连接,只需要连接本 Pod 内的 DBMesh 即可完成数据层调用,数据库的用户名、密码、分库分表规则等均不再需要关心。
图中名词解析:
ConfigServer:配置中心,负责各种元数据配置、动态开关等。
Registry:服务注册中心,负责 IDC 内服务发现。
AntVip:类 DNS 解析的产品,可通过域名解析一组目标地址,用于跨 IDC 服务发现。
MQ:消息中心服务端,用于收发消息。
落地数据
图12. 落地 CPU 数据
目前这套架构已经在支付核心链路中做试点,618 大促 Mesh 化应用对比无 Mesh 化应用 CPU 损耗增长 1.7%,单笔交易整体耗时增长 5ms。CPU 增长是由于多出一个进程,请求增加一条之后,RT 会有稳定的小幅增长,但这些成本相比于整体架构带来的红利,微乎其微,并且针对整个数据面的持续优化是有望逐步减少资源占用,提升资源利用率。
降低打扰度
中间件能力下沉在架构上看是可行的,实际落地如何做到无打扰的在奔跑的火车上换轮子,低打扰是一个非常重要的考量点。借助于 Kubernetes 的优秀实践,将业务容器与 Sidecar 容器编排在同一个 Pod 中是比较合理的架构,Sidecar 与业务容器互不干扰,互相升级均可做到双方无感。
图13. 落地方式
我们为了让业务应用升级尽可能如丝般顺滑,主要做了如下优化方案:
1. 无感镜像化
蚂蚁金服内部还有部分应用未完成镜像化,或者说以富容器的方式来使用镜像化,这种方式也是在逐步淘汰的过程中,为了让业务更顺滑的镜像化,PAAS 平台联动整个研发体系,将应用的打包过程通过自动生成 Dockerfile 的方式主动帮用户完成镜像化。做到用户无感知镜像化改造。
2. 低感 Mesh 化
镜像化完成之后,想要借助 Pod 模型将应用的容器和 Mosn 的容器部署在一起,我们需要将底层资源管理与调度全部替换到 Kubernetes 体系。然后在 PAAS 上给 Mesh 化的应用增加标识,通过 Kubernetes 识别这些标识并主动注入 Mosn sidecar 来完成应用的 Mesh 化接入。
3. 无感 Sidecar 升级
Sidecar 与业务进程独立后,还有一个核心的诉求就是希望 Sidecar 可以独立升级,为了让 Sidecar 的升级尽量对生产做到无打扰,我们实现了 Mosn 平滑升级的 Operator,通过对运行中 Mosn 的连接迁移,实现整个升级过程应用完全无感知。当然这里面包含着很多挑战,后面我们会详细介绍。
落地挑战
Sidecar 平滑升级
目前我们已经实现 Mosn 的平滑升级能力,Mosn 的主要能力是 RPC 和 消息的通讯代理,平滑升级的目的是业务 App 进程不重启,业务请求不中断,完成 Mosn 的版本升级。
图14. Mosn 平滑升级
由于 Mosn 与应用是独立的 container,如上图描述,Mosn 的升级是需要做到 Pod 内镜像的热替换,这种热替换能力必须要保障几点:
- Pod 不会被重建,应用进程始终正常运行
- 镜像的替换不会影响运行中的流量
- 镜像替换后整个 Pod 描述需要进行更新
为了达成以上目标,我们通过对 Kubernetes 的改造以及自定义 Operator 来完成以上升级的处理。处理流程如下:
图15. Mosn 平滑升级流程
在不考虑多镜像间做平滑升级的场景下,通过 Fork 进程,可以继承父进程的 FD 来完成长连接的迁移,实现无损热升级。Mosn 以镜像化的方式运行后,平滑升级的难度极大增加。整个平滑升级的难点在于如何让不同容器内的 Mosn 进程可互相感知并可完成连接迁移。
下面我们来看下 Mosn 升级过程中,如何保障流量无损:
图16. 正常流量走向
Mosn 处理的正常流量分为入口流量和出口流量,Client 可以看成和 Mosn 部署在同 Pod 内的 App,Server 可以是请求调用的目标服务提供方,可以是一个 App 也可以是被调用 App 侧的 Mosn。当一笔请求从 Client 发至 Server 时,中间会经过两条长连接:TCP1 和 TCP2,Mosn 会记录这笔请求对应的 ConnectionID,来完成请求的发起与响应。
图16. 正向流量迁移
当新的 Mosn 容器被启动时,Mosn 会根据本地的 Domain Socket 来尝试发送请求,当另一个 Mosn 进程存在时,Mosn V1 和 Mosn V2 进入热升级模式,Mosn V1 会将已存在连接的 FD 和已读出的数据包 通过 Domain Socket 发送至 Mosn V2 同时 V1 将不会再从已迁移的 FD 中读取数据,FD 迁移完成所有流量将会直接由 Mosn V2 来处理。
图17. 逆向流量迁移
Mosn V1 和 Mosn V2 进入热升级模式之后,可能会存在 Mosn V1 已经将请求发给 Server 后 Server 还没有来得及响应的情况,这种场景 Mosn V1 在迁移 FD 给 Mosn V2 时,Mosn V2 会在 FD 接管到之后的 ConnectionID 返回给 Mosn V1,当 Mosn V1 收到 Server 返回的 Response 之后,会将这笔请求通过 Domain Socket 发送给 Mosn V2,然后 Mosn V2 根据 ConnectionId 即可找到 TCP1 的连接,然后响应给 Client。
极致性能优化
图18. 请求合并写
Mosn 的核心网络模型是完全自实现的,我们在整个网络层做了非常多的优化,通过 golang 的 writev 我们把多笔请求合并成一次写,降低 sys.call 的调用,提升整体的性能与吞吐。同时在使用 writev 的过程中,有发现 golang 对 writev 的实现有 bug,会导致部分内存无法回收,我们给 golang 提交 PR 修复此问题,已被接受:https://github.com/golang/go/pull/32138
其他优化点还有很多,不再一一详细描述,仅供思路参考,如:
- Mosn 日志异步化,避免磁盘问题对 Mosn 转发性能的影响
- Route 路由结果缓存,空间换时间,提升吞吐
- 接近 90% 内存复用,尽量避免字节拷贝
- Cluster 懒加载,提升 Mosn 启动速度
- 通讯协议层优化,低版本协议针对性升级,降低解包成本
演进方向
图19. 演进方向控制面结合
Service Mesh 的控制面建设我们也在规划中逐步向前推进,希望能统一数据面和控制面的交互模型,控制面尽量遵循 SMI 标准,增加对 TCP 协议的描述支持,逐步增强 Mosn 作为数据面的稳定性,减少变更频率,用更通用的模型来描述服务发现、服务治理等场景,DBMesh 也会基于 XDS 做配置的下发,统一又控制面收口数据下发通道。这里控制面直接对接中间件的服务端是基于性能与容量考虑,蚂蚁金服内部的服务发现数据达单机房千万级别,使用 Kubernetes 的 ETCD 无法承载如此巨大的数据量,需要又控制面做桥梁并分片服务于不同的数据面。
图20. 产品化模型
接下来还会有完整的产品层建设,借助于 Mesh 化架构的思想,更多的能力希望通过下沉至 Sidecar 的方式来拿到架构升级带来的红利。Mesh 的产品层将会包括多种 Mesh 数据面形态的 Metrics 采集、监控大盘,控制面的统一对外途径,屏蔽外部系统与 Mesh 的交互复杂度,统一控制面与数据面交互协议,通过完善的运维体系建设,提升 Mesh 模式下的 Sidecar 灰度升级能力,做到对应用无打扰且稳定无人值守即可完成版本升级的目的。
总结
总结一下全文在 Service Mesh 领域的落地实践,可以归纳为以下六点:
- 应用层与基础设施层分离
- 基础设置层一次编写多处复用
- 数据面通过能力平移优先落地
- 降低打扰度提升落地效率
- 控制面建设统一数据面配置模型
- 性能、稳定性、可观测性持续优化
本文介绍了蚂蚁金服在 Service Mesh 落地的演进过程以及相关痛点的解决方式,希望可以通过我们的实际经历来为读者带来一些不同与社区主流方案的演进思考。
本次分享完整 PPT 下载地址:下载地址
在 KubeCon&CloudNativeCon&Open Source Summit 上,SOFAStack 进行了全天的 Workshop,其中提供了 Service Mesh 实践 Demo,我们将在近期同步,请关注下图二维码噢~