☰
Kubebuilder CRD 生成标记(Markers)完整指南:从 Go 类型到 CustomResourceDefinition
2026/9/25 15:55:47 网站建设 项目流程
  • 开发者工具
  • 代码生成
  • CLI
  • 云原生
  • 后端

【免费下载链接】kubebuilder

Kubebuilder - SDK for building Kubernetes APIs using CRDs

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

本篇技术指南系统讲解 Kubebuilder 项目中如何通过// +kubebuilder:xxx形式的 marker 注释,从 Go 类型与包中构造 CustomResourceDefinition(CRD)的原理与实战用法。文中内容基于本仓库docs/book/src/reference/markers/crd.md所对应的 CRD 生成标记体系,并结合controller-gen的实际调用链与仓库内真实项目(testdata/project-v4)的生成产物展开。读完本文,你将掌握 CRD 结构标记、验证标记、打印列、子资源、多版本存储等完整配置技能,并能自行解读make manifests生成的 CRD YAML。

CRD 标记是什么:从 Go 类型构造 CRD 的核心机制

在 Kubebuilder 项目中,CRD 并非手写 YAML,而是由controller-gen工具从 Go 类型自动生成。controller-gen通过解析源码中特殊的"标记注释"(以// +开头的注释行)来获知每个字段、类型和包的附加信息,进而构造出 OpenAPI v3 结构的验证 schema 与 CRD 的各个部分。

本仓库文档 crd.md 开篇即点明这些标记的核心定位:

These markers describe how to construct a custom resource definition from a series of Go types and packages.

也就是说,CRD 生成标记(CRD Generation markers)负责描述"如何从一系列 Go 类型和包构造 CRD",而实际验证 schema 的生成则由 验证标记(validation markers) 负责。两者分工明确:前者决定 CRD 的结构形态(资源路径、作用域、版本、打印列、子资源等),后者决定字段级的校验约束。

标记文档的生成机制:模板注入而非静态书写

值得注意的是,crd.md正文中{{#markerdocs CRD}}是一个模板占位符,其实际内容由仓库中的文档生成工具注入。在 markerdocs/main.go 中可以看到,MarkerDocs插件会执行controller-gen并解析其-wwww输出 JSON,从中提取每个 marker 的名称、参数类型与帮助说明,再替换到{{#markerdocs <category>}}位置(参见 main.go 中的 getMarkerDocs 与 Process 实现)。具体运行时通过 markerdocs.sh 构建并执行。因此,CRD 标记的"官方参考"本质上是 controller-gen 输出的实时帮助信息,与所安装的 controller-tools 版本严格对应。

标记的基本语法

在深入各类 CRD 标记之前,先掌握 marker 的三种形态(详见 markers.md):

  • 空标记(Empty):如同命令行布尔开关,写上即启用行为,例如// +kubebuilder:validation:Optional、// +kubebuilder:subresource:status。
  • 匿名标记(Anonymous):接受单个值作为参数,例如// +kubebuilder:validation:MaxItems=2。
  • 多选项标记(Multi-option):接受一个或多个具名参数,第一个参数与名字之间用冒号分隔,后续参数以逗号分隔,参数顺序无关紧要,部分参数可省略,例如:
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Replicas,type=string

标记参数支持字符串、整数、布尔、切片与映射。字符串、整数、布尔遵循 Go 语法;简单单字符串可省略引号(如// +kubebuilder:validation:Type=string),复杂字符串建议加引号;切片可用花括号逗号分隔,或简单场景下用分号分隔(如// +kubebuilder:validation:Enum=Wallace;Gromit;Chicken);映射使用{key: value, ...}形式(如// +kubebuilder:default={magic: {numero: 42, stringified: forty-two}})。

运行入口:make manifests 与 controller-gen

Kubebuilder 通过make manifests目标驱动controller-gen生成 CRD。以仓库中的真实样例项目为例,testdata/project-v4/Makefile 中的目标为:

manifests: controller-gen "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./..." output:crd:artifacts:config=config/crd/bases

关键点拆解:

  • crd是 controller-gen 的一个 generator(生成器),负责读取_types.go中的标记并产出 CRD YAML;
  • paths="./..."指定扫描全部 Go 包;
  • output:crd:artifacts:config=config/crd/bases是输出规则(output rule),把 CRD 相关的非代码产物输出到config/crd/bases目录,而不是默认的config/crd;
  • 同一命令中还一并生成 RBAC(rbac:roleName=manager-role)与 Webhook 配置(webhook)。

controller-gen的每个生成器都由与 marker 相同语法的选项控制,也支持不同的输出规则。如果想查看全部生成器与选项,可运行:

controller-gen -h # 更详细的信息: controller-gen -hhh

Kubebuilder 生成的 Makefile 会在本地bin/目录(LOCALBIN)按需安装 controller-gen(本仓库样例固定版本为CONTROLLER_TOOLS_VERSION ?= v0.22.0,见 testdata/project-v4/Makefile),无需手动全局安装。生成产物位于config/crd/bases,且会在 CRD 注解中记录controller-gen.kubebuilder.io/version,便于追溯生成工具版本(可在 testdata/project-v4/config/crd/bases/crew.testproject.org_admirales.yaml 中看到controller-gen.kubebuilder.io/version: v0.22.0)。

资源结构标记:resource

+kubebuilder:resource标记用于控制 CRD 在 Kubernetes 中的资源形态,包括path(资源复数路径)与scope(Namespaced 或 Cluster)。例如仓库测试数据 admiral_types.go 中的用法:

// +kubebuilder:object:root=true // +kubebuilder:ac:generate=false // +kubebuilder:subresource:status // +kubebuilder:resource:path=admirales,scope=Cluster // Admiral is the Schema for the admirales API type Admiral struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitzero"` Spec AdmiralSpec `json:"spec"` Status AdmiralStatus `json:"status,omitzero"` }

这里path=admirales声明了 CRD 的 resource 名(对应 YAML 中的plural与names: plural字段),scope=Cluster表示集群级资源。与之对照,navigator_types.go 中则使用// +kubebuilder:resource:path=navigators且未指定 scope,即默认的 Namespaced。若省略path,controller-gen 会根据类型名自动推导复数形式。生成的 YAML 中可看到对应的scope与names字段。

验证标记:validation

CRD 支持使用 OpenAPI v3 schema 进行声明式校验,校验规则通过 验证标记 附加到字段或类型上。每个验证标记大致对应一个 OpenAPI/JSON schema 选项。对于复杂校验、需复用校验或需校验切片元素时,最推荐的做法是定义一个新类型来承载校验(参见 generating-crd.md)。

一个综合示例:

type ToySpec struct { // +kubebuilder:validation:MaxLength=15 // +kubebuilder:validation:MinLength=1 Name string `json:"name,omitempty"` // +kubebuilder:validation:MaxItems=500 // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:UniqueItems=true Knights []string `json:"knights,omitempty"` Alias Alias `json:"alias,omitempty"` Rank Rank `json:"rank"` } // +kubebuilder:validation:Enum=Lion;Wolf;Dragon type Alias string // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=3 // +kubebuilder:validation:ExclusiveMaximum=false type Rank int32

要点说明:

  • 字段级验证直接写在字段上方:MaxLength/MinLength约束字符串长度,MaxItems/MinItems约束切片元素数量,UniqueItems要求切片元素唯一;
  • 类型级验证写在类型定义上方:Enum限定合法取值集合(多个取值用分号分隔),Minimum/Maximum/ExclusiveMaximum限定数值范围;
  • 通过为自定义类型附加标记,同一套校验可被多个字段复用,也能对切片元素生效;
  • 自定义资源实际由 API server 依据生成的 OpenAPI v3 schema 进行校验,且必须符合 Kubernetes 结构式 schema(structural schema)规则。数值类型应尽量映射到 OpenAPI 支持良好的 Go 类型(如int32、int64),需要十进制类表示时优先使用resource.Quantity。

关于+kubebuilder:validation:Optional与// +optional的差异(见 markers.md 的说明):两者都能作用于字段,但+kubebuilder:validation:Optional还可用于包级别,使包内所有字段生效;若只为 controller-gen 服务二者等价,但若要兼容其他生成器或让开发者自行构建客户端,建议同时保留+optional。在 1.x 中,获取+optional最可靠的方式是使用omitempty。

附加打印列:printcolumn

自 Kubernetes 1.11 起,kubectl get可向服务端查询要显示的列。CRD 通过additionalPrinterColumns字段控制kubectl get的输出,而该字段由 Go 类型上的+kubebuilder:printcolumn标记控制。

延续上面的示例,为 Toy 类型添加打印列:

// +kubebuilder:printcolumn:name="Alias",type=string,JSONPath=`.spec.alias` // +kubebuilder:printcolumn:name="Rank",type=integer,JSONPath=`.spec.rank` // +kubebuilder:printcolumn:name="Bravely Run Away",type=boolean,JSONPath=`.spec.knights[?(@ == "Sir Robin")]`,description="when danger rears its ugly head, he bravely turned his tail and fled",priority=10 // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type Toy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec ToySpec `json:"spec,omitempty"` Status ToyStatus `json:"status,omitempty"` }

参数解析:

  • name:列显示名称;
  • type:列数据类型,支持string、integer、boolean、date等;
  • JSONPath:从资源中提取列值的 JSONPath 表达式,如.spec.alias、.metadata.creationTimestamp;
  • description:列的描述说明(可选);
  • priority:列的显示优先级,非零值列默认在-o wide输出中才显示(可选)。

这些标记最终会映射为生成 CRD YAML 中的additionalPrinterColumns数组。

子资源:subresource

自 Kubernetes 1.13 起,CRD 可以选择实现/status与/scale子资源。官方强烈建议所有带 status 字段的资源都启用/status子资源。两者都有对应的标记(详见 generating-crd.md 的 Subresources 一节)。

status 子资源

通过// +kubebuilder:subresource:status启用。启用后,对主资源的更新不会改变 status;对 status 子资源的更新也只能修改 status 字段,二者隔离,避免控制器与其他写入方互相覆盖。示例:

// +kubebuilder:subresource:status type Toy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec ToySpec `json:"spec,omitempty"` Status ToyStatus `json:"status,omitempty"` }

在仓库测试数据 captain_types.go 中同样可见// +kubebuilder:subresource:status的标准用法。

scale 子资源

通过// +kubebuilder:subresource:scale启用,需要三个参数:

  • specpath:指向 spec 中副本数字段的 JSONPath,如.spec.replicas;
  • statuspath:指向 status 中副本数字段的 JSONPath,如.status.replicas;
  • selectorpath(可选):指向标签选择器字符串形式字段的 JSONPath,如.status.selector。若该字段是标签选择器的字符串形式,HorizontalPodAutoscaler 便可据此自动扩缩该资源。

示例:

type CustomSetSpec struct { Replicas *int32 `json:"replicas"` } type CustomSetStatus struct { Replicas int32 `json:"replicas"` Selector string `json:"selector"` // 必须是选择器的字符串形式 } // +kubebuilder:subresource:status // +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector type CustomSet struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec CustomSetSpec `json:"spec,omitempty"` Status CustomSetStatus `json:"status,omitempty"` }

生成的 CRD YAML 中对应subresources小节。仓库生成产物 crew.testproject.org_admirales.yaml 中可看到storage: true与subresources:并存的形态。

多版本与存储版本:storageversion

自 Kubernetes 1.13 起,CRD 可以定义 Kind 的多个版本,并通过 Webhook 在版本间转换。多版本 CRD 的完整流程参见 多版本教程。

默认情况下,Kubebuilder 出于对旧版 Kubernetes 的兼容,会为 CRD 关闭"不同版本不同验证"的能力。若要启用,需要修改 Makefile 中的CRD_OPTIONS:

  • 使用 v1beta CRD 时,从CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false改为CRD_OPTIONS ?= crd:preserveUnknownFields=false;
  • 使用 v1(推荐)时,改为CRD_OPTIONS ?= crd。

随后使用+kubebuilder:storageversion标记指明应由 API server 用于存储数据的 Group-Version-Kind(GVK)。每个 Kind 有且只能有一个版本被标记为存储版本。

仓库测试数据中即有典型范例:firstmate_types.go v1 带有// +kubebuilder:storageversion,而 v2 版本 未加该标记。对照生成的 CRD YAML crew.testproject.org_firstmates.yaml 与 #L234,可看到 v1 版本storage: true、v2 版本storage: false。多版本间的转换函数则由+kubebuilder:conversion相关标记与手动编写的转换 Webhook 配合完成(仓库 firstmate_conversion.go 与 v2 版 即为转换实现样例)。

常见组合模式:一个完整 CRD 类型的标记布局

综合以上内容,一个生产级 CRD 类型通常按如下模式组织标记(自上而下依次为:包级/列表、类型级结构、子资源、资源形态、存储版本、打印列、字段验证):

// +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=toys,scope=Namespaced // +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type Toy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec ToySpec `json:"spec,omitempty"` Status ToyStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true // ToyList contains a list of Toy type ToyList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` Items []Toy `json:"items"` }
  • +kubebuilder:object:root=true与对应的ToyList配合,使类型可作为runtime.Object注册进 scheme,同时驱动 DeepCopy 代码生成;
  • 修改_types.go后需重新运行make manifests(重新生成 CRD)与make generate(重新生成 DeepCopy 等代码),文件头部的注释"Important: Run "make" to regenerate code after modifying this file"即为提醒(可见于 admiral_types.go)。

验证与排错

  • 生成后检查产物:CRD 位于config/crd/bases下,命名形如<group>_<resource>.yaml;可通过make install安装到集群(内部先kustomize build config/crd再kubectl apply,见 testdata/project-v4/Makefile)。
  • 查看 controller-gen 全部选项:运行controller-gen -h(概要)或controller-gen -hhh(详细信息)。
  • 直接调用 controller-gen:若不想走 Makefile,可手动执行$(CONTROLLER_GEN) crd paths="./..." output:crd:artifacts:config=config/crd/bases观察其行为(参见 generating-crd.md 的 Under the hood 一节)。
  • 标记文档的动态性:由于 CRD 标记参考由 controller-gen 实时输出注入,若你安装的 controller-tools 版本与仓库样例不同,个别标记的可用性可能略有差异,请以controller-gen crd -www的输出为准。

延伸阅读

  • CRD 验证标记:字段与类型的 OpenAPI v3 校验约束全集;
  • 生成 CRD 全流程:validation、printer columns、subresources、多版本与底层机制的系统讲解;
  • Markers 总览:marker 语法、make manifests/make generate分工与+optional细节;
  • 多版本转换教程:hub-spoke 转换模型与 Webhook 落地;
  • 控制器生成器参考:controller-gen 工具本身的用法;
  • 真实样例:project-v4 测试项目 与 project-v4-multigroup 测试项目,其中包含 CRD 标记、转换函数与多版本组织的一手素材。
  • 开发者工具
  • 代码生成
  • CLI
  • 云原生
  • 后端

【免费下载链接】kubebuilder

Kubebuilder - SDK for building Kubernetes APIs using CRDs

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

相关推荐

上一篇:Rainmeter 音乐可视化器快速指南:Monstercat Visualizer 让桌面随音乐跳动
下一篇:WSABuilds 更新到 GApps 版后 Play Store 提示无法连接 play.google.com 怎么登录?

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

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

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

立即咨询