本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
Kubernetes 1.25:CustomResourceDefinition 验证规则进阶至 Beta
在 Kubernetes 1.25 中,CustomResourceDefinitions (CRD) 的验证规则已晋升为 Beta!
验证规则使得使用通用表达式语言 (CEL) 声明如何验证自定义资源成为可能。例如
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
openAPIV3Schema:
type: object
properties:
spec:
type: object
x-kubernetes-validations:
- rule: "self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas"
message: "replicas should be in the range minReplicas..maxReplicas."
properties:
replicas:
type: integer
...
验证规则支持广泛的用例。为了解其部分功能,我们来看几个例子:
验证规则 | 目的 |
---|---|
self.minReplicas <= self.replicas | 验证一个整数字段小于或等于另一个整数字段 |
'Available' in self.stateCounts | 验证映射中存在键为 'Available' 的条目 |
self.set1.all(e, !(e in self.set2)) | 验证两个集合的元素互不相交 |
self == oldSelf | 验证一个必填字段一旦设置后就不可变 |
self.created + self.ttl < self.expired | 验证 'expired' 日期在 'create' 日期加上一个 'ttl' 持续时间之后 |
验证规则富有表现力且灵活。请参阅验证规则文档以了解更多关于验证规则功能的信息。
为什么选择 CEL?
选择 CEL 作为验证规则的语言有几个原因:
- CEL 表达式可以轻松地内联到 CRD 模式中。它们的表现力足以取代当前在准入 Webhook 中实现的绝大多数 CRD 验证检查。这使得 CRD 更加独立自足,也更易于理解。
- CEL 表达式会根据 CRD 的模式进行“提前”(在 CRD 创建和更新时)编译和类型检查,这使得它们在“运行时”(在验证自定义资源时)能够高效、安全地进行评估。即使是 CEL 中的正则表达式字符串字面量,在创建或更新 CRD 时也会被验证和预编译。
为什么不使用验证 Webhook?
与验证 Webhook 相比,使用验证规则有以下好处:
- CRD 作者可以受益于更简单的工作流程,因为验证规则免去了开发和维护 Webhook 的需要。
- 集群管理员受益于不再需要为 CRD 验证而安装、升级和操作 Webhook。
- 集群的可操作性得到改善,因为 CRD 验证不再需要远程调用 Webhook 端点,消除了 Kubernetes API 服务器请求服务路径中的一个潜在故障点。这使得集群能够在扩展到更大数量的已安装 CRD 扩展时保持高可用性,因为否则每增加一个已安装的 Webhook,预期的控制平面可用性都会降低。
开始使用验证规则
在 OpenAPIv3 模式中编写验证规则
你可以在 CRD 的 OpenAPIv3 模式的任何层级定义验证规则。验证规则会自动作用于它们在模式中声明的位置。
CRD 验证规则的良好实践
- 将验证规则的作用域尽可能地限定在它们所验证的字段附近。
- 在验证独立约束时使用多条规则。
- 不要将验证规则用于已有的验证
- 在可用时使用 OpenAPIv3 的值验证(
maxLength
、maxItems
、maxProperties
、required
、enum
、minimum
、maximum
等)和字符串格式。 - 在适当的地方使用
x-kubernetes-int-or-string
、x-kubernetes-embedded-type
和x-kubernetes-list-type=(set|map)
。
良好实践的示例
验证 | 最佳实践 | 示例 |
---|---|---|
验证一个整数在 0 到 100 之间。 | 使用 OpenAPIv3 的值验证。 | type: integer |
限制映射(带有 additionalProperties 的对象)、数组和字符串的最大大小限制。 | 使用 OpenAPIv3 值验证。推荐用于所有映射、数组和字符串。这一最佳实践对于规则成本估算(下文解释)至关重要。 | type: |
要求一个日期时间比某个特定时间戳更晚。 | 使用 OpenAPIv3 字符串格式来声明该字段为日期时间。使用验证规则将其与特定时间戳进行比较。 | type: string |
要求两个集合不相交。 | 使用 x-kubernetes-list-type 来验证数组是集合。 使用验证规则来验证集合不相交。 | type: object |
CRD 转换规则
转换规则使得在验证规则中比较资源的新旧状态成为可能。你可以使用转换规则来确保集群的 API 服务器不接受无效的状态转换。转换规则是引用 'oldSelf' 的验证规则。API 服务器仅在旧值和新值都存在时才评估转换规则。
转换规则示例
转换规则 | 目的 |
---|---|
self == oldSelf | 对于必填字段,使其一旦设置后不可变。对于可选字段,只允许从未设置转换为已设置,或从已设置转换为未设置。 |
(在字段的父级上) has(self.field) == has(oldSelf.field) 在字段上: self == oldSelf | 使一个字段不可变:验证一个字段,即使是可选的,在资源创建后也永不改变(对于必填字段,前面的规则更简单)。 |
self.all(x, x in oldSelf) | 只允许向代表集合的字段添加项(防止删除)。 |
self >= oldSelf | 验证一个数字是单调递增的。 |
使用函数库
验证规则可以访问几个不同的函数库:
函数库使用示例
验证规则 | 目的 |
---|---|
!(self.getDayOfWeek() in [0, 6]) | 验证一个日期不是周日或周六。 |
isUrl(self) && url(self).getHostname() in ['a.example.com', 'b.example.com'] | 验证一个 URL 的主机名在允许的范围内。 |
self.map(x, x.weight).sum() == 1 | 验证对象列表中各对象的权重总和为 1。 |
int(self.find('^[0-9]*')) < 100 | 验证字符串以小于 100 的数字开头。 |
self.isSorted() | 验证一个列表是已排序的。 |
资源使用和限制
为了防止 CEL 评估消耗过多的计算资源,验证规则施加了一些限制。这些限制基于 CEL *成本单位*,这是一种与平台和机器无关的执行成本度量。因此,无论在何处强制执行,限制都是相同的。
估算成本限制
CEL 在设计上是非图灵完备的,因此停机问题不是一个问题。CEL 利用这一设计选择,包含了一个“估算成本”子系统,可以静态计算任何 CEL 表达式的最坏情况运行时成本。验证规则与估算成本系统集成,并禁止将估算成本过高(高)的 CEL 表达式包含在 CRD 中。估算成本限制设置得相当高,通常需要对无界大小的对象执行 O(n^2) 或更差的操作才会超出。幸运的是,修复通常非常简单:因为成本系统知道 CRD 模式中声明的大小限制,CRD 作者可以在 CRD 的模式中添加大小限制(数组的 `maxItems`、映射的 `maxProperties`、字符串的 `maxLength`)来降低估算成本。
良好实践
在 CRD 模式中为所有数组、映射(带有 `additionalProperties` 的 `object`)和字符串类型设置 `maxItems`、`maxProperties` 和 `maxLength`!这会带来更低、更准确的估算成本,并通常使 CRD 更安全。
CRD 验证规则的运行时成本限制
除了估算成本限制外,CEL 在评估 CEL 表达式时还会跟踪实际成本,如果超过限制,将停止表达式的执行。
由于已经有了估算成本限制,运行时成本限制很少会遇到。但它是有可能发生的。例如,对于一个完全由单个大列表组成的大型资源,以及一个在列表的每个元素上评估或遍历整个列表的验证规则,可能会遇到这种情况。
CRD 作者可以确保不会超过运行时成本限制,方法与避免估算成本限制的方式大致相同:通过在数组、映射和字符串类型上设置 `maxItems`、`maxProperties` 和 `maxLength`。
未来的工作
我们期待与社区合作,共同推动 CRD 验证规则的采用,并希望在即将到来的 Kubernetes 版本中看到该功能晋升为正式发布!
一个不断壮大的 Kubernetes 贡献者社区正在思考如何使用 CEL 作为准入 Webhook 的替代品,为策略执行用例编写可扩展的准入控制器。任何感兴趣的人都应该通过常规的 SIG API Machinery 渠道或 Slack 上的 #sig-api-machinery-cel-dev 与我们联系。
致谢
特别感谢 Cici Huang、Ben Luddy、Jordan Liggitt、David Eads、Daniel Smith、Stefan Schimanski 博士、Leila Jalali 以及所有为验证规则做出贡献的人!