1. 场景
场景示例:
- 只维护一个环境:主干环境,通过增量复制组件得到分⽀支环境,进行测试
- 同一项目、服务多分支并行测试, 不同测试环境隔离
- 基于用户特征做AB test, 如地域、浏览器、cookie、referrer
- 流量特征在服务扇出的调用链中传递, 同一调用链中支持一致的路由规则
2. 需求
以上场景示例要求测试环境有以下能力:
- 服务版本定义能力
- 路由规则定义能力:
- Version Base Routing
- Content Base Routing
- 协议支持
- 链路信息传播(染色)
3. 服务版本定义能力
需要对同一服务, 定义不同版本, 如基础版本, 不同分支测试版本等.
k8s 提供基于label能力, 可以用于标记服务的不同workload, 原生提供服务多版本区分能力. 后续「版本」都是指k8s label base 版本.
但是, k8s 没有流量识别和路由定义的功能, ServiceMesh/Istio 提供相应功能.
4. 路由规则定义能力
4.1 Version Base Routing:
istio 提供流量识别和路由定义的功能 (使用CRD VirtualService 定义), 可以支持「不同source 版本」 到不同「 destination版本」的路由定义, 该功能和上层服务协议无关, TCP/http 服务都可支持.
4.2 Content Base Routing
对用户/方法名/host等信息的路由需求, 需要实现Content Base Routing.
istio提供了多种流量识别的能力, 其中包括常见的基于流量内容的识别, header/path/host/port/sni/网段等等
但是, istio的流量识别和操纵主要是基于http/http2/grpc实现, 现实是有大量其他RPC协议治理需求, 因此需要扩展istio/envoy支持的协议.
另外, 以上路由规则定义是作用于上下游端到端的服务流量, 要实现微服务中调用链流量治理(主要是路由规则和全链路跟踪), 还需要实现链路信息传播(染色传播).
6. 协议支持
6.1 协议透明扩展
目前对服务网格协议扩展有几种可选方案, 这里主要讨论基于istio/envoy协议透明扩展方案:
目前istio 使用envoy作为sidecar流量代理, envoy通过filter chain 来实现流量的链式操纵, 该方案需要针对不同的私有协议, 开发一套envoy filter, 用于将私有协议透明转换为istio目前支持良好的协议, 如grpc.
配置生效过程:
- 容器化的service A 和service B 接入mesh: 注册为k8s service, 注入sidecar
- polit 会根据service 的端口命名, 识别serviceA/B 的(私有)协议类型, 对此特殊的(私有)协议, 下发包含「协议透明转换的filter」的xDS给sidecar
- sidecar 接收到xDS, 会更新envoy config (配置filter chain)
流量流转过程:
- Service A 发送私有协议, 被其sidecar拦截, sidecar配置的一个filter, 将私有协议转换为gRPC, 具体的, 需要将私有协议的destination, streamID等信息提取, 匹配, 并构造gRPC destination等头部, 把私有协议payload 放入gRPC 数据桢.
- 流量以gRPC方式从 serviceA sidecar 到 serviceB sidecar, 用户可以使用使用gRPC在istio体系的中治理能力.
- Service B 对应的sidecar, 其最后一个filter 是将gRPC协议转换为指定私有协议.
方案主要优劣:
优: istio 控制面改造较小, 私有协议可以使用istio 大部分服务治理能力, 服务之间不需要关系协议转换, 基本零改造.
劣: 协议转换, 性能开销大
如果只针对测试环境, 性能问题应该可以接受.
6.2 destination 信息
流量特征中最重要的是destination 信息, 除了ip和port外, 在RPC 中,往往还有destination信息, 不同RPC实现方式不同, 但语义相似:
协议 | destination (目的地) |
---|---|
HTTP 1 | Host header,Path |
HTTP 2 | Header帧中的伪header :authority ,:path |
Dubbo | payload 中的path/method |
thirft | method |
大部分协议有destination 信息, 上述协议转换的filter, 需要提取私有协议中的destination, 按照语义和gRPC 中destination进行互转.
6.3 私有协议的限制和要求:
6.3.1 要能够正确的拆包
envoy 需要实现私有协议序列化和反序列化, 必须要能够正确的拆包,然后以请求为单位进行转发,这是负载均衡的基础, 涉及tpc粘包拆包.
6.3.2 destination信息可解析
首先, 协议需要有destination信息, 可以对应到http的host/path/header等语义
不同协议的destination有不同的实现方式, 有的在header里, 有的在payload/body里. 协议的destination需要能透明解析, 不依赖特定的IDL, 这涉及RPC序列化分类:
RPC序列化分类:
- 序列化无类型: 如json
- 类型信息在序列化结果中: 如java自带的序列化/hessian
- client/server 持有序列化类型的IDL, 如thrift/grpc
其中1和2是可以支持解析destination(如果有的话). 对于3, 又可以分2种:
destination 解析不依赖IDL, 只有payload解析依赖IDL, 如grpc/thrift
destination 解析依赖client端和server端协商的 IDL, filter 无法透明获取 destination信息.
6.3.3 需要支持自定义header, 实现染色信息操纵
RPC 自定义header 可以用来承载链路跟踪信息, 典型的是全链路跟踪系统中的trace信息传递, 染色信息可以认为是trace数据的一部分.
链路跟踪系统基本的要求是trace信息能在完整的调用链中传递, 具体实现方式在SDK类型的框架和sidecar类型框架中不太一致:
-
SDK类型的框架: 可以做到透明的trace信息传递, 用户业务代码不需要修改, 但是要求引入指定的SDK, SDK实现了RPC调用的自动trace提取和注入. 但提取和注入和编程语言、RPC类型强相关.
-
Sidecar 类型的框架: 无法做到透明的trace信息传递, 需要用户业务代码主动获取上个链路数据, 并主动注入到下个RPC header中. 虽然sidecar可以解析出和操纵 trace/染色信息, 但是无法透明实现, 原因主要是:
1) Envoy是独立的Listener来处理Inbound和Outbound的请求。Inbound只会处理入的流量并将流量转发到本地的服务实例上。而Outbound就是根据服务发现找到对应的目标服务后端上。除了实在一个进程里外两个之间可以说没有任何关系。 对于sidecar 而言, inbound 流量和 outbound 流量是完全独立无关联的.
2) App业务代码是处理请求不是转发请求, 前后2次RPC是否是父子关系, 只有App可以决定.
相关资料:
istio 全链路跟踪需要app 显式传递x-b3 headers: istio DISTRIBUTED TRACINGOVERVIEW
envoy 跟踪传播: envoy Trace context propagation
6.3.4 需要有RequestId/Streamid, 以支持多路复
RequestId用来关联request和对应的response,请求报文中携带一个唯一的id值,应答报文中原值返回,以便在处理response时可以找到对应的request, 此信息不同RPC中叫法可能不同.
并非所有协议都有RequestId/Streamid, 如Dubbo/thrift/http2 带有此信息, 但是HTTP/1.1不支持多路复用并没有类似数据结构.
7. 社区类似场景和方案
7.1 envoy 官方提供了http1和grpc 协议的转换:
-
client 端 http request 转grpc:
场景: client端只支持http1, 但是要和grpc server 端通信:
https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/grpc_bridge
-
server 端grpc 转http:
场景: server 端只支持http1, 但是要和grpc client 端通信:
假设http1 是一种私有协议的话, 这2个filter 功能非常类似我们要做的「协议透明转换」filter.
7.2 蚂蚁金服service mesh产品 SOFAMesh 的通用协议扩展:
- 自研golang sidecar sofa-mosn 替代envoy作为数据面代理
- 自研通用协议
X-PROTOCOL
, 以支持新的RPC协议低成本接入
出于技术栈和性能考虑, 蚂蚁用golang 重新实现了envoy的功能, 并实现了一套可扩展的私有协议接入方案.
SOFAMesh 相关资料:
7.3 有赞:
场景: 公司多语言技术栈: nodejs 使用http做RPC, java 使用dubbo 做RPC. nodejs项目需要调用java服务, 并做统一服务治理.
实现: 自研golang sidecar, 将nodejs 项目发出的http 转换为java dubbo 协议.
8. 方案预研大致工作
-
预研私有协议选型: 序列化和反序列化、header、payload 格式等.
-
开发envoy 协议转换filter, filter 实现destination信息, 染色信息提取、操纵;
目前istio 使用的envoy 是改造过的, 代码: https://github.com/istio/proxy. 其中包括扩展的mixer client filter. 此方案filter 也应基于此repo扩展.
-
istio pilot 组件改造: 扩展service上新的私有协议的注册和识别, 在pilot和数据面envoy交互时, 针对私有协议的服务,pilot需要重组下发的xDS:
在outbound listener 的filter chain 首, 加入「私有协议转gRPC」的filter
在 inbound listener 的filter chain 尾, 加入「gRPC转私有协议」的filter
-
环境搭建, 预研与验证.