LabHub
学习 学习路径 课程

CNPE — 云原生平台工程师

CRD 不是功能,是契约

在 LabHub 中继续学习

一句话总结

CRD 向 API 注册新对象类型。完整的平台契约还必须规定接受哪些值、允许哪些变更、支持哪些版本、谁写状态。对象创建成功不等于服务已经就绪。

概念图: 一句话总结 · 为什么需要它 · 工作原理 · schema 检查形状,CEL 检查关系

为什么需要它

团队创建数据库 AppClaim,kubectl apply 成功却没有连接地址,因为 API 只保存了请求,尚无控制器创建真实数据库。重新应用 CRD 不能解决问题,应区分准入和调谐缺少哪一环。CRD 本身不会创建工作负载,必须配合控制器。本练习处理 CRD、验证、RBAC、配额,不实现 AppClaim 控制器。参阅官方 Custom Resources 概念

工作原理

schema 检查形状,CEL 检查关系

integer 类型并不能保证 replicas <= maxReplicas。两个字段都可能是合法整数,但申请值超过上限。应在包含它们的 spec schema 位置加入:

type: object
required: [tier, replicas, maxReplicas]
properties:
  tier:
    type: string
    enum: [bronze, silver, gold]
  replicas: {type: integer, minimum: 1}
  maxReplicas: {type: integer, minimum: 1}
x-kubernetes-validations:
  - rule: "self.replicas <= self.maxReplicas"
    message: "replicas는 maxReplicas 이하여야 합니다"

required 检查该位置字段缺失,enum 限制集合,CEL 检查关系。若 spec 本身必需,还应在上层 schema 声明 required: [spec];下层 required 不会使上层对象自动必需。参阅官方 CRD schema 与验证

不变规则的位置决定比较范围

默认转移规则中的 oldSelf 是对应旧值。本练习不使用 optionalOldSelf,创建时没有旧值,规则会跳过。spec 位置的 self.tier == oldSelf.tier 只比较 tier;同处使用 self == oldSelf 会冻结整个 spec,连正常 replicas 变更也阻止;放在 tier 字段上的 self == oldSelf 则只比较 tier。应检查规则挂载位置,而非只背字符串。参阅官方 CEL 转移规则说明

不变性也可由独立 admission 实现。新版 Kubernetes 的 optionalOldSelf: true 会在没有旧值时仍评估,并把 oldSelf 变为 Optional,因此不能笼统认为 oldSelf 规则创建时永不执行。参阅optionalOldSelf 条件与行为

served 与 storage 是不同承诺

项目 含义 不保证
served 提供该版本 API 路径 与其他版本验证相同
storage 新写入使用的唯一存储版本 旧对象已批量转换
conversion 版本间表示转换 创建外部服务或替代策略验证

从两个版本读取不会生成两个对象,而是同一 namespace/name 的不同 API 表示。修改 storage 不会自动重写所有旧对象。移除旧版本前要完成客户端迁移、存储迁移和 status.storedVersions 清理。默认 conversion.strategy: None 不会把 size 自动改成 capacity,字段改名需要单独转换设计。旧 API 开放期间,也要测试正常与禁止输入。参阅官方版本管理和移除流程

status 是独立写入路径

启用 subresources.status: {} 后,普通 POST、PUT、PATCH 会忽略 status 变更,/status 请求会忽略 status 之外的变更。这用于分离期望值与观测值的写入者,并不会自动填 status 或保证“Ready”为真。参阅官方 status 子资源契约。用户修改 spec 与控制器修改 status 的权限应分开。additionalPrinterColumns 只显示值,不会替代计算值的控制器。

现场表现

实际 k3s 中,v1 拒绝 replicas=9, maxReplicas=4tier=platinum,v1alpha1 却允许,原因是只给 storage v1 配置了规则。关键不是规则是否存在于某处,而是所有可用 API 路径是否遵守同一契约。测试要并列覆盖各版本正常创建、错误容量、未知 tier、拒绝 tier 变更、允许 replicas 变更。只测拒绝会漏掉“拒绝一切”的错误规则。

下一步

下一练习会定义两个版本的 AppClaim,并发送服务器 dry-run。正常值应通过,错误值应以相应字段验证错误拒绝。不能仅凭命令失败就宣称验证成功;下一理论还会讨论主体权限和发放数量上限。