4. 自定义资源(CRD)设计:定义 API 类型、生成清单与验证规则

自定义资源(Custom Resource Definition, CRD)是 Kubernetes 扩展能力的核心。在 Operator SDK 中,CRD 的设计直接决定了控制器的数据模型与行为边界。本章将深入讲解如何通过 types.go 定义 API 类型、使用工具链生成 CRD 清单,以及如何为字段添加验证规则,确保资源声明的正确性与健壮性。

4.1 API 类型定义:types.go 的结构与规范

Operator SDK 使用 types.go 文件定义自定义资源的结构。该文件通常位于 api/<version>/ 目录下,包含三个核心部分:Spec(期望状态)、Status(实际状态)和 资源顶层结构。

关键原则:Spec 定义用户输入的“期望”,Status 定义控制器观测的“实际”。两者分离,避免控制器直接修改 Spec。

以下是一个资源配额分配策略的典型 types.go 示例:

package v1

import (
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=resourcequotas,shortName=rq
type ResourceQuotaPolicy struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   ResourceQuotaPolicySpec   `json:"spec,omitempty"`
    Status ResourceQuotaPolicyStatus `json:"status,omitempty"`
}

type ResourceQuotaPolicySpec struct {
    // +kubebuilder:validation:Required
    // +kubebuilder:validation:Minimum=1
    // +kubebuilder:validation:Maximum=100
    Replicas int32 `json:"replicas"`

    // +kubebuilder:validation:Optional
    // +kubebuilder:default={"cpu": "500m", "memory": "256Mi"}
    ResourceLimits corev1.ResourceList `json:"resourceLimits,omitempty"`

    // +kubebuilder:validation:Enum=Strict;BestEffort;Burstable
    AllocationMode string `json:"allocationMode"`
}

type ResourceQuotaPolicyStatus struct {
    // +kubebuilder:validation:Optional
    Conditions []metav1.Condition `json:"conditions,omitempty"`

    // +kubebuilder:validation:Optional
    ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}

4.2 字段标记与验证规则

Kubebuilder 标记(markers)是嵌入在代码注释中的指令,用于控制 CRD 生成行为。下表列出最常用的验证与约束标记:

标记 作用 示例
+kubebuilder:validation:Required 标记字段为必填 // +kubebuilder:validation:Required
+kubebuilder:validation:Optional 标记字段为可选 // +kubebuilder:validation:Optional
+kubebuilder:validation:Minimum 设置数值最小值 // +kubebuilder:validation:Minimum=1
+kubebuilder:validation:Maximum 设置数值最大值 // +kubebuilder:validation:Maximum=100
+kubebuilder:validation:Enum 限制字段只能取指定枚举值 // +kubebuilder:validation:Enum=Strict;BestEffort
+kubebuilder:default 设置字段默认值 // +kubebuilder:default={"cpu":"500m"}
+kubebuilder:validation:Pattern 正则表达式校验字符串 // +kubebuilder:validation:Pattern=`^[a-z]+$`
实战建议:对于资源配额字段,优先使用 corev1.ResourceList 类型,它天然支持 Kubernetes 资源单位的校验(如 "500m"、"1Gi"),无需额外编写验证逻辑。

4.3 生成 CRD 清单

Operator SDK 提供 make generate 和 make manifests 命令,自动从 types.go 生成 CRD YAML 清单。执行流程如下:

  1. 生成 DeepCopy 方法:make generate 调用 controller-gen object:headerFile="hack/boilerplate.go.txt",为每个资源类型生成 DeepCopyInto 和 DeepCopyObject 方法。
  2. 生成 CRD YAML:make manifests 调用 controller-gen crd,输出到 config/crd/bases/ 目录。
  3. 生成 RBAC 与 Webhook 配置:同时生成 ClusterRole、Webhook 等清单文件。

生成的 CRD 清单示例(简化):

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: resourcequotapolicies.example.com
spec:
  group: example.com
  names:
    kind: ResourceQuotaPolicy
    plural: resourcequotapolicies
    shortNames:
    - rq
  scope: Namespaced
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              replicas:
                minimum: 1
                maximum: 100
                type: integer
              allocationMode:
                enum:
                - Strict
                - BestEffort
                - Burstable
                type: string
    subresources:
      status: {}

4.4 CRD 字段与验证规则设计流程

下图展示了从 types.go 定义到 CRD 清单生成的完整链路:

CRD 设计流程:从 types.go 到 CRD 清单 types.go Spec + Status + 标记 make generate 生成 DeepCopy 方法 make manifests 生成 CRD YAML CRD 清单 config/crd/bases/ 验证规则 OpenAPI v3 Schema 标记驱动生成 无需手写 OpenAPI Schema

4.5 高级验证:Webhook 与 CEL

除了 CRD 内置的 OpenAPI v3 验证,Operator SDK 还支持两种更强大的验证方式:

  • 准入 Webhook(ValidatingAdmissionWebhook):在资源创建或更新时执行自定义 Go 逻辑,适合跨字段校验或调用外部系统。
  • CEL(Common Expression Language):Kubernetes 1.25+ 支持在 CRD 中直接嵌入 CEL 表达式,无需编写 Webhook 即可实现复杂校验。

CEL 示例(在 CRD 中直接定义):

x-kubernetes-validations:
- rule: "self.replicas <= self.maxReplicas"
  message: "replicas 不能超过 maxReplicas"
- rule: "has(self.allocationMode) ? self.allocationMode in ['Strict','BestEffort'] : true"
  message: "allocationMode 必须为 Strict 或 BestEffort"
注意:CEL 表达式在 CRD 级别生效,性能优于 Webhook,但无法调用外部 API。对于需要复杂业务逻辑的场景,仍应使用 ValidatingWebhook。

4.6 字段设计最佳实践

实践 说明 示例
使用强类型 避免使用 string 表示枚举,使用 Enum 标记 // +kubebuilder:validation:Enum=Strict;BestEffort
设置合理默认值 减少用户输入负担,提升易用性 // +kubebuilder:default={"cpu":"500m"}
区分 Spec 与 Status Spec 只读(用户设置),Status 只写(控制器更新) Status 字段全部标记 Optional
使用资源类型 CPU/内存使用 corev1.ResourceList 自动支持 "500m"、"1Gi" 格式
避免嵌套过深 嵌套超过 3 层影响可读性与校验性能 使用扁平结构 + 引用类型

4.7 验证规则冲突与调试

在 CRD 设计过程中,常见的验证规则冲突包括:

  • Minimum 大于 Maximum:导致所有值均无法通过校验。
  • Enum 与 Pattern 冲突:同时设置枚举和正则,可能导致无合法值。
  • Required 与 Default 冲突:必填字段不应设置默认值,否则默认值会被忽略。

调试方法:使用 kubectl explain 查看 CRD 的 Schema 结构,或直接查看 config/crd/bases/ 下的 YAML 文件,确认生成的 OpenAPI 规则是否符合预期。

快速验证:执行 make manifests 后,使用 kubectl apply --dry-run=server -f config/samples/ 测试 CRD 实例的校验效果。