☰
kgateway API 与 CRD 开发指南:从类型定义到代码生成与注册的完整实践
2026/10/12 1:43:29 网站建设 项目流程
  • API网关
  • 云原生
  • 微服务

【免费下载链接】kgateway

The Cloud-Native API Gateway and AI Gateway

项目地址:https://gitcode.com/gh_mirrors/kg/kgateway
点击查看免费下载

kgateway 是一个云原生 API 网关与 AI 网关项目,其所有网关能力(Backend、TrafficPolicy、ListenerPolicy、GatewayParameters 等)都构建在自定义资源(CRD)之上。本文以仓库中的 api/README.md 为核心骨架,结合 gateway_parameters_types.go、hack/generate.sh、pkg/apiclient/types.go 等源码,完整讲解"为 kgateway 新增一个 API / CRD"的五个标准步骤、API 类型编写规范,以及如何将 Gateway API 的策略类型安全地复刻进 TrafficPolicy API。读完本文,你将能够在 kgateway 代码库中独立添加新的自定义资源类型,并理解其从 Go 类型到 CRD、RBAC、clientset 的全链路生成机制。

api 目录定位:kgateway 的 API 类型仓库

api目录存放 kgateway 全部 API 与自定义资源的 Go 类型定义,是控制面与数据面配置模型的事实来源(source of truth)。当前仓库中该目录的核心布局如下:

  • api/v1alpha1/kgateway:kgateway 自有资源的 Go 类型,API 组为gateway.kgateway.dev,版本为v1alpha1,包含 backend_types.go、traffic_policy_types.go、listener_policy_types.go、gateway_parameters_types.go 等类型文件;
  • api/v1alpha1/shared:跨资源复用的共享类型,如 shared_types.go 中的LocalPolicyTargetReference、StringMatcher、HeaderModifiers,以及 timeouts.go 中的Timeouts;
  • 每个 API 版本目录下还有由 codegen 生成的 zz_generated.deepcopy.go 与 zz_generated.register.go(带DO NOT EDIT注释,均不可手工修改)。

从 zz_generated.register.go 可以看到,生成的注册文件声明了GroupName = "gateway.kgateway.dev"、GroupVersion = {Group: "gateway.kgateway.dev", Version: "v1alpha1"},并通过addKnownTypes将Backend、TrafficPolicy、ListenerPolicy、GatewayParameters等类型及其 List 类型注册进 Kubernetes scheme,这是后续生成 clientset、进行 List/Watch 的基础。

新增一个 API / CRD 的五个标准步骤

api/README.md 给出了向 Kubernetes Gateway 集成中新增 CRD 的完整流程,共五步。下面逐步展开,并结合仓库源码说明每一步的实际含义。

步骤 1:创建 API 版本目录与doc.go

如果是首次创建新 API 版本(例如未来的v1、v2alpha1),需要为该版本新建目录,并在其中创建doc.go,写入// +kubebuilder:object:generate=true注解,使该目录下的 Go 类型在运行 codegen 时被转换为 CRD。以现有版本为例:

// api/v1alpha1/kgateway/doc.go // +k8s:openapi-gen=true // +kubebuilder:object:generate=true // +groupName=gateway.kgateway.dev // +versionName=v1alpha1 package kgateway

关键注解说明:

  • +kubebuilder:object:generate=true:指示 controller-gen 为该目录下的类型生成zz_generated.deepcopy.go;
  • +groupName标记(marker):指定生成 CRD 的 API 组名,gateway.kgateway.dev正是所有 kgateway 自有 CRD 的组名,与 zz_generated.register.go 中的GroupName常量一致;
  • RBAC 规则通过+kubebuilder:rbac注解定义。README 特别强调:该注解不应挂在类型(struct)上,而应挂在文件或包级别。查看 doc.go 可以看到,它集中声明了 Gateway API 资源(gatewayclasses、httproutes、grpcroutes 等)、Controller 资源(pods、secrets、namespaces、endpoints)、Proxy deployer 资源(deployments、services、serviceaccounts、poddisruptionbudgets、HPA/VPA)、EDS 资源(endpointslices)、Istio 资源以及 leader election 所需 lease 的 RBAC 权限。

步骤 2:编写_types.go类型文件

在 API 版本目录中创建_types.go文件,定义资源类型与资源列表类型。README 以 gateway_parameters_types.go 为范本,其结构如下:

