LabHub
学习 学习路径 课程

CNPA — 云原生平台工程助理

被拒过一次,才懂那份契约

在 LabHub 中继续学习

一句话总结

CRD 的价值在于借用 API 服务器的验证、默认值、RBAC 与审计能力。在什么都接受的集群中,这些能力一项也无法验证。

概念图: 不具备 API 服务器能力 · 默认会裁剪字段 · 同时使用 · 同时关闭裁剪与验证

为什么必须经历拒绝

上一模块使用 CRD 构建了平台 API。但实验环境不具备 API 服务器能力,因此无论填入什么内容都会接受。

CRD 的作用是“把 API 服务器已有的能力借给自定义类型”,包括:

검증      스키마에 안 맞으면 거절한다
기본값    빠진 값을 저장 시점에 채운다
RBAC      다른 자원과 똑같이 권한을 건다
감사      누가 언제 무엇을 바꿨는지 남는다
watch     컨트롤러가 변화를 구독한다
문서      kubectl explain 이 스키마를 읽어 준다

自行构建 API 时,必须重新实现全部六项能力。但在只会接受请求的集群中,我们无法验证其中任何一项。

被悄然裁剪的字段

结构化模式默认会裁剪字段。不在 properties 中的字段会自动删除,并不是因为额外开启了阻止设置。把 replicas 误写为 replica 时,该值会消失,却没人提醒。

这里也常有人误用 additionalProperties: false。它不能与 properties 同时使用(Forbidden: mutual exclusive),因为没有使用必要,所以 API 直接禁止这种组合。

反过来,添加 x-kubernetes-preserve-unknown-fields: true同时关闭裁剪与验证。为了方便而加入它的一刻,所有边界都会消失。

由谁填充默认值会产生不同结果

스키마의 default    저장 시점에 채워져 kubectl get -o yaml 에 바로 보인다
컨트롤러가 채움     한참 뒤에 나타난다. 그 사이에는 비어 있다

开发者检查对象时就应看到值,才能知道会发生什么。黄金路径正是在这里形成。

修改模式时会破坏什么

CRD 部署后,集群中已经存在保存的对象。修改模式等于重新解释已保存数据,因此必须遵守规则。

变更 是否安全 原因
添加可选字段 旧对象只是该字段为空
添加必填字段 旧对象会验证失败,甚至无法修改
删除字段 ⚠️ 值会被裁剪,无法恢复
更改类型(string→int) 无法读取已保存值
增加 enum 值
删除 enum 值 使用该值的对象会失效

如果必须增加必填字段,应创建新版本v1alpha1v1beta1)。只能有一个版本设置 storage: true,其他版本通过转换(conversion)提供。没有转换 Webhook 时采用 None 策略,字段会原样通过;如果结构不同,就必须部署 Webhook。

模式能验证到什么程度

OpenAPI 模式能做和不能做的事情界限明确。

properties:
  replicas:
    type: integer
    minimum: 1
    maximum: 100
    default: 3
  tier:
    type: string
    enum: [bronze, silver, gold]
  name:
    type: string
    pattern: '^[a-z][a-z0-9-]{2,30}$'

这些可以由模式完成,但字段之间的关系不行,例如“tier 为 gold 时 replicas 至少为 5”。过去需要 Webhook,现在可以使用 CEL 验证规则在 CRD 内表达。

x-kubernetes-validations:
  - rule: "self.tier != 'gold' || self.replicas >= 5"
    message: "gold 등급은 복제본이 5개 이상이어야 합니다"
  - rule: "self.name == oldSelf.name"      # 불변 필드
    message: "name 은 만든 뒤에 바꿀 수 없습니다"

它明显优于 Webhook:无需独立部署,不会因 Webhook 宕机阻塞 API,还能让错误消息与模式放在一起。只有 CEL 无法表达时才使用 Webhook。

状态放在哪里

status 应与 spec 分离,放入子资源。

subresources:
  status: {}
  scale:
    specReplicasPath: .spec.replicas
    statusReplicasPath: .status.replicas

这样用户无法修改 status,控制器也无法修改 spec,权限自然分离。加入 scale 子资源后,kubectl scale 与 HPA 可以直接工作,这正是为自定义资源接入 HPA 的方法。

实践中真正重要的事项

**拼写错误不会返回错误,而会悄然消失。**结构化模式默认裁剪字段,把 replicas 写成 replica 时,值会静默删除。发布平台 API 时,最好在说明中要求用户通过 kubectl get -o yaml 重新读取保存结果。

**x-kubernetes-preserve-unknown-fields: true 是最后手段。**一旦为了方便加入它,裁剪和验证都会关闭,为该类型建立的全部边界随之消失。

**默认值应放在模式中,而不是控制器中。**模式的 default 会在保存时填充,开发者能立即看到;控制器填充则会留下一段空白时间。黄金路径就建立在这种差异之上。

下一项实验会在真实 API 服务器上,通过亲自遭到拒绝来验证这些内容。