4、第一个CRD定义:创建API类型、定义Spec与Status字段、生成CRD YAML
在Operator开发中,CRD(Custom Resource Definition)是自定义资源的蓝图。本章将引导你使用Operator SDK创建第一个CRD,从初始化API类型到定义Spec/Status字段,最终生成可部署的CRD YAML清单。这是整个Operator开发的基础步骤,决定了你的自定义资源能描述什么样的业务对象。
4.1 使用Operator SDK创建API类型
Operator SDK提供了create api命令,用于快速生成CRD的Go类型骨架和对应的控制器框架。执行以下命令前,请确保已在项目根目录下初始化了Operator项目(operator-sdk init)。
# 创建API类型,指定组(group)、版本(version)和资源种类(kind)
operator-sdk create api \
--group=example \
--version=v1 \
--kind=AppService \
--resource=true \
--controller=true
参数说明:
| 参数 | 含义 | 示例值 |
|---|---|---|
--group |
API组,用于资源分组 | example |
--version |
API版本,支持v1、v1beta1等 | v1 |
--kind |
资源种类名称,首字母大写 | AppService |
--resource |
是否生成CRD资源类型 | true |
--controller |
是否生成控制器 | true |
执行成功后,项目结构会新增以下关键文件:
- api/v1/appservice_types.go:自定义资源的Go结构体定义,包含Spec和Status。
- api/v1/zz_generated.deepcopy.go:自动生成的深拷贝函数,用于资源对象的深拷贝。
- controllers/appservice_controller.go:控制器逻辑骨架。
- config/crd/bases/:后续生成的CRD YAML存放目录。
提示:如果只生成资源类型而不生成控制器,可将
--controller=false。对于纯CRD定义场景,可以先只生成资源,后续再补充控制器。
4.2 定义Spec与Status字段
CRD的核心在于Spec(期望状态)和Status(实际状态)的设计。打开api/v1/appservice_types.go,你会看到类似如下的骨架:
// AppServiceSpec defines the desired state of AppService
type AppServiceSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// 副本数
Replicas int32 `json:"replicas,omitempty"`
// 容器镜像
Image string `json:"image,omitempty"`
// 服务端口
Port int32 `json:"port,omitempty"`
}
// AppServiceStatus defines the observed state of AppService
type AppServiceStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// 当前就绪副本数
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
// 资源状态信息
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
字段设计原则:
- Spec字段:用户可配置的期望值,如副本数、镜像、端口、环境变量等。使用
json标签控制序列化名称,omitempty表示空值时忽略。 - Status字段:由控制器更新的观测值,如当前就绪副本数、资源状态、错误信息等。通常包含
Conditions数组用于表达资源生命周期阶段。 - 类型选择:基础类型(string、int32、bool)、Kubernetes标准类型(
corev1.ResourceRequirements)、自定义结构体。
注意:修改
_types.go文件后,必须运行make generate重新生成深拷贝函数和CRD YAML。否则,控制器在运行时可能因缺少深拷贝方法而崩溃。
4.3 生成CRD YAML
定义完字段后,使用Operator SDK的代码生成工具生成CRD YAML清单。执行以下命令:
# 生成CRD YAML(基于Go类型定义)
make generate
make manifests
执行流程:
- make generate:调用
controller-gen object:headerFile="hack/boilerplate.go.txt",更新zz_generated.deepcopy.go。 - make manifests:调用
controller-gen crd,根据api/v1/目录下的类型定义生成CRD YAML,输出到config/crd/bases/目录。
生成的CRD YAML文件路径示例:
config/crd/bases/example.example.com_appservices.yaml
该YAML文件包含了完整的CRD定义,包括:
- apiVersion:
apiextensions.k8s.io/v1 - kind:
CustomResourceDefinition - spec.group:
example.example.com - spec.names.kind:
AppService - spec.versions:包含
schema.openAPIV3Schema,即根据Go结构体自动生成的OpenAPI校验规则。
关键检查点:打开生成的CRD YAML,确认
spec.versions[0].schema.openAPIV3Schema.properties.spec下包含了你在Go类型中定义的所有字段。如果缺少字段,说明make generate未正确执行或类型定义有语法错误。
4.4 CRD定义与生成流程总览
下图展示了从创建API类型到生成CRD YAML的完整流程,以及各步骤产出的关键文件。
4.5 验证CRD定义
生成CRD YAML后,建议进行以下验证:
- 语法校验:
kubectl apply --dry-run=client -f config/crd/bases/example.example.com_appservices.yaml,确认无语法错误。 - 字段完整性:检查YAML中
properties.spec和properties.status是否包含所有定义字段。 - 校验规则:确认
openAPIV3Schema中字段类型、必填项、枚举值等约束是否正确。
最佳实践:在开发阶段,建议将CRD YAML纳入版本控制(Git),并配合CI流水线在每次修改类型后自动重新生成,确保CRD定义与代码一致。
4.6 常见问题与排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
make generate报错:deepcopy未生成 |
类型定义中使用了未注册的第三方类型 | 确保所有字段类型都实现了runtime.Object接口,或使用// +kubebuilder:object:generate=true标记 |
| CRD YAML中缺少字段 | 字段未添加json标签,或类型不可导出(小写开头) |
检查字段首字母大写,并添加json:"fieldName"标签 |
部署CRD时报schema validation error |
OpenAPI校验规则与字段类型不匹配 | 检查openAPIV3Schema中字段类型,确保与Go类型一致 |
| 控制器无法识别自定义资源 | CRD未部署或GVK(GroupVersionKind)不匹配 | 确认CRD已部署,且控制器中的For()或Watches()使用了正确的GVK |