从 Envoy Gateway 看 Gateway API 扩展性

从 Envoy Gateway 看 Gateway API 扩展性

概览

gwapi

Gateway API 支持三种扩展方式,如:

  • 针对 GatewayClass 提供 parametersRef
  • 针对 xRoute 提供 per-route、per-backend 的 implementation-specific filter (ExtensionRef)
  • 针对 Gateway 和 xRoute 整体 提供 PolicyAttachment

对比

下面通过一个表格来对比三种扩展的区别:

扩展方式生效对象生效范围面向角色作用
parametersRefGatewayClassGatewayClass 下所有的 Gateway 资源infrastructure provider为所管理 Gateway 提供通用配置
PolicyAttachmentGateway、xRoute网关级别、整体 Route 级别(xRoute下所有 Rule 生效)operator、developer为 Gateway 和 整体 Route 提供默认和覆盖值
ExtensionRefxRoute路由 (Rule) 级别、服务 (Backend) 级别developer为某个路由或者某个服务提供扩展能力

根据上面的表格,可以看出,三种扩展方式,分别在不同对象,不同范围内生效。

在不同场景下,我们可以根据具体需求选择扩展方式:

  • 针对只能在 路由/服务 级别生效的扩展能力,推荐使用 ExtensionRef,面向开发者(对 Envoy 来说,不是所有 filter 都支持路由级别的,这些扩展不适合使用 ExtensionRef 作为扩展)。
  • 针对需要针对整个 Gateway / 整体 Route 下所有 Rule 的场景,如安全相关,可以考虑使用 PolicyAttachment,这样不用在每个 Route 的每个 Rule 里配置重复的扩展配置,面向集群管理员和开发者。
  • 针对整体 Gateway 的通用配置,如所有网关的副本数等,应该在 GC 上使用 parametersRef,面向集群管理员,基础设施提供商。

Envoy Gateway 扩展 Gateway API

Envoy Gateway 基于 Gateway API 作为顶层配置语言,在实现了所有核心 Gateway API 定义的同时,也基于 Gateway API 的扩展点进行了扩展。

下面我们通过 Envoy Gateway 去看三种 Gateway API 扩展的具体实例

从 EnvoyProxy CRD 看 GatewayClass parametersRef

GatewayClass 往往被供应商所维护,定义了 Gateway 具体的实现,如:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

在集群中安装好 Envoy Gateway 后,这个 GatewayClass 会被 Envoy Gateway 识别(对比 controllerName)。Gateway 资源的创建时需要指定关联的 GatewayClass,也意味着这个 Gateway 资源会被 Envoy Gateway 控制面所管理,如:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 8080

创建 Gateway 资源后,集群中会自动创建 EnvoyProxy 的实例,由于 EnvoyProxy 的资源(Deployment/Service)是由 Envoy Gateway 控制面所管理,假如这个时候用户想对创建的 EnvoyProxy 实例资源进行修改怎么办?比如更改 EnvoyProxy 的 Service Annotation 或者 Service Type。

在 GatewayClass 的层级,Gateway API 提供了 parametersRef 支持引用自定义资源,去管理此 GatewayClass 所有的资源。

Envoy Gateway 于是定义了一个 CRD 叫做 EnvoyProxy,用户可以通过创建 EnvoyProxy CR,去对 EnvoyProxy 进行定制化,如:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-proxy-config
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        annotations:
          custom1: svc-annotation1
          custom2: svc-annotation2

此 EnvoyProxy CR 描述了对创建的 EnvoyProxy Service,添加了两个自定义 annotations。创建了 EnvoyProxy CR 之后,需要在 GatewayClass 中引用:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: custom-proxy-config
    namespace: envoy-gateway-system

接着 Envoy Gateway 控制面会读取这个 CR 并将配置更新到 EnvoyProxy Kubernetes 资源中。

从 EnvoyPatchPolicy 看 PolicyAttachment

除了 parametersRef,Gateway API 还提供了 PolicyAttachment 这种机制,扩展的对象是整体的 Gateway 或者 xRoute(HTTPRoute、gRPCRoute、UDPRoute)。

