- 开发者工具
- 代码生成
- CLI
- 云原生
- 后端
【免费下载链接】kubebuilder
Kubebuilder - SDK for building Kubernetes APIs using CRDs
本篇技术指南系统讲解 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 -hhhKubebuilder 生成的 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
相关推荐
SymPy 拉普拉斯变换:从“暴力积分”到规则表驱动引擎的设计与实现解析
SymPy 拉普拉斯变换:从“暴力积分”到规则表驱动引擎的设计与实现解析 本文以 SymPy 官方设计文档《Laplace Transform: Design
开发者工具代码生成CLI云原生后端Kubebuilder Markers标记大全:8大类注解驱动CRD与RBAC代码生成速查
Kubebuilder Markers标记大全:8大类注解驱动CRD与RBAC代码生成速查 Kubebuilder 是构建 Kubernetes API(CRD
开发者工具代码生成CLI云原生后端Kubebuilder CRD 校验标记(Validation Markers)完全指南:用 OpenAPI v3 Schema 声明式约束你的自定义资源
Kubebuilder CRD 校验标记(Validation Markers)完全指南:用 OpenAPI v3 Schema 声明式约束你的自定义资源 本文
开发者工具代码生成CLI云原生后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考