无责任畅想:云原生中间件的下一站
作者 | 于雨
来源|阿里巴巴云原生公众号
本文源自 2020 年 12 月 20 日作者在云原生社区 meetup 第二期北京站演讲 《Apache Dubbo-go 在云原生时代的实践与探索》的部分内容,如果对演讲完整内容感兴趣请访问:
https://www.bilibili.com/video/av245840877
自从以 2013 年开源的 docker 为代表的的容器技术和以 2014 年开源的 K8s 为代表的容器编排技术登上舞台之后,相关技术从业人员从认知和体感上接受,云原生时代真的到来了。
当然也有很多资深技术人员认为,云原生时代要从 2010s 时以 OpenStack 为代表的虚机编排时代开始。当然,也有人说其实云原生技术诞生很早,可以从巨型机时代在巨型机上虚拟出若干小型机开始追溯。
在云原生时代,不变镜像作为核心技术的 docker 定义了不可变的单服务部署形态,统一了容器编排形态的 k8s 则定义了不变的 service 接口,二者结合定义了服务可依赖的不可变的基础设施。有了这种完备的不变的基础设置,就可以定义不可变的中间件新形态 -- 云原生中间件。
云原生时代的中间件,包含了不可变的缓存、通信、消息、事件(event) 等基础通信设施,应用只需通过本地代理即可调用所需的服务,无需关心服务能力来源。
微服务框架
从最早的单体应用时代到分布式技术时代,流行的是微服务技术。微服务时代各大公司都沉淀出了具有代表性的一些服务通信框架,如 Google 的 gRPC,阿里的 Dubbo 和 HSF,百度的 bRPC 等等。多个服务通信框架之间的竞争,基本上是在大公司之间进行角力。
站在使用者的角度,当然期待一个网络框架在进化过程中能够保持向前兼容性,多个框架之间保持互通性。
1. 服务框架的向后兼容性
通信框架的基础是通信协议和序列化协议,其中很重要的问题就是新版本协议对旧版本的向后兼容性。在一个组织中一般都使用统一的通信框架,但现实中可能因为各种原因,同一个框架的序列化协议或者通信协议的向后兼容能力差,会导致使用不同版本通信框架的各个服务之间的异构化。如采用了 pb v2 和 pb v3 的通信框架不兼容,不遑多让的 Thrift 0.8.x 与 Thrift 0.9.x 之间也不兼容。
不过 Protobuf v3 或者 Protobuf v2 的各个子版本之间的向前和先后兼容性还是不错的,但还是有一些弱鸡公司的内部序列化协议无论是否采用 TLV 形式,其协议各个版本的之间还是无法兼容,进而导致各个子版本的服务框架相互异构,最终导致使用了不同版本的服务框架的业务背上大量包袱无法快速演进,有些新版本的业务中存在各种神逻辑可能不是为了兼容旧版本的业务逻辑,而是为了兼容旧版本框架的通信协议。
2. 多框架之间的互通性
一个常识是,组织规模膨胀到足够大的程度后,不可能有一个通用的框架适用于所有的场景,一些大经济实体随着业务体量变大,业务类型变得庞杂,不可避免地存在一些重复的轮子,这些庞大规模的组织因为其规模效应,任何一个适用于特定场景的框架只要能在内部找到若干落地应用场景就足以让其开发维护成本变得可负担且收益甚大。
公司内部各个分公司之间可能存在不同服务框架导致各个服务之间通信异构化越来越严重,如阿里内部早前存在异构的 Dubbo 和 HSF(目前阿里内部 HSF 和 Dubbo 已经开始融合, HSF 已经采用 Dubbo 作为内核以 Dubbo 插件的形式存在),如当下的阿里的 HSF 和各个收来的新公司之间的网络通信,其间通信可能不得不借助于 Proxy 形式的通信网关。
每个服务框架各个子版本之间需要保持向后兼容,各个服务框架之间的兼容可能需要网关代理。
3. 多语言框架之间的互联
除了序列化协议、通信框架的异构外,还普遍存在着因为各个不同语言技术栈之间差异导致的异构:每种语言都有个各自的序列化协议和通信框架。如 Java 世界就存在着 Spring Cloud 和 Dubbo 两大服务治理框架,Spring Cloud 尚无多语言版本实现,相比之下 Dubbo 的多语言工作稍好一些。
4. 打通打平不同的技术体系
同一实体内部不同公司之间有两三个不同服务框架的情况已经算是很好了,大公司组织内部可以容忍适量的重复造轮子,但大量的重复造轮子就过犹不及了。据说有巨头内部不同部门之间存在各自的 RPC 服务框架 ,其总体服务框架超 100+!
随着国内互联网行业发展由于头部效应趋向性明显,在大鱼吃小鱼的时代背景下,大公司与收购来的公司之间的技术体系异构也是云原生时代中间件面临的一个问题,二者之间因为业务规模不同带来的服务注册中心、配置中心、语言技术栈、服务鉴权、缓存和存储等诸多技术不统一。有的刚收来的公司甚至使用了大公司的竞争对手的云平台,这就又带来了平台级的差异,诸如此类的异构问题不一而足。
借助网络代理技术,可以初步快速打通不同技术体系之间的异构差异。
5. 通信代理的必要性
除了南北向通信的网络接入层代理外,微服务时代使用同一通信框架的各个服务实体之间直接进行通信,很少听说各个服务实体之间通过代理 Proxy 进行通信,其根由是各个通信实体之间通信框架同构。
或者说,网络通信代理被各个强悍的通信框架给消化掉,以 Proxyless SDK 的形式存在,如服务发现、服务路由、容灾限流、负载均衡等功能都存在于各个服务框架的 SDK 中。
但随着多协议多语言等各种因素导致的各个框架之间的各种异化,大家普遍相信:没有什么差异不是一层 Proxy 解决不了的。
6. Service Mesh
2016 年之后兴起的 Service Mesh 技术区分为 Proxy Service Mesh 和 Proxyless Service Mesh,至于二者之间的差异可参见本文 2019 年的一篇文章 Service Mesh 形态刍议。目前比较流行的 Service Mesh 技术形式是 Proxy Service Mesh,其比较有代表性的组件有数据面的 envoy 和控制面的 Istio。
一些 Service Mesh 技术文档宣称,将服务框架的序列化协议、通信能力、服务治理能力沉淀到服务网格的代理(如 envoy 这类数据面 sidecar)中,可有如下收益:
- 服务框架 SDK 会变的非常轻量,甚至完全沉淀到 sidecar 中。
- 多语言、多协议和多种通信方式之间的差异将被磨平。
- 统一流量控制,提升系统的弹性。
- 统一监控设施,提高可观测性。
- 统一安全可信认证,提升安全性。
- 升级过程业务无感,做到平滑升级,提升可靠性。
- 提升业务版本迭代速度。
- 快速打通不同技术治理体系之间的差异。
- 在 Mesh 和 非 Mesh 模式之间快速切换。
有人可能据此误以为 Service Mesh 技术可将业务和服务框架的复杂性消灭于无形,将 Istio + sidecar 形式为代表的服务网格可以定义为 Service Mesh 的终极形态。
Sidecar 与中间件
Proxy Service Mesh 的数据面的 sidecar 仅具备通信能力,业务应用和 sidecar 之间仍然存在一个 gap:微服务时代应用所使用的中间件系统的能力需要沉淀到 sidecar 中。
1. Sidecar 的能力
Proxy Service Mesh 中 sidecar 的一个典型代表是 envoy,其本质是一个具备通信能力的 local proxy,具备如下数据面能力:
- 流量控制
- 序列化协议转换
- 通信协议转换
- 数据路由控制
实际上, envoy 仅仅是提供了这些能力的接口,至于具体的序列化协议转换、通信协议转换插件等工作还是需要相关的基础设施开发人员去做的。
除了序列化协议和通信协议转换,中间件的其他能力向 Proxy 下沉的过程中,都需要做大量的 envoy filter 层面的工作。相对于 Proxyless 形式的 Service Mesh,Proxy Service Mesh 成本没有任何变化,其对业务的侵入性也没有减轻更多。
2. 协议下沉
如果说 Proxyless Service Mesh 形态下的 SDK 升级需要业务层面做很多改造,Proxy Service Mesh 形态下的业务从 Proxyless 向 Proxy 形态升级过程中改造成本也不可谓不小。如果通信协议采用了 Protobuf V3,Proxy Serivce Mesh 形态下的业务升级协议时可能做到平滑升级:只升级 Proxy 不用升级业务服务实体。但现实是一些公司内部的私有协议根本做不到向后兼容,其成本就是升级协议时服务网格的 Proxy 和业务实体一起升级,至于期望的平滑升级就很难做到了。
诸般问题的核心是 Local Proxy 没有统一通信协议和序列化协议,仅仅注重于流量劫持。
3. 有状态的应用
Service Mesh 技术的一些爱好者假设服务实体是一个无状态的服务,其代理也当然是一个无状态的 sidecar,然后宣传 Service Mesh 时代的各种红利。
至于那些有状态的应用,大公司有人有钱耗费巨量人日成本后,把状态转移到 Proxy 中去,让应用无状态,但业务无感知的平滑升级是就难做到了。这个成本对大经济实体来说当然是可负担且收益甚大的,但不是一些中小厂家所能承受的,他们在踩完各种坑后,可能神奇的发现他们根本没有实力实现有状态的 sidecar。
4. 复杂性守恒
总结一番,基于 sidecar 搭建一个 Proxy Service Mesh 可能存在如下工作:
- 不同序列化协议转换
- 不同通信协议之间的转换
- 同一序列化协议多版本之间的转换
- 私有和公开通用的序列化协议之间的转换
- 对单体时代或者微服务时代的业务进行改造升级
- 业务应用和 sidecar 同时升级带来的额外运营维护
- 有状态的 Sidecar 的开发测试与维护
- 应用和代理的优雅退出和平滑升级
- 不同技术体系之间的互通
- Mesh 模式和非 Mesh 模式之间的切换
面对这些难度层层递进的工作,中小厂家很有可能就会退缩到微服务技术体系。其最根本的原因是:同等级别的业务形态下,技术复杂性守恒,总成本守恒。一如微服务技术至于单体技术:各个单一服务都被拆分的足够简单,但单体技术的复杂性转换为了巨量服务之间的复杂性。
另一种 Mesh
以 Service Mesh 技术为代表的的云原生技术体系根植于以 K8s 为代表的的云原生基础设施之上。
1. 协议标准化
云原生中间件 Proxy 的下一站除了被动地去兼容各种协议,更应该主动的去统一序列化协议,如自身直接支持 Protobuf v3 协议,业务也采用这种可保证向后兼容的协议即可。
除了被动地去兼容各种通信框架做互联互通,可以更主动地向事实上的通信框架 gRPC 或者通信协议 HTTP 靠拢。gRPC 有各种语言的 SDK 库。各种语言自身会主动提供 HTTP 通信协议库,且 HTTP2.0 向后兼容 HTTP 1.1。例如微软的 Dapr,就直接提供 gRPC 和 HTTP 两种通信协议的 API,上层业务需要做的是选择其中一种通信协议的 API 进行开发。
2. Service Proxy
一些的云原生时代的事实上的标准通信设施有:2008 年开源的 protobuf v2 和 2014 年开源的 protobuf v3 统一了序列化协议;2016 年 gRPC 发布 v1 之后逐渐成了跨语言首选的通信库。
除了序列化协议和通信协议,微服务时代的中间件体系大概有如下技术栈:
- RPC,其代表是 Dubbo/Spring Cloud/gRPC 等。
- 限流熔断等流控,如 hystrix/sentinel 等。
- Cache,其代表是 Redis。
- MQ,其代表有 kafka/rocketmq 等。
- 服务跟踪,如兼容 Opentracing 标准的各种框架。
- 日志收集,如 Flume/Logtail 等。
- 指标收集,如 prometheus。
- 事务框架,如阿里的 seata。
- 配置下发,如 apollo/nacos。
- 服务注册,如 zookeeper/etcd 等。
- 流量控制,如 hystrix/sentinel 等。
- 搜索,如 ElasticSearch。
- 流式计算,如 spark/flink。
把各种技术栈统一到一种事实上的技术标准,才能反推定义出不可变的中间件设施的终态。把上面这些事实上的中间件梳理一番后,整体工作即是:
- 统一定义各服务的标准模型
- 定义这些标准模型的可适配多种语言的 API
- 一个具备通信和中间件标准模型 API 的 Proxy
- 适配这些 API 的业务
上图定义一种不同于 istio + Envoy 的另一种可能的 Proxy Service Mesh 进化路径。该 Service Mesh 模型下的 sidecar 不仅仅是一个 Local Proxy,更应该是一个提供各个中间件技术栈标准能力的 Service Proxy。
Service Proxy 可能是一个集状态管理、event 传递、消息收发、分布式追踪、搜索、配置管理、缓存数据、旁路日志传输等诸多功能于一体的 Proxy, 也可能是分别提供部分服务的多个 Proxy 的集合,但对上提供的各个服务的 API 是不变的。
3. Application Mesh
或者更进一步,可以把 Service Proxy 拆分为两个 Proxy:
- 仍然以现有的以劫持网络流量的 sidecar 形式存在的 Local Proxy。
- 另一个专门提供各个 Service API 的 Application Proxy。
Application Proxy 可以是一个独立存在的进程或者容器,也可以是一个可被应用调用嵌入式的 SDK 库。无论是 Proxy 形式还是 Proxyless 形式,这都是一种新形态的 Service Mesh,可被称之为 Application Mesh。
Application Proxy 可以依赖于 Local Proxy,亦可不依赖。如人们常说的三级缓存其实也是一种 Application Mesh 形态,从进程内的 local cache 到本机(容器、虚拟机 or 物理机)cache proxy 一直回溯到 cache cluster, 应用只需要从 local cache 处获取数据即可。
当然,Application Mesh 亦可不依赖于特定的基础设施平台,包括 k8s,本文就不展开讨论了。
除了 Service Mesh 技术带来的收益,Application Mesh 具有如下更多的收益:
- 更好的扩展性与向后兼容性
基于标准的 API,其服务的不变性得到极大改善,不变性可以确保中间件的向后兼容与更好的扩展能力。基于标准 API,第三方服务商可以在云厂商提供的基础设施之上扩展出更多形态的中间件设施。
- 与语言无关
统一序列化协议和通信协议后,无特定语言依赖是很自然的事情。主流语言都会支持 HTTP 协议,gRPC 库自身能够提供主流语言的支持。无特定语言绑定带来的另一个间接好处是更好的可移植性。
- 与云平台无关
通过标准的服务让应用做到无云平台依赖,统一了中间件技术栈的平台提供的技术能力相同的,云平台使用者没有被云服务提供商绑架的危险。
- 应用与中间件更彻底地解耦
通过标准协议和标准 API 让应用与中间件彻底解耦,让开发者在开发阶段对新类型 Proxy 的有感,做到业务上线后业务对 Proxy 及其底层系统的升级过程无感知,即更好地平滑升级。
- 应用开发更简单
基于标准 API,应用开发者甚至可以用单体时代的服务框架开发分布式应用,提升应用版本迭代速度。
- 更快的启动速度
状态从应用转移到代理后,可以通过提前预热代理,让应用瞬时批量启动。
未来的收益
任何技术都不是没有代价的。诚如微服务技术带来了服务数量的剧增,Service Mesh 技术带来了吞吐的降低和延时的增加,但下一站的云原生中间件形态会带来的是另一种新的价值形态,相比而言这个代价是可以接受的。
1. 业务价值
就其浅显的技术价值而言,做到基础中间件技术的统一后,即可做到无关语言,无关各个具体的中间件实体。减轻业务开发人员负担,使其专注于业务逻辑,做到真正的快速迭代与平滑升级,提升研发效率的同时降低各种成本。
2. 云平台无关
新形态带来的商业价值就是无云平台依赖,各平台间相互之间的竞争就不会停留在某种独有的核心技术优势上,而是在同一技术体系下不断降低服务成本,提供更好的用户体验、更强的服务能力与更亲民的价格。
能够且愿意实现这种终态 proxy 的组织当然不是各中小型业务厂家,所以统一了这些标准服务 API 的 Proxy 之下的应该是提供这些标准服务的各大云厂商。越早向统一服务模型靠拢的云厂商越快得利,越相信自己私有服务能力的云厂商越孤立。
3. 初创公司的机会
基于大厂提供的基础设施,可以孕育出一个独立的 service proxy 生态:一些第三方的初创厂家专职提供云原生中间件解决方案。
基于新形态的中间件方案,Low Code 或者 No Code 技术才能更好落地。单体时代的 IDE 才能更进一步 -- 分布式时代的 IDE,基于各种形态中间件的标准 API 之对这些中间件的能力进行组合,以 WYSIWYG 方式开发出分布式应用。
4. 打破大厂内部藩篱
对一些大厂组织内部而言,可借助真正形态的 Service Mesh 技术栈可以统一大经济实体内部技术栈,打破人为的各种异构隔离,提升研发运维效率和物理资源利用效率,降低整体人力与资源的交付运维成本。
5. 走向新时代
以统一技术形态的 Service Mesh 为基础的云原生中间件技术体系真正发起起来,在其之上的 Serverless 才有更多的落地场景,广大中小企业才能分享云原生时代的技术红利,业务开发人员的编码工作就会越来越少,编程技术也会越来越智能--从手工作坊走向大规模机器自动生产时代。
欢迎对 apache/dubbo-go 项目有兴趣的同学欢迎钉钉搜索群号:31363295,加入交流群。
作者简介
于雨(github @AlexStocks),dubbogo 社区负责人,一个有十多年服务端做着基础架构和中间件研发一线工作经验的程序员,陆续参与和改进过 Redis/Pika/Muduo/dubbo-go/Sentinel-go 等知名项目,目前在蚂蚁金服可信原生部从事容器编排和 service mesh 工作。