据调研数据显示,约 70% 的生产故障是由变更引起的。为了消除变更过程存在的风险,在发布过程中,我们总是希望能够用小部分特定流量来验证下新发布应用是否正常。即使新版本有问题,也能及时发现,控制影响面,保障了整体的稳定性,这就是微服务架构下的全链路灰度的能力。
MSE 在微服务全链路灰度场景下提供了一套成熟完善且开箱即用的能力。
随着企业微服务化改造的深入,对微服务治理的场景与应用也有了更多的诉求,全链路灰度就是如此。MSE 默认支持按照内容规则与百分比规则的灰度路由策略,其中按照内容灰度支持 header、params 等参数件支持精确/前缀/正则等多种匹配策略,满足常见全链路灰度场景的诉求。
如果我们遇到较为复杂的场景,发现 MSE 提供的策略无法满足我们诉求时,应该怎么解决?
接下来,我们来一起讨论几个合理且较为复杂的灰度需求。
1. 很多复杂且合理的灰度诉求
其实关于全链路灰度有其他很多合理的诉求,比如:
1. 我们希望随机百分比可以根据参数特征来调整,这样对于每个用户来说,是否被灰度是固定的,多次调用体验一致。
2. 我们来自于手机客户端的流量带有 version 特征、来自网页流量又是带有 tag 的特征,我们期望两者满足任一条件的流量去往灰度环境即流量条件匹配为“或”的模式;
3. 来自我们生产流量确实比较大,我们期望第一批灰度的流量可以控制到整体 1‰ 流量的灰度诉求;
4. 我们期望可以基于流量 Body 参数解析的灰度诉求。
面对一系列复杂并带有定制需求的合理诉求,产品层面很难做到完全支持。这些诉求在企业客户的生产实际中非常常见,而当前的 MSE 控制台配置方案似乎并不能完美应对这些多样化的实际场景。毕竟,当我们将目光转向不同企业客户的生产实践时,会发现复杂场景的变化和多样性只会更加显著。那么,我们如何能够有效地满足复杂环境下全链路灰度发布的诉求呢?
2. 云原生网关 WASM 插件
什么是 WASM?WASM(WebAssembly)是一种可移植、高性能的二进制指令集,用于在 Web 浏览器中运行代码。Envoy 使用 WASM 作为插件扩展机制,允许开发人员编写自定义的功能扩展,以满足特定的需求。
云原生网关 WASM 插件扩展机制的工作原理如下:
1. MSE 云原生网关提供了插件市场,供我们编写自定义的 WASM 插件来满足各种扩展的诉求,如请求/响应转换、过滤器、身份验证等。同时编写 WASM 插件支持(Go、Rust、类 JS、lua 等)。
2. 我们只需要将编译好的 WASM 文件通过自定义插件的方式上传到插件市场,MSE 云原生网关会将其加载到 Envoy 中。
3. 当云原生网关处理网络流量时,它会根据配置将流量传递给适当的 WASM 插件进行处理,即上图的 Custom Filters。WASM 插件可以读取和修改请求/响应数据,执行自定义逻辑,并将流量传递给下一个插件或最终目标。
MSE 插件市场还提供了一些默认认证鉴权、流量管控、安全防护等平台官方插件,可以帮助我们提升网关的安全与稳定性,并且支持多语言自定义扩展,满足网关上自定义流量治理需求。
MSE WASM 插件扩展机制的优点包括:
1. 借助 WASM 特性支持多语言扩展,提供了灵活性和可扩展性,可以通过 WASM 插件编写开发,满足特定的业务需求。
2. 网关 Wasm 插件与开源 Envoy100% 兼容,不存在锁定。
3. 提供插件市场,网关的二次扩展功能均通过插件提供给用户按需使用。
4. 插件采用热更新机制,在沙盒中执行,对网关自身稳定性无影响。
WASM 插件以其独特的轻量级和高效性能特点,为云原生网关带来了创新的扩展能力,而这一切不会带来明显的性能开销。WASM 插件运行在沙箱环境中,提供了一种安全可控的方式来部署自定义逻辑,这样不仅保障了网关的灵活性和可扩展性,也确保了对整体性能的最小影响。
看起来 MSE 云原生网关的 WASM 插件确实是一种优雅且便捷的方式,能够满足各种全链路灰度的需求。接下来,我将通过编写 WASM 插件来实现在复杂条件下的全链路灰度。
3. 通过 WASM 插件实现参数比例
上文提到,WASM 插件可以支持多语言扩展,我们可以选择我们擅长的语言进行开发,本文以 Go 语言为例。
云原生网关提供了 wrapper 包以及相关的 API 供我们快速编写 WASM 插件。
1. 云原生网关配置基于 x-mse-tag 的灰度路由,详见基于 MSE 云原生网关实现全链路灰度[1]。
服务治理泳道配置如下:
云原生网关路由配置如下:
我们创建灰度泳道,只要 header 中存在 x-mse-tag=gray 的请求都会被认为是灰度流量,且在后续链路中都会优先去往灰度环境。因此在 WASM 插件中,我们可以对流量进行任意自定义的计算和匹配。只要符合我们的灰度条件,我们就可以在请求头中添加一个名为 "x-mse-tag" 值为 "gray" 的标识。这样,我们就可以对灰度流量进行标记和识别。
2. 定义插件扩展配置。
type ParamsRandomConfig struct { # 参数比例功能开关 paramsRandomEnable bool # 参数比例依据哪个Header的值,例如userId paramsRandomHeaderKey string # 参数比例的百分比值, paramsPercentageRatio int64 }
3. 解析插件扩展参数,在控制台插件配置中填写的 YAML 配置会自动转换为 JSON,此处直接从 JSON 这个参数里解析配置即可。
// 在控制台插件配置中填写的YAML配置会自动转换为JSON,此处直接从JSON这个参数里解析配置即可 func parseConfig(json gjson.Result, config *MyConfig, log wrapper.Log) error { // 解析出配置,更新到config中 config.paramsRandomEnable = json.Get("paramsRandomEnable").Bool() config.paramsRandomHeaderKey = json.Get("paramsRandomHeaderKey").String() config.paramsPercentageRatio = json.Get("paramsPercentageRatio").Int() return nil }
4. 请求处理 Filter 编写。
func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log wrapper.Log) types.Action { if config.paramsRandomEnable { randomHeaderValue, err := proxywasm.GetHttpRequestHeader(config.paramsRandomHeaderKey) if err != nil { proxywasm.LogErrorf("get header enhance error: %v", err) return types.ActionContinue } // 取目标参数值的 hash ,用于百分比值计算 hash := sha256.Sum256([]byte(randomHeaderValue)) hashInt := new(big.Int) hashInt.SetBytes(hash[:]) modulo := new(big.Int).Mod(hashInt, big.NewInt(100)) result := modulo.Cmp(big.NewInt(config.paramsPercentageRatio)) if result <= 0 { // 写入 x-mse-tag=gray 说明该请求流量标为 gray,在后续链路中会优先去玩gray环境的节点, // 如果对应的应用没有gray环境,会fallback到基线环境 proxywasm.AddHttpRequestHeader("x-mse-tag", "gray") } else { // 不符合灰度条件的流量 proxywasm.LogInfof("set header false value: %s, hash: %s", randomHeaderValue, hashInt) } } return types.ActionContinue }
5. 编译生成 WASM 文件
我们通过如下命令编译生成 WASM 文件。
go mod tidy tinygo build -o main.wasm -scheduler=none -target=wasi -gc=custom -tags='custommalloc nottinygc_finalizer' ./main.go
编译成功会在当前目录下创建文件 main.wasm。该文件在下文本地调试的示例中也会被用到。
在使用云原生网关插件市场的自定义插件功能时,直接上传该文件即可。
6. 配置参数并验证参数比例功能
如上图所示,我们指定 Header 中的 userId 为百分比依据的参数,并且配置了 10% 流量灰度的比例值。点击保存后配置,实时生效。
请求过程中 userId=1 的 header 恒定去往灰度环境,userId=11 的请求恒定去往基线环境。
7. 观察插件日志
到目前为止,我们通过编写 WASM 插件实现了根据特定 Header 的参数比例需求。
4. 总结
通过 WASM 插件,我们可以实现各种全链路灰度的需求,包括但不限于以下几个方面:
- 根据用户标识进行灰度
可以根据用户的身份、角色、权限等信息将特定用户的请求路由到相应的灰度环境,以实现个别用户的全链路灰度。
- 根据地理位置进行灰度
可以根据用户的地理位置信息将请求路由到特定地区的灰度环境,以满足特定地区的全链路灰度需求。
- 基于流量比例的灰度
可以根据流量比例将请求路由到不同的灰度环境,以实现按比例分配流量的全链路灰度。
- 基于请求包复杂属性的灰度
可以根据请求的属性,如请求头、请求体、查询参数等信息,来判断是否满足特定条件,从而路由请求到相应的灰度环境。
利用 WASM 插件的强大适应性,我们可以针对性地编写插件以适应不同的全链路灰度发布需求。这为定制化业务场景提供了无限的可能性,使得灰度测试和发布可以根据独特的业务要求灵活执行。特别是对于计算密集型和无状态的任务,如认证鉴权、请求/响应的加密和混淆、内容转换等,将这些逻辑部署在网关层是理想选择。它不仅保持了系统的简洁和灵活性,而且确保了核心网关功能的低性能损失,这在优化资源使用和维护服务品质方面都提供了显著的好处。
目前 MSE WASM 插件支持 Redis 访问,当然 WASM 插件也不是万能的,如果逻辑里需要对接数据库,或者要起多线程处理,就不适合做成网关插件,当前 WASM 插件也不支持这些能力。
参考链接:
[2] 开发插件_微服务引擎(MSE)
作者:十眠