资源类型(struct):包含 metadata 字段、Spec与Status,并叠加kubebuilder注解:

// +kubebuilder:rbac:groups=gateway.kgateway.dev,resources=gatewayparameters,verbs=get;list;watch // +kubebuilder:rbac:groups=gateway.kgateway.dev,resources=gatewayparameters/status,verbs=get;update;patch // +genclient // +kubebuilder:object:root=true // +kubebuilder:metadata:labels={app=kgateway,app.kubernetes.io/name=kgateway} // +kubebuilder:resource:categories=kgateway,path=gatewayparameters // +kubebuilder:subresource:status type GatewayParameters struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ObjectMeta `json:"metadata,omitempty"` // +required Spec GatewayParametersSpec `json:"spec"` // +optional Status GatewayParametersStatus `json:"status,omitempty"` }

要点:

  • 在类型上方的+kubebuilder:rbac注解只声明本资源的 RBAC 规则(如对gatewayparameters的get;list;watch,对gatewayparameters/status的get;update;patch);
  • +kubebuilder:object:root=true标记该类型是根对象(会生成 deepcopy 与 List 类型);
  • +kubebuilder:subresource:status启用/status子资源;
  • +kubebuilder:resource:categories=kgateway,path=gatewayparameters设置资源分类与路径(复数形式)。

资源列表类型(List):包含 metadata 字段与Items切片:

// +kubebuilder:object:root=true type GatewayParametersList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` Items []GatewayParameters `json:"items"` }

此外,从TrafficPolicy(见 traffic_policy_types.go)可以看到更多可用的资源级注解,例如+kubebuilder:printcolumn:name="Accepted",type=string,JSONPath=".status.ancestors[*].conditions[?(@.type=='Accepted')].status"用于在kubectl get时输出可读状态列,以及+kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=Direct"用于标注策略的挂载语义。

步骤 3:运行 codegen 生成全套产物

README 要求执行make generated-code -B,这将调用 hack/generate.go 指定的controller-gen命令。hack/generate.go 本身是一个go:generate模板占位文件(panic("this file is a go:generate template...")),真正的生成逻辑在其同级脚本 hack/generate.sh 中,关键流程包括:

  1. go tool register-gen --output-file zz_generated.register.go:生成类型注册文件,随后通过 sed 将版本占位符替换为v1alpha1;
  2. go tool controller-gen crd:maxDescLen=50000 object rbac:roleName=kgateway paths=".../api/v1alpha1/kgateway" paths=".../api/v1alpha1/shared":同时生成 deepcopy、CRD 与 RBAC;
  3. go tool client-gen --clientset-name "versioned" ... --plural-exceptions "GatewayParameters:GatewayParameters":生成 versioned clientset 到 pkg/client,注意对GatewayParameters做了复数例外处理;
  4. 最后对生成的 CRD 做若干后处理(如将 ClusterRole 名字模板化为kgateway-{{ .Release.Namespace }}、用 Helm 字符串字面量转义 CRD 描述中的{{ ... }}模板语法,避免helm lint失败)。

codegen 完成后会产出以下几类文件:

产物输出位置
zz_generated.deepcopy.go与 Go 类型同目录(如 api/v1alpha1/kgateway/zz_generated.deepcopy.go)
zz_generated.register.go与 Go 类型同目录(如 api/v1alpha1/kgateway/zz_generated.register.go)
CRD YAMLCRD Helm chart 模板目录 install/helm/kgateway-crds/templates,当前已包含 7 个 CRD:gateway.kgateway.dev_backendconfigpolicies.yaml、_backends.yaml、_directresponses.yaml、_gatewayextensions.yaml、_gatewayparameters.yaml、_listenerpolicies.yaml、_trafficpolicies.yaml
RBAC Roleinstall/helm/kgateway/templates/role.yaml
kube clientspkg/client(versioned clientset)

README 还提到 codegen 会更新api/applyconfiguration、pkg/generated与pkg/client目录。从当前仓库快照看,pkg/client/clientset/versioned 已包含clientset.go、fake/、scheme/、typed/,而api/applyconfiguration与pkg/generated目录尚未生成;结合 hack/generate.sh 的实际调用序列可以推断,当前主流程实际执行的生成器是register-gen、controller-gen与client-gen。生成的 clientset 主要用于插件初始化(plugin initialization),其中的 fake client 用于测试。

步骤 4:向 apiclient 注册 CRD

