4. API 定义与 CRD 生成:定义自定义资源(CR)结构体、使用 markers 标签、生成 CRD YAML
在 Operator 开发中,自定义资源(CR)是核心数据模型,它定义了用户期望的集群状态。本章将深入讲解如何使用 Go 结构体定义 CR、通过 markers 标签控制生成行为,并最终生成 CRD(CustomResourceDefinition)YAML 文件。
4.1 自定义资源结构体定义
Operator SDK 使用 Kubernetes 风格的 API 结构体来定义 CR。每个 CR 通常包含三个核心部分:TypeMeta、ObjectMeta 和 Spec/Status。
核心结构体组成:
- TypeMeta:API 版本和资源类型(由 SDK 自动填充)
- ObjectMeta:名称、命名空间、标签等元数据(Kubernetes 标准)
- Spec:用户期望的“期望状态”(Desired State)
- Status:集群当前的“实际状态”(Actual State),由 Operator 更新
以下是一个典型的 CR 结构体定义示例(位于 api/v1/ 目录下):
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MyAppSpec 定义用户期望的应用配置
type MyAppSpec struct {
// +kubebuilder:validation:Required
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=10
Replicas int32 `json:"replicas"`
// +kubebuilder:validation:Required
Image string `json:"image"`
// +kubebuilder:validation:Optional
// +kubebuilder:default="default-config"
ConfigName string `json:"configName,omitempty"`
}
// MyAppStatus 定义应用当前状态
type MyAppStatus struct {
// +kubebuilder:validation:Optional
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
// +kubebuilder:validation:Optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=myapps,scope=Namespaced
// +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.replicas"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// MyApp 是 CR 的主结构体
type MyApp struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MyAppSpec `json:"spec,omitempty"`
Status MyAppStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// MyAppList 包含多个 MyApp 实例
type MyAppList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MyApp `json:"items"`
}
4.2 Markers 标签详解
Markers 是 Operator SDK 中用于控制代码生成、CRD 验证、打印列等行为的注释标签。它们以 +kubebuilder: 或 +k8s: 开头,直接写在结构体或字段上方。
| Marker 类别 | 常用标签 | 作用说明 |
|---|---|---|
| 对象标记 | +kubebuilder:object:root=true |
标记该结构体是 CR 的根对象,用于生成 DeepCopy 和客户端代码 |
| 子资源标记 | +kubebuilder:subresource:status |
启用 Status 子资源,允许通过 /status 端点更新状态 |
| 资源元数据 | +kubebuilder:resource:path=myapps,scope=Namespaced |
定义 CR 的 REST 路径和作用域(Namespaced/Cluster) |
| 打印列 | +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.replicas" |
在 kubectl get 输出中显示额外列 |
| 字段验证 | +kubebuilder:validation:Required |
标记字段为必填项 |
| 字段约束 | +kubebuilder:validation:Minimum=1 |
设置数值字段的最小值/最大值/正则等约束 |
| 默认值 | +kubebuilder:default="default-config" |
为字段设置默认值(用户未指定时生效) |
| DeepCopy 生成 | +k8s:deepcopy-gen:interfaces=... |
控制 DeepCopy 方法的生成范围 |
最佳实践:建议为每个
Spec 字段都添加 +kubebuilder:validation:Required 或 +kubebuilder:validation:Optional 标记,避免生成 CRD 时出现歧义。
4.3 CRD YAML 生成流程
定义好结构体和 markers 后,通过以下命令生成 CRD YAML:
# 生成所有 CRD YAML 文件(默认输出到 config/crd/bases/)
make generate
# 仅生成 CRD 清单(不包含其他资源)
make manifests
# 查看生成的 CRD 文件
ls config/crd/bases/
# 输出示例:example.com_myapps.yaml
生成的 CRD YAML 文件结构如下:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
name: myapps.example.com
spec:
group: example.com
names:
kind: MyApp
listKind: MyAppList
plural: myapps
singular: myapp
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: MyApp 是 CR 的主结构体
properties:
spec:
properties:
replicas:
format: int32
minimum: 1
maximum: 10
type: integer
image:
type: string
configName:
default: default-config
type: string
required:
- replicas
- image
type: object
status:
properties:
availableReplicas:
format: int32
type: integer
conditions:
items:
type: object
x-kubernetes-preserve-unknown-fields: true
type: array
type: object
type: object
subresources:
status: {}
additionalPrinterColumns:
- jsonPath: .spec.replicas
name: Replicas
type: integer
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
注意:如果修改了结构体或 markers,必须重新运行
make generate 和 make manifests,否则 CRD 不会更新。建议将这两个命令加入 CI/CD 流程。
4.4 CRD 生成流程示意图
下图展示了从 Go 结构体到 CRD YAML 的完整生成链路:
4.5 验证 CRD 与测试 CR 实例
生成 CRD 后,可以通过以下步骤验证:
- 安装 CRD 到集群:
make install或kubectl apply -f config/crd/bases/ - 查看已安装的 CRD:
kubectl get crd | grep myapps - 创建 CR 实例:编写 YAML 文件并
kubectl apply -f config/samples/ - 验证 CR 状态:
kubectl get myapps -o yaml
示例 CR 实例 YAML(config/samples/example_v1_myapp.yaml):
apiVersion: example.com/v1
kind: MyApp
metadata:
name: myapp-sample
spec:
replicas: 3
image: nginx:1.25
configName: prod-config
调试技巧:如果 CRD 安装失败,使用
kubectl explain myapp 查看 CRD 的字段描述,确保与结构体定义一致。也可以使用 kubectl get crd myapps.example.com -o yaml 查看完整的 CRD 定义。
4.6 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| CRD 生成后字段缺失 | 结构体字段缺少 json 标签或 markers |
检查每个字段是否都有 json:"fieldName" 标签 |
| 验证规则不生效 | markers 拼写错误或位置不对 | 确认 markers 写在字段上方,且使用正确的 +kubebuilder:validation: 前缀 |
| 打印列不显示 | printcolumn 标记未正确配置 |
检查 JSONPath 是否正确,确保字段在 Spec 或 Status 中存在 |
| Status 子资源无法更新 | 缺少 +kubebuilder:subresource:status 标记 |
在 CR 主结构体上方添加该标记 |
| DeepCopy 方法缺失 | 未运行 make generate 或标记错误 |
运行 make generate,检查 zz_generated.deepcopy.go 文件 |
重要:CRD 一旦被创建并用于生产环境,修改字段类型或删除字段可能导致现有 CR 实例无法解析。建议在开发阶段充分测试,或使用 CRD 版本升级机制(Conversion Webhook)进行平滑迁移。