从 Envoy Gateway 看 Gateway API 扩展性
- Xunzhuo
- Gateway API
- December 3, 2023
概览
Gateway API 支持三种扩展方式,如:
- 针对 GatewayClass 提供 parametersRef
- 针对 xRoute 提供 per-route、per-backend 的 implementation-specific filter (ExtensionRef)
- 针对 Gateway 和 xRoute 整体 提供 PolicyAttachment
对比
下面通过一个表格来对比三种扩展的区别:
扩展方式 | 生效对象 | 生效范围 | 面向角色 | 作用 |
---|---|---|---|---|
parametersRef | GatewayClass | GatewayClass 下所有的 Gateway 资源 | infrastructure provider | 为所管理 Gateway 提供通用配置 |
PolicyAttachment | Gateway、xRoute | 网关级别、整体 Route 级别(xRoute下所有 Rule 生效) | operator、developer | 为 Gateway 和 整体 Route 提供默认和覆盖值 |
ExtensionRef | xRoute | 路由 (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,也更适合复杂场景的描述。