在 pkg/apiclient/types.go 中注册 CRD,使控制面客户端能够对新增资源执行 List / Watch / Write。该文件通过RegisterTypes()(内部用sync.Once保证只注册一次)调用kubeclient.Register,为每个资源注册 GVR、GVK 及三个闭包函数(List、Watch、WriteAPI)。例如对GatewayParameters的注册:

kubeclient.Register( wellknown.GatewayParametersGVR, wellknown.GatewayParametersGVK, func(c kubeclient.ClientGetter, namespace string, o metav1.ListOptions) (runtime.Object, error) { return c.(Client).Kgateway().GatewayKgateway().GatewayParameters(namespace).List(context.Background(), o) }, func(c kubeclient.ClientGetter, namespace string, o metav1.ListOptions) (watch.Interface, error) { return c.(Client).Kgateway().GatewayKgateway().GatewayParameters(namespace).Watch(context.Background(), o) }, func(c kubeclient.ClientGetter, namespace string) kubetypes.WriteAPI[*kgateway.GatewayParameters] { return c.(Client).Kgateway().GatewayKgateway().GatewayParameters(namespace) }, )

types.go 中已注册的资源还包括TCPRoute、TLSRoute、Backend、BackendConfigPolicy、DirectResponse、ListenerPolicy、TrafficPolicy、GatewayExtension。新增资源时,GVR/GVK 常量定义在 pkg/kgateway/wellknown/constants.go 等 wellknown 文件中。

步骤 5:为测试注册 CRD(两处)

新增资源必须同时注册到两个测试位置:

  1. filterObjects函数(pkg/apiclient/fake/fake.go):按对象类型将测试输入分为 kgateway 对象与 Istio 对象两组,当前分支覆盖*kgateway.Backend、*kgateway.BackendConfigPolicy、*kgateway.DirectResponse、*kgateway.GatewayExtension、*kgateway.GatewayParameters、*kgateway.ListenerPolicy、*kgateway.TrafficPolicy。新增资源需在此 switch 中追加对应类型,否则测试对象会被误判为 Istio 类型。

  2. AllCRDs列表(test/testutils/crd.go):集中声明测试环境需要安装的全部 CRD 的 GroupVersionResource,分为 Gateway API、K8s API、Istio API、kgateway API 四组。kgateway 组当前包括wellknown.BackendGVR、BackendConfigPolicyGVR、TrafficPolicyGVR、ListenerPolicyGVR、DirectResponseGVR、GatewayExtensionGVR、GatewayParametersGVR,新增资源的 GVR 需追加到此处,测试环境才会安装对应的 CRD。此外该文件还提供GetStructuralSchemasForAllCharts、ApplyDefaults等工具,用于从 install/helm/kgateway-crds/templates 加载 CRD 的结构化 schema 并执行默认值填充与未知字段裁剪,模拟 API Server 行为。

API 编写指南:字段注解与类型规范

api/README.md 的 "API guidelines" 部分是所有_types.go文件必须遵守的约定,逐条展开如下(均可在 gateway_parameters_types.go 中找到对应的源码示例)。

文档与注解要求

所有字段都应包含文档注释以及合适的 json、kubebuilder 注解;如果字段存在默认值,必须文档化该默认值。例如EnvoyContainer.Image的注释明确写出了可单独覆盖的默认值(registry: quay.io/solo-io、repository: envoy-wrapper、tag: <kgateway version>、pullPolicy: IfNotPresent)。

可选字段(optional)的写法

  • 使用+optional标记;
  • 使用omitemptyjson 标签;
  • 使用指针类型(如*string),除非类型本身有 nil 零值(如切片、map)。

唯一的例外是:当字段带默认值(+kubebuilder:default=...)时,允许使用非指针类型。典型例子是 retries.go 中的Attempts字段:

// +kubebuilder:default=1 // +kubebuilder:validation:Minimum=0 Attempts int32 `json:"attempts,omitempty"`

它声明默认重试次数为 1,因此可以不使用指针。

必填字段(required)的写法

  • 使用+required标记;
  • 禁止设置omitemptyjson 标签。

例如GatewayParameters.Spec(Spec GatewayParametersSpec json:"spec")与TrafficPolicy.Spec都是必填且未加omitempty。

避免指针切片

