SOFAMosn是基于Go开发的sidecar,用于service mesh中的数据面代理。
https://github.com/sofastack/sofa-mosn
https://github.com/sofastack/sofa-rpc
启动mosn形似./main start -c config.json,那么我们可以先从分析配置入手,看看mosn作为数据代理的整个流转过程。
config.json的配置如下:
{
“servers”: [
{
“default_log_path”: “stdout”,
“listeners”: [
{
“name”: “serverListener”,
“address”: “127.0.0.1:2046”,
“bind_port”: true,
“log_path”: “stdout”,
“filter_chains”: [
{
“tls_context”: {},
“filters”: [
{
“type”: “proxy”,
“config”: {
“downstream_protocol”: “Http2”,
“upstream_protocol”: “SofaRpc”,
“router_config_name”: “server_router”
}
},
{
“type”: “connection_manager”,
“config”: {
“router_config_name”: “server_router”,
“virtual_hosts”: [
{
“name”: “serverHost”,
“domains”: [
“”
],
“routers”: [
{
“match”: {
“headers”: [
{
“name”: “service”,
“value”: “.”
}
]
},
“route”: {
“cluster_name”: “serverCluster”
}
}
]
}
]
}
}
]
}
]
},
{
“name”: “clientListener”,
“address”: “127.0.0.1:2045”,
“bind_port”: true,
“log_path”: “stdout”,
“filter_chains”: [
{
“tls_context”: {},
“filters”: [
{
“type”: “proxy”,
“config”: {
“downstream_protocol”: “SofaRpc”,
“upstream_protocol”: “Http2”,
“router_config_name”: “client_router”
}
},
{
“type”: “connection_manager”,
“config”: {
“router_config_name”: “client_router”,
“virtual_hosts”: [
{
“name”: “clientHost”,
“domains”: [
“”
],
“routers”: [
{
“match”: {
“headers”: [
{
“name”: “service”,
“value”: “.”
}
]
},
“route”: {
“cluster_name”: “clientCluster”
}
}
]
}
]
}
}
]
}
]
}
]
}
],
“cluster_manager”: {
“clusters”: [
{
“Name”: “serverCluster”,
“type”: “SIMPLE”,
“lb_type”: “LB_RANDOM”,
“max_request_per_conn”: 1024,
“conn_buffer_limit_bytes”: 32768,
“hosts”: [
{
“address”: “127.0.0.1:8080”
}
]
},
{
“Name”: “clientCluster”,
“type”: “SIMPLE”,
“lb_type”: “LB_RANDOM”,
“max_request_per_conn”: 1024,
“conn_buffer_limit_bytes”: 32768,
“hosts”: [
{
“address”: “127.0.0.1:2046”
}
]
}
]
}
}
这里的配置非常多,并且有很多的概念及术语。其实mosn概念大多继承于Envoy,所以相关概念可以参考Envoy的官方文档。
基本术语
Host/主机:能够进行网络通信的实体(如移动设备、服务器上的应用程序)。
Downstream/下游:下游主机连接到 Mosn,发送请求并接收响应。
Upstream/上游:上游主机接收来自 Mosn 的连接和请求,并返回响应。
Listener/监听器:监听器是命名网地址(例如,端口、unix domain socket等),可以被下游客户端连接。Mosn 暴露一个或者多个监听器给下游主机连接。
Cluster/集群:集群是指 Mosn 连接到的逻辑上相同的一组上游主机。Mosn 通过服务发现来发现集群的成员。Mosn 通过负载均衡策略决定将请求路由到哪个集群成员。
Mosn配置详解
Mosn中的配置包括两大类: listener(servers)配置和cluster(cluster_manager)配置。
Listener配置
Mosn的每一个server都可配置多个listener以实现复杂的代理逻辑。listener往往包含一组filter依次对数据流进行处理。
我们看到例子中,由sofa-rpc的client端发起了请求,请求的是127.0.0.1:2045。而我们可以发现config.json中配置的clientListener刚好监听的地址是127.0.0.1:2045。它主要包含了以下一些配置
name: 监听器名称
address: 监听的地址
filter_chains:一些列过滤器链
mosn的过滤器有很多种类,按照配置的顺序依次处理数据,在这个例子中,配置了两个过滤器,proxy和connectionManager。
proxy主要是指定了上下游的协议,便于进行协议转换:
downstream_protocol: 下游协议,当前例子采用了sofa-rpc
upstream_protocol:上游协议,当前例子采用了http2
connectionManager则主要用于配置匹配规则,路由规则等,即virtual_hosts配置。而 virtual_hosts 配置中必须包含以下几项配置:
name:服务名称
domains:DNS 域名,必须能跟 virtual_host 的 URL 匹配
routes:路由列表
每个路由中还可以包含以下配置:
match:匹配规则,例如header中需要包含特定的key等
cluster:路由到处理该请求的 mosn cluster
在当前例子中,即为URL为*的请求,并且headers中包含key为service的header,均交于名为clientCluster的cluster处理。
Cluster配置
在clientListener配置的最后,讲道将请求交于clientCluster处理。那么我们接下来看cluster的相关配置。cluster的配置中主要包含了服务发现和负载均衡方式配置。
我们在config.json中找到clientCluster的配置,发现其主要包含以下几项:
name: cluster名称
lb_type: 负载均衡方式
hosts: 在当前例子中,SIMPLE模式的cluster,直接配置了服务发现的主机列表
在当前例子中,负载均衡方式为随机,并且主机列表仅仅只有一台,那么意味着请求将转发到127.0.0.1:2046。
服务端mosn配置
当前我们的流转过程是从clientListener到clientCluster,而这两者其实都包含在了client端的mosn中。clientCluster将请求转发到了127.0.0.1:2046,先由serverListener监听到请求,再交由serverCluster处理,这两者属于server端的mosn,处理逻辑与之前描述的client端的mosn一致,在此不做展开。最后,serverCluster将请求通过服务发现与负载均衡后,转发到了真正的服务端sofa-rpc,即127.0.0.1:8080。
流量劫持
也许有人会有疑问,原先client的请求是直接发给server端的,现在怎么会发给mosn,被clientListener监听到,从而完成整个转发过程呢,难道需要client端知道client mosn的地址?再改写目的地址?
这就涉及到流量劫持了,一个通用的解决方案是iptables,可参考理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持。除此之外还有IPVS、Cilium + eBPF等方案,在此就不展开了。
Mosn代理流量的流转过程
由以上配置分析后,我们可以得到整个mosn的流转过程如下:
,Service Mesh 作为一个专用的基础设施层,用于提供安全、快速、可靠、智能的服务间通讯,可为微服务的连接、管理和监控带来巨大的便利,从而加速微服务的落地。
作为国内领先的金融服务提供商,蚂蚁金服对系统架构的性能、稳定性、安全性要求极高,且面对的运维架构复杂。为了达到高可用和快速迭代的目的,蚂蚁金服正全面拥抱微服务,云原生, 故 Service Mesh 成为助力蚂蚁 SOFA5,以及兼容 K8S 的容器平台 Sigma等微服务化关键组件落地的重要推手。
在 Service Mesh 落地的方案挑选中, Istio 作为 Service Mesh 的集大成者,无论在功能实现,稳定性,扩展性,以及社区关注度等方面都是不二选择,其数据平面 Envoy 更是具有优秀的设计,可扩展的 XDS API,以及较高的性能等特点,蚂蚁一开始便将 Istio 作为重点的关注对象。
然而,由于 Envoy 使用 C++ 语言开发,不符合蚂蚁技术栈的发展方向且无法兼容现在的运维体系,以及蚂蚁内部有许多业务定制化的诉求,导致我们无法直接使用 Istio。经过调研发现,作为云计算时代主流语言的 Golang 同样具有较高的转发性能,这促使我们考虑开发 Golang 版本高性能的 sidecar 来替换 Envoy 与 Istio 做集成。
SOFAMosn 是一款采用 Golang 开发的 Service Mesh 数据平面代理,由蚂蚁金服系统部网络团队、蚂蚁金服中间件团队、UC 大文娱团队共同开发,功能和定位类似 Envoy,旨在提供分布式,模块化,可观察,智能化的代理能力。它通过模块化,分层解耦的设计,提供了可编程,事件机制,扩展性,高吞吐量的能力。
当前, SOFAMosn 已支持 Envoy 和 Istio 的 API,实现并验证了 Envoy 的常用功能(全量功能在开发中),通过 XDS API 与 Pilot 对接,SOFAMosn 可获取控制面推送的配置信息,来完成代理的功能。在实践中,你可以使用 SOFAMosn 替代 Envoy 作为转发平面与 Istio 集成来实现 Service Mesh 组件,也可以单独使用 SOFAMosn 作为业务网关,通过使用 SOFAMosn 你将在如下几个方面获得收益:
SOFAMosn 使用 Golang 作为开发语言,开发效率高,在云原生时代可与 k8s 等技术无缝对接,有利于加速微服务的落地;
SOFAMosn 可代理 Java,C++,Golang,PHP,Python 等异构语言之间组件的互相调用,避免多语言版本组件的重复开发,可提高业务开发效率,目前 SOFAMosn 已经在蚂蚁金服中作为跨语言 RPC 调用的桥梁被使用;
SOFAMosn 可提供灵活的流量调度能力,有助于运维体系的支撑,包括:蓝绿升级、容灾切换等;
SOFAMosn 提供TLS、服务鉴权等能力,可满足服务加密与安全的诉求;
当前 SOFAMosn 已经在 Github 上开源,我们欢迎所有感兴趣的同学参与进来,与我们一起共建一个精品的 Golang Sidecar,项目地址为:https://github.com/alipay/sofa-mosn
为了帮助大家更好的理解 SOFAMosn,本文作为开篇文章,会整体性的介绍 SOFAMosn 的特性以期给大家一个完整的印象,具体的细节这里不做展开,如果您对细节感兴趣,欢迎关注后续文章。
本文介绍的内容将包括 :
SOFAMosn 是如何工作的
SOFAMosn 内部是如何完成代理功能的
SOFAMosn 如何提高Golang的转发性能
SOFAMosn 做了哪些内存优化
SOFAMosn 如何做到系统的高可用
SOFAMosn 如何支持扩展
SOFAMosn 如何做到安全
SOFAMosn 是如何工作的
SOFAMosn 本质是一个 4-7 层代理,所以它可以以独立进程的形式作为 sidecar 与用户程序部署在相同的物理机或者VM中,当然也可以以独立网关的形式单独运行在一台主机或者虚拟机中。
MOSN (注: SOFAMosn 有时也简称为 MOSN) 与 Service 部署在同一个 Pod 上,MOSN 监听在固定的端口,一个正向的请求链路包括如下步骤:
ServiceA 作为客户端可使用任意语言实现,可使用目前支持的任意的协议类型,比如HTTP1.x,HTTP2.0,SOFARPC 等,将 sub/pub、request 信息等发送给MOSN
MOSN 可代理 ServiceA 的服务发现,路由,负载均衡等能力,通过协议转换,转发 ServiceA 的请求信息到上游的 MOSN
上游 MOSN 将接收到的请求通过协议转换,发送到代理的 ServiceB 上
反向链路类似,通过上述的代理流程,MOSN 代理了 Service A 与 Service B 之间的请求。
这里有一些需要注意的是:
你可以使用 MOSN 只代理 Client 的请求,MOSN 可以直接访问 Server,链路:Client -> MOSN -> Server,反之亦然
MOSN 上下游协议可配置为当前支持的协议中的任意一种
SOFAMosn 内部是如何完成代理功能的
了解 SOFAMosn 的代理能力,我们需要窥探它的实现框架以及数据在其内部的流转。这里我们先介绍组成 SOFAMosn 的模块,再介绍 SOFAMosn 的分层设计
SOFAMosn 的组成模块
在上图中,蓝色框中的模块为当前已经支持的模块,红色虚线模块为开发中模块,其中:
Starter 用于启动 MOSN,包括从配置文件或者以 XDS 模式启动,其中Config 用于配置文件的解析等,XDS 用于和 Istio 交互,获取 Pilot 推送的配置等
MOSN 解析配置后,会生成 Server以及Listener ,在 Listener 中有监听端口、 ProxyFilter 、Log 等信息;Server 包含 Listener ,为 MOSN 运行时的抽象,Server 运行后,会开启 Listener 监听,接受连接等
MOSN 运行起来后,还会生成 Upstream相关信息,用于维护后端的 Cluster和 Host信息
MOSN 在转发请求时,会在 Upstream 的 Cluster 中通过 Router 以及 LoadBalancer 挑选 Host
Router 为 MOSN 的路由模块,当前支持根据 label 做路由等
LoadBalance 为 MOSN 的负载均衡模块,支持 WRR,Subset LB
Metrics 模块用于对协议层的数据做记录和追踪
Hardware 为 MOSN 后期规划的包括使用加速卡来做 TLS 加速以及 DPDK 来做协议栈加速的一些硬件技术手段
Mixer 用于对请求做服务鉴权等,为开发中模块
FlowControl 用来对后端做流控,为开发中模块
Lab 和 Admin 模块为实验性待开发模块
SOFAMosn 的分层设计
为了转发数据,实现一个4-7层的 proxy,在分层上,SOFAMosn 将整体功能分为 “网络 IO 层”,”二进制协议处理层”,”协议流程处理层”以及”转发路由处理层” 等四层进行设计,每一层实现的功能高度内聚可用于完成独立的功能,且层与层之间可相互配合实现完整的 proxy 转发。
如下图所示:SOFAMosn 对请求做代理的时候,在入口方向,会依次经过网络 IO 层(NET/IO),二进制协议处理层(Protocol),协议流程处理层(Streaming),转发路由处理层(Proxy);出向与入向过程基本相反
下面我们简单介绍每一层的作用,关于每一层的特性,请参考:
https://github.com/alipay/sofa-mosn/blob/master/docs/design/MOSNLayerFeature.md
NET/IO 层提供了 IO 读写的封装以及可扩展的 IO 事件订阅机制;
Protocol 层提供了根据不同协议对数据进行序列化/反序列化的处理能力;
Streaming 层提供向上的协议一致性,负责 stream 的生命周期,管理 Client / Server 模式的请求流行为,对 Client 端stream 提供池化机制等;
Proxy 层提供路由选择,负载均衡等的能力,做数据流之间的转发;
下面是将此图打开后的示意图
MOSN 在 IO 层读取数据,通过 read filter 将数据发送到 Protocol 层进行 Decode
Decode 出来的数据,根据不同的协议,回调到 stream 层,进行 stream 的创建和封装
stream 创建完毕后,会回调到 Proxy 层做路由和转发,Proxy 层会关联上下游间的转发关系
Proxy 挑选到后端后,会根据后端使用的协议,将数据发送到对应协议的 Protocol 层,对数据重新做 Encode
Encode 后的数据会发经过 write filter 并最终使用 IO 的 write 发送出去
SOFAMosn 如何提高 Golang 的转发性能
Golang 的转发性能比起 C++ 是稍有逊色的,为了尽可能的提高 MOSN 的转发性能,我们在线程模型上进行优化,当前 MOSN 支持两种线程模型,用户可根据场景选择开启适用的模型
模型一使用 Golang 默认的 epoll 机制,对每个连接分配独立的读写协程进行阻塞读写操作, proxy 层做转发时,使用常驻 worker 协程池负责处理 Stream Event
此模型在 IO上使用 Golang 的调度机制,适用于连接数较少的场景,例如:SOFAMosn 作为 sidecar、与 client 同机部署的场景
模型二基于 NetPoll 重写 epoll 机制,将 IO 和 PROXY 均进行池化,downstream connection将自身的读写事件注册到netpoll的epoll/kqueue wait 协程,epoll/kqueue wait 协程接受可读事件时,触发回调,从协程池中挑选一个执行读操作
使用自定义 Netpoll IO 池化操作带来的好处是:
当可读事件触发时,从协程池中获取一个 goroutine 来执行读处理,而不是新分配一个 goroutine,以此来控制高并发下的协程数量
当收到链接可读事件时,才真正为其分配 read buffer 以及相应的执行协程。这样可以优化大量空闲链接场景导致的额外协程和 read buffer 开销
此模型适用于连接数较多,可读的连接数有限,例如:SOFAMosn 作为 api Gateway 的场景
SOFAMosn 做了哪些内存优化
Golang 相比于 C++,在内存使用效率上依赖于 GC,为了提高 Golang 的内存使用率,MOSN 做了如下的尝试来减少内存的使用,优化 GC 的效率:
通过自定义的内存复用接口实现了通用的内存复用框架,可实现自定义内存的复用
通过优化 []byte 的获取和回收,进一步优化全局内存的使用;
通过优化 socket 的读写循环以及事件触发机制,减小空闲连接对内存分配的使用,进一步减少内存使用;
使用 writev 替代 write, 减少内存分配和拷贝,减少锁力度;
SOFAMosn 如何做到系统的高可用
MOSN 在运行时,会开启 crontab 进行监控,在程序挂掉时,会及时拉起;
同时,MOSN 在 进行升级或者 reload 等场景下做连接迁移时, 除了经典的传递 listener fd 加协议层等待方式以外,还支持对存量链接进行协议无关的迁移来实现平滑升级,平滑 reload 等功能;
在对存量连接进行迁移时,mosn 通过 forkexec 生成New mosn,之后依次对存量的请求数据做迁移,对残留响应做迁移来完成;
SOFAMosn 如何支持扩展
MOSN 当前支持 “协议扩展” 来做到对多协议的支持,支持 “NetworkFilter 扩展” 来实现自定义 proxy 的功能,支持 “StreamFilter 扩展” 来对数据做过滤:
SOFARPC
HTTP1.x, HTTP2.0
Dubbo
等协议,后面还会支持更多的协议
TCP Proxy
Layer-7 Proxy
Fault Injection
支持配置健康检查等
支持故障注入功能
SOFAMosn 如何做到安全
SOFAMosn 中,通过使用 TLS 加密传输和服务鉴权来保证消息的安全可靠,在未来还会通过使用 keyless 等方案来提高加解密的性能,下面我们介绍SOFAMosn 在 TLS 上的一些实践
我们通过压测发现,在 ECDHE-ECDSA-AES256-GCM-SHA384 这种常用的加密套件下,Go 自身的 TLS 在性能上优于 Boring SSL,与 Openssl 相差不多
经过调研发现,Go 对 p256,AES-GCM 对称加密,SHA,MD5 等算法上均有汇编优化,因而我们选择使用 Golang 自带的 TLS 来做 SOFAMosn 的 TLS 通信
SOFAMosn server 通过使用 Listener 的 Inspector 功能,可同时处理客户端的 TLS 和 非 TLS 请求
SOFAMosn client 在发起连接的时候,根据挑选的 host 是否支持 TLS 来决定是否使用 TLS 发起连接
SOFA 文档: www.sofastack.tech/
SOFA: github.com/alipay
SOFAMosn: github.com/alipay/sofa
Service Mesh 是蚂蚁金服下一代架构的核心,本主题主要分享在蚂蚁金服当前的体量下,我们如何做到在奔跑的火车上换轮子,将现有的 SOA(service-oriented architecture,面向服务的架构)体系快速演进至 Service Mesh 架构。聚焦 RPC 层面的设计和改造方案,本次将分享蚂蚁金服双十一核心应用是如何将现有的微服务体系平滑过渡到 Service Mesh 架构下并降低大促成本。
蚂蚁金服每年双十一大促会面临非常大的流量挑战,在已有 LDC(Logical Data Center,逻辑数据中心,是蚂蚁金服原创的一种“异地多活单元化架构”实现方案)微服务架构下已支撑起弹性扩容能力。
服务框架:SOFARPC(已开源);
Service Mesh:MOSN(已开源);
SOFARPC:https://github.com/sofastack/sofa-rpc
MOSN:https://github.com/sofastack/sofa-mosn
Service Mesh 简介
业界普遍认可的 Service Mesh 架构,对应到蚂蚁金服的 Service Mesh 也分为控制面和数据面,分别叫做 SOFAMesh 和 MOSN,其中 SOFAMesh 后面会以更加开放的姿态参与到 Istio 里面去。
今天我们讲的实践主要集中在 MOSN 上,以下我的分享中提到的主要就是集中在数据面上的落地,这里面大家可以看到,我们有支持 HTTP/SOFARPC/Dubbo/WebService。
为什么我们要 Service Mesh
有了一个初步的了解之后,可能大家都会有这样一个疑问,你们为什么要 Service Mesh,我先给出结论:
因为我们要解决在 SOA 下面,没有解决但亟待解决的:基础架构和业务研发的耦合,以及未来无限的对业务透明的稳定性与高可用相关诉求。
那么接下来,我们一起先看看在没有 Service Mesh 之前的状况。
在没有 Service Mesh 之前,整个 SOFAStack 技术演进的过程中,框架和业务的结合相当紧密,对于一些 RPC 层面的需求,比如流量调度、流量镜像、灰度引流等,是需要在 RPC 层面进行升级开发支持,同时需要业务方来升级对应的中间件版本,这给我们带来了一些困扰和挑战。<img
线上客户端框架版本不统一;
业务和框架耦合,升级成本高,很多需求由于在客户端无法推动,需要在服务端做相应的功能,方案不够优雅;
机器逐年增加,如果不增加机器,如何度过双十一;
在基础框架准备完成后,对于新功能,不再升级给用户的 API 层是否可行;
流量调拨,灰度引流,蓝绿发布,AB Test 等新的诉求;
这些都困扰着我们。我们知道在 SOA 的架构下,负责每个服务的团队都可以独立地去负责一个或者多个服务,这些服务的升级维护也不需要其他团队的接入,SOA 其实做到了团队之间可以按照接口的契约来接耦。但是长期以来,基础设施团队需要推动很多事情,都需要业务团队进行紧密的配合,帮忙升级 JAR 包,基础设施团队和业务团队在工作上的耦合非常严重,上面提到的各种问题,包括线上客户端版本的不一致,升级成本高等等,都是这个问题带来的后果。
而 Service Mesh 提供了一种可能性,能够将基础设施下沉,让基础设施团队和业务团队能够解耦,让基础设施和业务都可以更加快步地往前跑。
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_x.webp”/>
我们的方案
说了这么多,那我们怎么解决呢?我们经历了这样的选型思考。
总体目标架构
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_arc.webp”/>
我们的 MOSN 支持了 Pilot、自有服务发现 SOFARegistry 和自有的消息组件,还有一些 DB 的组件。在产品层,提供给开发者不同的能力,包括运维、监控、安全等能力,这个是目前我们的一个线上状态。
SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。
SOFARegistry:https://github.com/sofastack/sofa-registry
看上去很美好,要走到这个状态,我们要回答业务的三个灵魂拷问。
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_arc_up.webp”/>
这三个问题后面,分别对应着业务的几大诉求,大家做过基础框架的应该比较有感触。
框架升级方案
准备开始升级之后,我们要分析目前我们的线上情况,而我们现在线上的情况,应用代码和框架有一定程度的解耦,用户面向的是一个 API,最终代码会被打包在 SOFABoot 中运行起来。
SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。
SOFABoot:https://github.com/sofastack/sofa-boot
那么,我们就可以在风险评估可控的情况下,直接升级底层的 SOFABoot。在这里,我们的 RPC 会检测一些信息,来确定当前 Pod 是否需要开启 MOSN 的能力。然后我们完成如下的步骤。
我们通过检测 PaaS 传递的容器标识,知道自己是否开启了 MOSN,则将发布和订阅给 MOSN,然后调用不再寻址,直接完成调用。
可以看到,通过批量的运维操作,我们直接修改了线上的 SOFABoot 版本,以此来直接使得现有的应用具备了 MOSN 的能力。有些同学可能会问,那你一直这么做不行吗?不行,因为这个操作是要配合流量关闭等操作来运行的,也不具备平滑升级的能力,而且直接和业务代码强相关,不适合长期操作。
这里我们来详细回答一下,为什么不采用社区的流量劫持方案?
主要原因是一方面 iptables 在规则配置较多时,性能下滑严重。另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。蚂蚁金服在引入 Service Mesh 的时候,就是以全站落地为目标的,而不是简单的“玩具”,所以我们对性能和运维方面的要求非常高,特别是造成业务有损或者资源利用率下降的情况,都是不能接受的。
容器替换方案
解决了刚刚提到的第一个难题,也只是解决了可以做,而并不能做得好,更没有做得快,面对线上数十万带着流量的业务容器, 我们如何立刻开始实现这些容器的快速稳定接入?
这么大的量,按照传统的替换接入显然是很耗接入成本的事情,于是我们选择了原地接入,我们可以来看下两者的区别:
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_arc_up_1.webp”/>
在之前,我们做一些升级操作都需要有一定的资源 Buffer,然后批量的进行操作,替换 Buffer 的不断移动,来完成升级的操作。这就要求 PaaS 层留有非常多的 Buffer,但是在双十一的情况下,我们要求不增加机器,并且为了一个接入 MOSN 的操作,反而需要更多的钱来买资源,这岂不是背离了我们的初衷。有人可能会问,不是还是增加了内存和 CPU 吗?这是提高了 CPU 利用率,以前业务的 CPU 利用率很低,并且这是一个类似超卖的方案,看上去分配了,实际上基本没增加。
可以看到, 通过 PaaS 层,我们的 Operator 操作直接在现有容器中注入,并原地重启,在容器级别完成升级。升级完成后,这个 Pod 就具备了 MOSN 的能力。
MOSN 升级方案
在快速接入的问题完成后,我们要面临第二个问题。由于是大规模的容器,所以 MOSN 在开发过程中势必会存在一些问题,出现问题时该如何升级?要知道线上几十万容器要升级一个组件的难度是很大的,因此,在版本初期我们就考虑到 MOSN 升级的方案。
能想到最简单的方法,就是销毁容器,然后用新的来重建。但是在容器数量很多的时候,这种运维成本是不可接受的。如果销毁容器重建的速度不够快,就可能会影响业务的容量,造成业务故障。因此,我们在 MOSN 层面和 PaaS 一起,开发了无损流量升级的方案。
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_arc_up_resatrt.webp”/>
在这个方案中,MOSN 会感知自己的状态,新的 MOSN 启动会通过共享卷的 Domain Socket 来检测是否已有老的 MOSN 在运行,如果有,则通知原有 MOSN 进行平滑升级操作。
具体来说,MOSN 启动的时候查看同 Pod 是否有运行的 MOSN (通过共享卷的 Domain Socket),如果存在,需要进入如下流程:
src=”https://xiazemin.github.io/MyBlog/img/mson_service_mesh_arc_up_greceful.webp”/>
New MOSN 通知 Old MOSN,进入平滑升级流程;
Old MOSN 把服务的 Listen Fd 传递给 New MOSN,New MOSN 接收 Fd 之后启动, 此时 Old 和 New MOSN 都正常提供服务;
然后 New MOSN 通知 Old MOSN,关闭 Listen Fd,然后开始迁移存量的长链接;
Old MOSN 迁移完成, 销毁容器;
这样,我们就能做到,线上做任意的 MOSN 版本升级,而不影响老的业务,
分时调度案例
技术的变革通常不是技术本身的诉求,一定是业务的诉求,是场景的诉求。没有人会为了升级而升级,为了革新而革新,通常是技术受业务驱动,也反过来驱动业务。
在阿里经济体下,在淘宝直播,实时红包,蚂蚁森林,各种活动的不断扩张中,给技术带了复杂的场景考验。
这个时候,业务同学往往想的是什么?我的量快撑不住了,我的代码已经最优化了,我要扩容加机器,而更多的机器则对应付出更多的成本。面对这样的情况,我们觉得应用 Service Mesh 是一个很好的解法,通过和 JVM、系统部的配合,利用进阶的分时调度实现灵活的资源调度,不加机器,也可以在资源调度下有更好的效果。
首先,我们假设有两个大的资源池的资源需求情况,可以看到在 X 点的时候,资源域 A 需要更多的资源,Y 点的时候,资源域 B 需要更多的资源,总量不得增加。那当然,我们就希望能借调机器,就像下面这样,请大家看左图。
src=”https://xiazemin.github.io/MyBlog/img/mson_hijack_keepalive.webp”/>
在这个方案中, 我们需要先释放资源,销毁进程,然后开始重建资源,启动资源域 B 的资源。这个过程对于大量的机器是很重的,而且变更就是风险,关键时候做这种变更,很有可能带来衍生影响。
而在 MOSN 中,我们有了新的解法。如右图所示,有一部分资源一直通过超卖,运行着两种应用,但是 X 点的时候,对于资源域 A,我们通过 MOSN 来将流量全部转走,应用的 CPU 和内存就被限制到非常低的情况,大概保留 1% 的能力。这样操作,机器依然可以预热,进程也不停。
在这里,我们可以看这张图。
在需要比较大的资源调度时,我们推送一把开关,则资源限制打开,包活状态取消。资源域 B 瞬间可以满血复活,而资源域 A 此时进入上一个状态,CPU 和内存被限制。在这里,MOSN 以一个极低的资源占用完成流量保活的能力,使得资源的快速借调成为可能。
Service Mesh 在蚂蚁金服经过 2 年的沉淀,最终经过双十一的检验。在双十一,我们覆盖了数百个双十一交易核心链路,MOSN 注入的容器数量达到了数十万,双十一当天处理的 QPS 达到了几千万,平均处理 RT<0.2 ms,MOSN 本身在大促中间完成了数十次的在线升级,基本上达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。
不论何种架构,软件工程没有银弹。架构设计与方案落地总是一种平衡与取舍,目前还有一些 Gap 需要我们继续努力,但是我们相信,云原生是远方也是未来,经过我们两年的探索和实践,我们也积累了丰富的经验。
我们相信,Service Mesh 可能会是云原生下最接近“银弹”的那一颗,未来 Service Mesh 会成为云原生下微服务的标准解决方案,接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,一起和社区把 Istio 打造成 Service Mesh 的事实标准。