Higress - wasm资源机制

224 阅读8分钟

WasmPlugin资源的调用与绑定流程

在Higress中,WasmPlugin资源是通过一系列步骤被处理、转换并最终绑定到Envoy代理上执行的。以下将详细解释这个过程的代码逻辑和时序。

WasmPlugin资源定义

首先,WasmPlugin是一个Kubernetes自定义资源(CRD),它的定义在API扩展包中.

WasmPlugin资源包含多个关键字段,如URL(指向Wasm模块或OCI容器)、配置信息、执行阶段等:

message WasmPlugin {
  // URL of a Wasm module or OCI container. If no scheme is present,
  // defaults to `oci://`, referencing an OCI image. Other valid schemes
  // are `file://` for referencing .wasm module files present locally
  // within the proxy container, and `http[s]://` for .wasm module files
  // hosted remotely.
  string url = 2;

  // SHA256 checksum that will be used to verify Wasm module or OCI container.
  // If the `url` field already references a SHA256 (using the `@sha256:`
  // notation), it must match the value of this field. If an OCI image is
  // referenced by tag and this field is set, its checksum will be verified
  // against the contents of this field after pulling.
  string sha256 = 3;

  // The pull behaviour to be applied when fetching an OCI image. Only
  // relevant when images are referenced by tag instead of SHA. Defaults
  // to IfNotPresent, except when an OCI image is referenced in the `url`
  // and the `latest` tag is used, in which case `Always` is the default,
  // mirroring K8s behaviour.
  // Setting is ignored if `url` field is referencing a Wasm module directly
  // using `file://` or `http[s]://`
  PullPolicy image_pull_policy = 4;

  // Credentials to use for OCI image pulling.
  // Name of a K8s Secret in the same namespace as the `WasmPlugin` that
  // contains a docker pull secret which is to be used to authenticate
  // against the registry when pulling the image.
  string image_pull_secret = 5;

  // Public key that will be used to verify signatures of signed OCI images
  // or Wasm modules. Must be supplied in PEM format.
  string verification_key = 6;

  // The configuration that will be passed on to the plugin.
  google.protobuf.Struct plugin_config = 7;

  // The plugin name to be used in the Envoy configuration (used to be called
  // `rootID`). Some .wasm modules might require this value to select the Wasm
  // plugin to execute.
  string plugin_name = 8;

  // Determines where in the filter chain this `WasmPlugin` is to be injected.
  PluginPhase phase = 9;

  // Determines ordering of `WasmPlugins` in the same `phase`.
  // When multiple `WasmPlugins` are applied to the same workload in the
  // same `phase`, they will be applied by priority, in descending order.
  // If `priority` is not set, or two `WasmPlugins` exist with the same
  // value, the ordering will be deterministically derived from name and
  // namespace of the `WasmPlugins`. Defaults to `0`.
  google.protobuf.Int32Value priority = 10;

  // Specifies the failure behavior for the plugin due to fatal errors.
  FailStrategy fail_strategy = 13;

  // Configuration for a Wasm VM.
  // more details can be found [here](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/wasm/v3/wasm.proto#extensions-wasm-v3-vmconfig).
  VmConfig vm_config = 11;

  // 这里定义了WasmPlugin自定义资源的结构,包含URL、配置信息、执行阶段等关键字段
  // Extended by Higress, the default configuration takes effect globally
  google.protobuf.Struct default_config = 101;
  // Extended by Higress, matching rules take effect
  repeated MatchRule match_rules = 102;
  // disable the default config
  google.protobuf.BoolValue default_config_disable = 103;
}

WasmPlugin资源处理流程

  1. 资源监听与事件触发

Higress控制器会监听WasmPlugin资源的变化(创建、更新、删除),并触发相应的处理函数。当WasmPlugin资源发生变化时,会调用AddOrUpdateWasmPluginDeleteWasmPlugin方法:

  1. 资源转换

WasmPlugin资源需要被转换为Istio的WasmPlugin格式,这是通过convertIstioWasmPlugin方法实现的:

这个转换过程会处理以下内容:

  • 设置选择器,确定插件应用到哪些工作负载

  • 复制URL、SHA256等基本信息

  • 处理插件配置信息

  • 处理VM配置(如环境变量)