EnvoyPatchPolicy 是 Envoy Gateway 基于 PolicyAttachment 机制提供的一个 CRD,用于对指定的 Gateway(EnvoyProxy 实例)更新它的 xDS 配置信息,如:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
  name: custom-response-patch-policy
  namespace: default
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: eg
    namespace: default
  type: JSONPatch
  jsonPatches:
    - type: "type.googleapis.com/envoy.config.listener.v3.Listener"
      # The listener name is of the form <GatewayNamespace>/<GatewayName>/<GatewayListenerName>
      name: default/eg/http
      operation:
        op: add
        path: "/default_filter_chain/filters/0/typed_config/local_reply_config"
        value:
          mappers:
          - filter:
              status_code_filter:
                comparison:
                 op: EQ
                 value:
                   default_value: 404
                   runtime_key: key_b
            status_code: 406
            body:
              inline_string: "could not find what you are looking for"

可以看到上述 EnvoyPatchPolicy 的对象是:

  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: eg
    namespace: default

Envoy Gateway 控制面在读取到这个信息后,会将上述 xDS 配置 Patch 到对应 EnvoyProxy 中。

相比 GatewayClass,可以看到这种方式更灵活,场景也相对不同,扩展的对象也从 GatewayClass 变为单个 Gateway 或 Route 资源。

从 ratelimit filter 看 extensionRefs

Gateway API 在 xRoute 中,比如 HTTPRoute,可以在 RouteRules 中添加自定义的 Filter,扩展对象进一步缩小,将面对单个路由规则以及后端服务。

Envoy Gateway 之前在实现全局限流时,也是基于这种方式,定义了 RateLimitFilter 这个 CRD 去描述限流规则,然后在 HTTPRoute 引用它,如:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: RateLimitFilter
metadata:
  name: ratelimit-specific-user
spec:
  type: Global
  global:
    rules:
    - clientSelectors:
      - headers:
        - name: x-user-id
          value: one
      limit:
        requests: 3
        unit: Hour

对应的 HTTPRoute 配置为:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: http-ratelimit
spec:
  parentRefs:
  - name: eg
  hostnames:
  - ratelimit.example 
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.envoyproxy.io
        kind: RateLimitFilter
        name: ratelimit-specific-user
    backendRefs:
    - group: ""
      kind: Service
      name: backend
      port: 3000

可以看到在 filters 中引用了 ExtensionRef 类型的 Filter,此类型即为自定义的 Filter,会在路由 match 到指定规则后生效。

相比于上两种扩展方式,这个扩展范围更小,面向的是路由规则。

说到最后

本文通过 Envoy Gateway 介绍了三种扩展 Gateway API 的方式,大家也能感受到 Gateway API 在扩展性设计上考虑了很多。

为什么提供这么多扩展方式?求同存异 是 Gateway API 的理念之一。

Gateway API 提供了核心流量规则的描述,同时也支持额外的自定义需求。

比如云厂商在创建 Gateway 后指定的 LB 参数,同时也包括一些 Gateway Specific 的 API,比如 EnvoyPatchPolicy / WASMExtensionPolicy 等这些 EnvoyProxy 特有的能力。

Ingress 往往扩展都往 annotation 这样的非结构化数据上丢的,在 API 可移植性上存在巨大的阻碍,单单 nginx-ingress 的 annotation 就有上百种,更别说其他 Ingress 实现了。

标准化引用 CRD,在 Kubernetes 中,不管是 API 描述,校验,维护、升级上,都远好于 annotation 的 string map,也更适合复杂场景的描述。

comments powered by Disqus

Related Posts

EnvoyProxy 中 UDP 协议概览

EnvoyProxy 中 UDP 协议概览

本文结合一些基础的网络知识,介绍了 EnvoyProxy 中 UDP 协议的介绍与使用。

Read More
Endpoints vs EndpointSlice

Endpoints vs EndpointSlice

本文简单的比较了一下 `Endpoints` 和 `EndpointSlice` 的优劣

Read More