适应多样化需求:WASM 插件在全链路灰度发布中的应用

简介: MSE(微服务引擎)在微服务全链路灰度场景下提供了一套成熟的功能,支持内容规则和百分比规则的灰度路由策略。


据调研数据显示,约 70% 的生产故障是由变更引起的。为了消除变更过程存在的风险,在发布过程中,我们总是希望能够用小部分特定流量来验证下新发布应用是否正常。即使新版本有问题,也能及时发现,控制影响面,保障了整体的稳定性,这就是微服务架构下的全链路灰度的能力。


MSE 在微服务全链路灰度场景下提供了一套成熟完善且开箱即用的能力。

image.png

随着企业微服务化改造的深入,对微服务治理的场景与应用也有了更多的诉求,全链路灰度就是如此。MSE 默认支持按照内容规则与百分比规则的灰度路由策略,其中按照内容灰度支持 header、params 等参数件支持精确/前缀/正则等多种匹配策略,满足常见全链路灰度场景的诉求。


如果我们遇到较为复杂的场景,发现 MSE 提供的策略无法满足我们诉求时,应该怎么解决?


接下来,我们来一起讨论几个合理且较为复杂的灰度需求。


1. 很多复杂且合理的灰度诉求


其实关于全链路灰度有其他很多合理的诉求,比如:


1. 我们希望随机百分比可以根据参数特征来调整,这样对于每个用户来说,是否被灰度是固定的,多次调用体验一致。

2. 我们来自于手机客户端的流量带有 version 特征、来自网页流量又是带有 tag 的特征,我们期望两者满足任一条件的流量去往灰度环境即流量条件匹配为“或”的模式;

3. 来自我们生产流量确实比较大,我们期望第一批灰度的流量可以控制到整体 1‰ 流量的灰度诉求;

4. 我们期望可以基于流量 Body 参数解析的灰度诉求。


面对一系列复杂并带有定制需求的合理诉求,产品层面很难做到完全支持。这些诉求在企业客户的生产实际中非常常见,而当前的 MSE 控制台配置方案似乎并不能完美应对这些多样化的实际场景。毕竟,当我们将目光转向不同企业客户的生产实践时,会发现复杂场景的变化和多样性只会更加显著。那么,我们如何能够有效地满足复杂环境下全链路灰度发布的诉求呢?


2. 云原生网关 WASM 插件


什么是 WASM?WASM(WebAssembly)是一种可移植、高性能的二进制指令集,用于在 Web 浏览器中运行代码。Envoy 使用 WASM 作为插件扩展机制,允许开发人员编写自定义的功能扩展,以满足特定的需求。

image.png

云原生网关 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]


服务治理泳道配置如下:

image.png

云原生网关路由配置如下:

image.png

我们创建灰度泳道,只要 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. 配置参数并验证参数比例功能

image.png

如上图所示,我们指定 Header 中的 userId 为百分比依据的参数,并且配置了 10% 流量灰度的比例值。点击保存后配置,实时生效。


请求过程中 userId=1 的 header 恒定去往灰度环境,userId=11 的请求恒定去往基线环境。


7. 观察插件日志


到目前为止,我们通过编写 WASM 插件实现了根据特定 Header 的参数比例需求。


4. 总结


通过 WASM 插件,我们可以实现各种全链路灰度的需求,包括但不限于以下几个方面:


  • 根据用户标识进行灰度

可以根据用户的身份、角色、权限等信息将特定用户的请求路由到相应的灰度环境,以实现个别用户的全链路灰度。

  • 根据地理位置进行灰度

可以根据用户的地理位置信息将请求路由到特定地区的灰度环境,以满足特定地区的全链路灰度需求。

  • 基于流量比例的灰度

可以根据流量比例将请求路由到不同的灰度环境,以实现按比例分配流量的全链路灰度。

  • 基于请求包复杂属性的灰度

可以根据请求的属性,如请求头、请求体、查询参数等信息,来判断是否满足特定条件,从而路由请求到相应的灰度环境。


利用 WASM 插件的强大适应性,我们可以针对性地编写插件以适应不同的全链路灰度发布需求。这为定制化业务场景提供了无限的可能性,使得灰度测试和发布可以根据独特的业务要求灵活执行。特别是对于计算密集型和无状态的任务,如认证鉴权、请求/响应的加密和混淆、内容转换等,将这些逻辑部署在网关层是理想选择。它不仅保持了系统的简洁和灵活性,而且确保了核心网关功能的低性能损失,这在优化资源使用和维护服务品质方面都提供了显著的好处。


目前 MSE WASM 插件支持 Redis 访问,当然 WASM 插件也不是万能的,如果逻辑里需要对接数据库,或者要起多线程处理,就不适合做成网关插件,当前 WASM 插件也不支持这些能力。


参考链接:

[1] 基于 MSE 云原生网关实现全链路灰度

[2] 开发插件_微服务引擎(MSE)


作者:十眠

作者介绍
目录