这篇文章已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发布以来是否已失效。
Kubernetes 1.25:CustomResourceDefinition 验证规则进阶至 Beta
在 Kubernetes 1.25 中,CustomResourceDefinitions 的验证规则 (CRD) 已升级到 Beta 版本!
验证规则可以使用 Common Expression Language (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 | 验证一个 map 中存在键为 '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 模式已提供的验证使用验证规则
- 在可用时使用 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 |
约束 map (带有 additionalProperties 的对象)、数组和字符串的最大大小限制。 | 使用 OpenAPIv3 值验证。推荐用于所有 map、数组和字符串。此最佳实践对于规则成本估算 (下文解释) 至关重要。 | 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
,map 使用 maxProperties
,字符串使用 maxLength
) 来降低估算成本。
良好实践
在 CRD 模式的所有数组、map (带有 additionalProperties
的 object
) 和字符串类型上设置 maxItems
、maxProperties
和 maxLength
!这可以降低估算成本并使其更准确,通常也能使 CRD 更安全地使用。
CRD 验证规则的运行时成本限制
除了估算成本限制外,CEL 在评估 CEL 表达式时还会跟踪实际成本,如果超出限制,将中止表达式的执行。
由于估算成本限制已经存在,运行时成本限制很少会被触发。但这仍然是可能的。例如,对于一个完全由一个大型列表组成的大资源,如果验证规则在列表中的每个元素上进行评估,或者遍历整个列表,就可能遇到运行时成本限制。
CRD 作者可以通过与避免估算成本限制类似的方式来确保不会超出运行时成本限制:在数组、map 和字符串类型上设置 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 以及所有为验证规则做出贡献的人!