优先使用[]string而不是[]*string,避免切片元素为指针。该建议来自上游 kubernetes/code-generator 的已知问题(issue #166)。仓库中大量字段遵守了这一约定,如EnvoyContainer.ExtraArgs []string、Retry.RetryOn []RetryOnCondition。

时长字段使用 metav1.Duration

时间时长字段统一使用metav1.Duration类型,并配合 CEL 校验规则限制取值范围。例如 shared/timeouts.go 中的Timeouts.Request:

// +kubebuilder:validation:Type=string // +kubebuilder:validation:MaxLength=32 // +kubebuilder:validation:XValidation:rule="matches(self, '^([0-9]{1,5}(h|m|s|ms)){1,4}$')",message="invalid duration value" Request *metav1.Duration `json:"request,omitempty"`

retries.go中的PerTryTimeout与BackoffBaseInterval还额外声明了duration(self) >= duration('1ms')等下限约束。TrafficPolicySpec上甚至有一条跨字段 CEL 规则(traffic_policy_types.go):retry.perTryTimeout must be less than timeouts.request,强制单次重试超时小于整体请求超时。

跨字段约束优先用 AtLeastOneOf / ExactlyOneOf

当约束跨越一组字段时,使用+kubebuilder:validation:AtLeastOneOf或+kubebuilder:validation:ExactlyOneOf,而不是手写 CEL。仓库中的实例包括:

  • gateway_parameters_types.go 的GatewayParametersSpec:+kubebuilder:validation:ExactlyOneOf=kube;selfManaged,即kube与selfManaged只能二选一;
  • gateway_parameters_types.go 的LogFormat:+kubebuilder:validation:ExactlyOneOf=json;text;
  • shared_types.go 的HTTPHeader:+kubebuilder:validation:ExactlyOneOf=value;secretRef;
  • shared_types.go 的StringMatcher:+kubebuilder:validation:ExactlyOneOf=exact;prefix;suffix;contains;safeRegex;
  • shared_types.go 的HTTPHeaderFilter:+kubebuilder:validation:AtLeastOneOf=set;add;remove;
  • traffic_policy_types.go 的APIKeySource:+kubebuilder:validation:AtLeastOneOf=header;query;cookie。

在 TrafficPolicy API 中复刻 Gateway API 策略

为了让策略能够在配置层级的不同位置(Gateway、Gateway 的 listener、route 级别)挂载,kgateway 会把部分 Gateway API 策略复刻进 TrafficPolicy API。README 给出了三条决策准则,下面逐一结合源码说明。

情形一:Gateway API 类型足够时,直接嵌入

当 Gateway API 的现有类型足以满足需求时,直接将其嵌入 TrafficPolicy API。TrafficPolicy的cors是典型例子——直接内嵌了 Gateway API 的HTTPCORSFilter类型。源码见 traffic_policy_types.go:

type CorsPolicy struct { // +kubebuilder:pruning:PreserveUnknownFields *gwv1.HTTPCORSFilter `json:",inline"` // Disable the CORS filter. // Can be used to disable CORS policies applied at a higher level in the config hierarchy. // +optional Disable *shared.PolicyDisable `json:"disable,omitempty"` }

值得注意的是,kgateway 在嵌入的同时扩展了一个disable字段,用于关闭配置层级更高处应用的 CORS 策略——这正体现了 TrafficPolicy 作为聚合策略层的价值。

情形二:注意<gateway:experimental>标记

嵌入 Gateway API 类型前,必须判断该类型是否被标记为<gateway:experimental>。实验性类型可能引入破坏性变更,因此:

  • 不鼓励在 TrafficPolicy API 中嵌入实验性类型;
  • 如果 Gateway API 类型发生了破坏性变更,推荐把变更前的旧版本类型复刻进 TrafficPolicy API,而不是把破坏性变更传播到 TrafficPolicy API。

这样既隔离了上游实验性 API 的波动,又保证了策略 API 的向后兼容。

情形三:Gateway API 类型不充分时,新建自定义类型

当 Gateway API 类型不足以表达更高级的需求时,应在 TrafficPolicy API 中新建类型,而不是嵌入 Gateway API 类型。TrafficPolicy的retry与timeouts就是典型例子——它们分别定义了新类型,而非复用 Gateway API 的HTTPRouteRetry与HTTPRouteTimeouts。

retry使用的Retry类型定义在独立的 api/v1alpha1/kgateway/retries.go,提供的能力远超 Gateway API 原生重试类型:

  • RetryOn []RetryOnCondition:带枚举校验的重试条件,取值包括5xx、gateway-error、reset、connect-failure、envoy-ratelimited、retriable-4xx、refused-stream、retriable-status-codes等;
  • Attempts int32:默认 1,为 0 时禁用重试;
  • PerTryTimeout *metav1.Duration:单次重试超时(含首次尝试),须小于全局路由超时;
  • StatusCodes []gwv1.HTTPRouteRetryStatusCode:额外可重试的 400-599 状态码;
  • BackoffBaseInterval *metav1.Duration:带完全抖动的指数退避基准间隔,默认 25ms,退避区间为[0, (2^N-1)*B]且封顶为基准的 10 倍。

timeouts使用的Timeouts类型定义在 api/v1alpha1/shared/timeouts.go,包含Request(从网关到后端的单请求整体超时,0 表示禁用)与StreamIdle(空闲流超时)两个字段,都通过 CEL 规则校验时长格式与上限。

通过这两种模式,TrafficPolicy 既与 Gateway API 生态保持了类型层面的兼容与复用,又为网关特有能力(如分级挂载、禁用覆盖、更细粒度的重试/超时控制)保留了独立的演进空间。

生成代码的日常维护与验证

在 CI 与日常开发中,codegen 的维护方式参见 devel/contributing/code-generation.md 与 Makefile:

  • make generated-code -B:强制重新生成全部代码(clean-gen clean-stamps后无条件执行),对应 README 中的步骤 3;
  • make generate-all:基于 stamp 文件的增量生成目标,只在源文件变化时重新生成,速度更快;
  • make verify:运行生成并检查是否有文件变更,适合提交 PR 前验证"是否忘记提交生成的代码";
  • make go-generate-apis:只运行仓库中所有go generate指令(API 变更时使用);
  • make go-generate-mocks:只运行 mockgen 指令(接口 API 变更时使用)。

一个实用的工作流是:修改_types.go→ 执行make generated-code -B→ 检查 diff 中是否包含新增的zz_generated.deepcopy.go/zz_generated.register.go/ CRD YAML / role.yaml / clientset 变更 → 在 pkg/apiclient/types.go、pkg/apiclient/fake/fake.go 与 test/testutils/crd.go 三处完成注册 → 运行相关单测验证。

实战示例:从 YAML 反推 GatewayParameters 的字段体系

代码生成的 CRD 最终由用户以 YAML 形式消费。以 examples/example-gatewayparameters-stats-matcher.yaml 为例,可以直观看到GatewayParameters的字段如何对应到类型定义:spec.kube.stats.enabled、routePrefixRewrite、enableStatsRoute、statsRoutePrefixRewrite对应 gateway_parameters_types.go 的StatsConfig;matcher.inclusionList(支持prefix、suffix、safeRegex匹配)对应StatsMatcher与 shared_types.go 的StringMatcher。spec.kube下的deployment、envoyContainer、sdsContainer、podTemplate、service、serviceAccount、istio、stats以及deploymentOverlay、serviceOverlay、podDisruptionBudget、horizontalPodAutoscaler、verticalPodAutoscaler等 overlay 字段(见 GatewayParametersOverlays),共同勾勒出 kgateway 数据面动态供给的完整配置面。

总结

在 kgateway 中新增 API / CRD 是一条清晰、高度自动化的流水线:创建版本目录与doc.go→ 按 API 指南编写_types.go→ 运行make generated-code -B生成 deepcopy、注册表、CRD、RBAC 与 clientset → 在 pkg/apiclient/types.go 注册客户端 → 在 pkg/apiclient/fake/fake.go 与 test/testutils/crd.go 注册测试用 CRD。与此同时,遵循字段注解规范(optional/required、指针、Duration、AtLeastOneOf/ExactlyOneOf)并按照"直接嵌入、谨慎对待实验类型、按需新建类型"三种模式与 Gateway API 生态协同,就能保证新增 API 既规范又具备策略层级挂载能力。本文所述步骤与示例文件均可在仓库中直接查阅,可作为后续扩展 kgateway API 的实操参考。

  • API网关
  • 云原生
  • 微服务

【免费下载链接】kgateway

The Cloud-Native API Gateway and AI Gateway

项目地址:https://gitcode.com/gh_mirrors/kg/kgateway
点击查看免费下载
上一篇:Theo CLI工具完全指南:命令行操作设计令牌的10个技巧
下一篇:5个高级技巧:彻底掌握BililiveRecorder的隐藏功能

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询