这篇文章发布已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不正确。

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验证 map 中存在一个键为 'Available' 的条目
self.set1.all(e, !(e in self.set2))验证两个集合的元素是不相交的
self == oldSelf验证一个必需字段一旦设置就不可变
self.created + self.ttl < self.expired验证“过期”日期在“创建”日期加上“ttl”持续时间之后

验证规则具有表达性和灵活性。 请参阅验证规则文档,了解有关验证规则功能的更多信息。

为什么选择 CEL?

选择 CEL 作为验证规则的语言有两个原因

  • CEL 表达式可以很容易地内联到 CRD 模式中。它们具有足够的表达能力,可以取代目前在 admission webhooks 中实现的大多数 CRD 验证检查。 这使得 CRD 是独立的,并且更容易理解。
  • CEL 表达式会针对 CRD 的模式进行“提前”编译和类型检查(当创建和更新 CRD 时),从而允许它们在“运行时”(当验证自定义资源时)高效且安全地进行评估。 即使 CEL 中的正则表达式字符串字面量也会在创建或更新 CRD 时进行验证和预编译。

为什么不使用验证 webhook?

与验证 webhook 相比,使用验证规则的好处

  • CRD 作者受益于更简单的工作流程,因为验证规则消除了开发和维护 webhook 的需要。
  • 集群管理员不再需要为了 CRD 验证而安装、升级和操作 webhook,从而受益。
  • 集群的可操作性得到了提高,因为 CRD 验证不再需要远程调用 webhook 端点,从而消除了 Kubernetes API 服务器请求服务路径中潜在的故障点。 这使得集群能够保持高可用性,同时可以扩展到更大的已安装 CRD 扩展,因为预期的控制平面可用性会随着每个附加 webhook 的安装而降低。

开始使用验证规则

在 OpenAPIv3 模式中编写验证规则

您可以为 CRD 的 OpenAPIv3 模式的任何级别定义验证规则。验证规则会自动限定到模式中声明它们的位置。

CRD 验证规则的良好实践

  • 将验证规则的作用域尽可能缩小到它们验证的字段。
  • 在验证独立约束时使用多个规则。
  • 不要对已经存在的验证使用验证规则
  • 在可用时使用 OpenAPIv3 值验证maxLengthmaxItemsmaxPropertiesrequiredenumminimummaximum,..)和 字符串格式
  • 在适当的地方使用 x-kubernetes-int-or-stringx-kubernetes-embedded-typex-kubernetes-list-type=(set|map)

良好实践示例

验证最佳实践示例
验证一个整数在 0 到 100 之间。使用 OpenAPIv3 值验证。
type: integer
minimum: 0
maximum: 100
约束 maps (带有 additionalProperties 的对象)、数组和字符串的最大大小限制。使用 OpenAPIv3 值验证。 推荐用于所有 maps、数组和字符串。 此最佳实践对于规则成本估算(如下所述)至关重要。
type:
maxItems: 100
要求日期时间比特定时间戳更新。使用 OpenAPIv3 字符串格式声明该字段是日期时间。 使用验证规则将其与特定的时间戳进行比较。
type: string
format: date-time
x-kubernetes-validations:
- rule: "self >= timestamp('2000-01-01T00:00:00.000Z')"
要求两个集合是不相交的。使用 x-kubernetes-list-type 验证数组是集合。
使用验证规则验证集合是不相交的。
type: object
properties:
set1:
type: array
x-kubernetes-list-type: set
set2: ...
x-kubernetes-validations:
- rule: "!self.set1.all(e, !(e in self.set2))"

CRD 过渡规则

过渡规则使得可以在验证规则中将资源的新状态与旧状态进行比较。 您可以使用过渡规则来确保集群的 API 服务器不接受无效的状态转换。 过渡规则是引用 'oldSelf' 的验证规则。 仅当旧值和新值都存在时,API 服务器才会评估过渡规则。

过渡规则示例

过渡规则目的
self == oldSelf对于必需字段,一旦设置该字段就使其不可变。 对于可选字段,仅允许从 unset 到 set 或从 set 到 unset 的转换。
(在字段的父级上)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、maps 的 maxProperties、字符串的 maxLength)来降低估计成本。

良好实践

在 CRD 模式中的所有数组、map (带有 additionalPropertiesobject) 和字符串类型上设置 maxItemsmaxPropertiesmaxLength! 这将导致更低且更准确的估计成本,并且通常使 CRD 更安全地使用。

CRD 验证规则的运行时成本限制

除了估计成本限制之外,CEL 还会跟踪评估 CEL 表达式时的实际成本,如果超过限制,则会停止表达式的执行。

由于已设置了估计成本限制,运行时成本限制很少遇到。但这是有可能发生的。例如,对于一个完全由单个大型列表组成的大型资源,以及对列表中的每个元素进行评估或遍历整个列表的验证规则,就可能遇到这种情况。

CRD 作者可以通过与避免估计成本限制类似的方式,确保不会超出运行时成本限制:通过在数组、映射和字符串类型上设置 maxItemsmaxPropertiesmaxLength

未来工作

我们期待与社区合作,共同推广 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 以及所有为验证规则做出贡献的人!