// 注册处理WasmPlugin资源变更的处理函数,这些处理函数会将WasmPlugin资源的变更转换为xDS配置,并通过Istio的XDS服务器下发到Envoy代理
func (m *IngressConfig) RegisterEventHandler(kind config.GroupVersionKind, f istiomodel.EventHandler) {
    IngressLog.Infof("register resource %v", kind)
    switch kind {
    case gvk.VirtualService:
        m.virtualServiceHandlers = append(m.virtualServiceHandlers, f)

    case gvk.Gateway:
        m.gatewayHandlers = append(m.gatewayHandlers, f)

    case gvk.DestinationRule:
        m.destinationRuleHandlers = append(m.destinationRuleHandlers, f)

    case gvk.EnvoyFilter:
        m.envoyFilterHandlers = append(m.envoyFilterHandlers, f)

    case gvk.ServiceEntry:
        m.serviceEntryHandlers = append(m.serviceEntryHandlers, f)

    // 当资源类型为WasmPlugin时,将处理函数添加到wasmPluginHandlers列表中
case gvk.WasmPlugin:
    m.wasmPluginHandlers = append(m.wasmPluginHandlers, f)
    }

    for _, remoteIngressController := range m.remoteIngressControllers {
        remoteIngressController.RegisterEventHandler(kind, f)
    }
    for _, remoteGatewayController := range m.remoteGatewayControllers {
        remoteGatewayController.RegisterEventHandler(kind, f)
    }
}

3. 配置处理

WasmPlugin资源支持全局配置和基于规则的匹配配置: ingress_config.go

如果defaultConfigDisable为true且没有有效的匹配规则,则不会生成Istio WasmPlugin资源:

  1. 资源存储与通知

转换后的Istio WasmPlugin资源会被存储在内存中,并通过处理器函数通知Istio控制平面

当资源被删除时,也会触发相应的通知

WasmPlugin资源的应用

当用户创建一个WasmPlugin资源时, 这个资源会经过上述处理流程,最终被转换为Istio的WasmPlugin资源,并应用到Envoy代理上。

路由级或域名级匹配

WasmPlugin资源支持通过matchRules字段实现更精细的控制,可以将插件应用到特定的入口或域名

代码时序图

插件执行流程

当HTTP请求到达Envoy代理时,Wasm插件会根据其配置的阶段(Phase)和优先级(Priority)在HTTP过滤器链中的特定位置执行:

  1. 请求阶段:处理请求头部和请求体
  2. 响应阶段:处理响应头部和响应体

插件的执行是由Envoy的Wasm运行时环境管理的,它会调用插件中注册的各种处理函数,如onHttpRequestHeadersonHttpRequestBody等。

总结

WasmPlugin资源的处理流程可以概括为以下几个步骤:

  1. 用户创建或更新WasmPlugin资源
  2. Higress控制器监听到资源变化并触发处理函数
  3. 资源被转换为Istio WasmPlugin格式
  4. 转换后的资源被存储并通知Istio控制平面
  5. Istio控制平面通过xDS协议将配置下发到Envoy代理
  6. Envoy代理加载Wasm模块并在HTTP过滤器链中执行插件

这个流程确保了WasmPlugin资源能够正确地被应用到Envoy代理上,并在HTTP请求处理过程中执行相应的逻辑。

WasmPlugin资源的处理流程

  1. 用户创建或更新WasmPlugin资源

用户通过Kubernetes API创建或更新WasmPlugin自定义资源。这个资源的定义在api/extensions/v1alpha1/wasmplugin.proto文件中

WasmPlugin资源包含多个关键字段,如URL、配置信息、执行阶段等。一个典型的WasmPlugin资源YAML示例如下:

  1. Higress控制器监听到资源变化并触发处理函数

Higress控制器通过Kubernetes的Informer机制监听WasmPlugin资源的变化。当资源发生变化时,会触发相应的处理函数。

pkg/ingress/kube/wasmplugin/controller.go中,WasmPluginController负责监听WasmPlugin资源:

type WasmPluginController controller.Controller[listersv1.WasmPluginLister]  func NewController(client kubeclient.Client, options common.Options) WasmPluginController { var informer cache.SharedIndexInformer if options.WatchNamespace == "" { informer = client.HigressInformer().Extensions().V1alpha1().WasmPlugins().Informer() } else { informer = client.HigressInformer().InformerFor(&v1.WasmPlugin{}, func(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { return informersv1.NewWasmPluginInformer(client, options.WatchNamespace, resyncPeriod, nil) }) } return controller.NewCommonController("wasmplugin", listersv1.NewWasmPluginLister(informer.GetIndexer()), informer, GetWasmPlugin, options.ClusterId) }  func GetWasmPlugin(lister listersv1.WasmPluginLister, namespacedName types.NamespacedName) (controllers.Object, error) { return lister.WasmPlugins(namespacedName.Namespace).Get(namespacedName.Name) }  // WasmPluginController监听WasmPlugin资源的变化 // 当WasmPlugin资源发生变化时,该控制器会被触发相应的处理函数 type  WasmPluginController struct  { // 客户端接口,用于与Kubernetes API交互 client         client.Interface // WasmPlugin资源的列表器,用于获取WasmPlugin资源 wasmPluginLister listerv1.WasmPluginLister // WasmPlugin资源的共享索引通知器,用于监听资源变化 wasmPluginInformer cache.SharedIndexInformer // 处理函数列表,当资源变化时会调用这些函数 handlers       [] func (util.ClusterNamespacedName) } 

当WasmPlugin资源发生变化时,会调用AddOrUpdateWasmPlugin方法: ingress_config.go:1045-1054

这个方法首先检查资源的命名空间是否匹配,然后从lister中获取WasmPlugin资源。

  1. 资源被转换为Istio WasmPlugin格式

获取到WasmPlugin资源后,需要将其转换为Istio的WasmPlugin格式,这是通过convertIstioWasmPlugin方法实现的.

这个转换过程会处理以下内容:

  • 设置选择器,确定插件应用到哪些工作负载(通过Selector字段)
  • 复制URL、SHA256等基本信息
  • 处理插件配置信息(PluginConfig字段)
  • 设置执行阶段(Phase字段)和优先级(Priority字段)
  • 设置失败策略(FailStrategy字段)
  1. 配置处理

WasmPlugin资源支持全局配置和基于规则的匹配配置。如果设置了defaultConfigDisable为true且没有有效的匹配规则,则不会生成Istio WasmPlugin资源: ingress_config.go:934-939 ingress_config.go:1035-1038

这里的逻辑是:

  1. 如果DefaultConfigDisable为false,则使用DefaultConfig作为插件配置
  2. 如果有匹配规则,则处理匹配规则
  3. 如果DefaultConfigDisable为true且没有有效的匹配规则,则返回nil,不生成资源
  4. 转换后的资源被存储并通知Istio控制平面

转换后的Istio WasmPlugin资源会被存储在内存中,并通过处理器函数通知Istio控制平面

这里的代码将转换后的WasmPlugin资源存储在wasmPlugins映射中,然后通过wasmPluginHandlers通知Istio控制平面。

当资源被删除时,也会触发相应的通知: ingress_config.go:1096-1108

这里的代码从wasmPlugins映射中删除资源,然后通过wasmPluginHandlers通知Istio控制平面资源已被删除。

  1. Istio控制平面通过xDS协议将配置下发到Envoy代理

Istio控制平面(Istiod)接收到WasmPlugin资源的变更通知后,会将其转换为Envoy的配置,并通过xDS协议下发到Envoy代理。

pkg/ingress/config/ingress_config.go中,RegisterEventHandler方法注册了处理WasmPlugin资源变更的处理函数:

func (m *IngressConfig) RegisterEventHandler(kind config.GroupVersionKind, f istiomodel.EventHandler) {  
    IngressLog.Infof("register resource %v", kind)  
    switch kind {  
    // ...其他资源类型...
    case gvk.WasmPlugin:  
        m.wasmPluginHandlers = append(m.wasmPluginHandlers, f)  
    }  
    // ...
}

这些处理函数会将WasmPlugin资源的变更转换为xDS配置,并通过Istio的XDS服务器下发到Envoy代理。

  1. Envoy代理加载Wasm模块并在HTTP过滤器链中执行插件

Envoy代理接收到xDS配置后,会加载Wasm模块并在HTTP过滤器链中执行插件。

Wasm插件在Envoy中的执行是由Envoy的Wasm运行时环境管理的。根据插件的配置(如PhasePriority),插件会在HTTP过滤器链中的特定位置执行。

插件的执行流程如下:

  1. 请求阶段:处理请求头部(onHttpRequestHeaders)和请求体(onHttpRequestBody
  2. 响应阶段:处理响应头部(onHttpResponseHeaders)和响应体(onHttpResponseBody