Kubernetes 中的通用表达语言

Kubernetes API 中使用 通用表达式语言 (CEL) 来声明验证规则、策略规则以及其他约束或条件。

CEL 表达式直接在 API 服务器中进行评估,这使得 CEL 成为许多可扩展性用例的便捷替代方案,而无需使用 webhooks 等进程外机制。只要控制平面的 API 服务器组件保持可用,你的 CEL 表达式就会继续执行。

语言概述

CEL 语言具有简单的语法,类似于 C、C++、Java、JavaScript 和 Go 中的表达式。

CEL 旨在嵌入到应用程序中。每个 CEL“程序”都是一个评估为单个值的表达式。CEL 表达式通常是简短的“单行代码”,可以很好地嵌入到 Kubernetes API 资源的字符串字段中。

CEL 程序的输入是“变量”。包含 CEL 的每个 Kubernetes API 字段都在 API 文档中声明了该字段可用的变量。例如,在 CustomResourceDefinitions 的 x-kubernetes-validations[i].rules 字段中,selfoldSelf 变量可用,它们引用了要由 CEL 表达式验证的自定义资源数据的先前状态和当前状态。其他 Kubernetes API 字段可能声明不同的变量。参阅 API 字段的 API 文档,了解该字段可用的变量。

CEL 表达式示例

CEL 表达式示例及其用途
规则用途
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas验证定义副本数的三个字段是否按适当顺序排列
'Available' in self.stateCounts验证 Map 中是否存在键为 'Available' 的条目
(self.list1.size() == 0) != (self.list2.size() == 0)验证两个列表中的一个不为空,但不能同时为空
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$'))验证 listMap 条目中键字段 'name' 为 'MY_ENV' 的 'value' 字段
has(self.expired) && self.created + self.ttl < self.expired验证 'expired' 日期晚于 'create' 日期加上 'ttl' 时长
self.health.startsWith('ok')验证 'health' 字符串字段具有前缀 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)验证键为 'x' 的 listMap 项的 'foo' 属性小于 10
type(self) == string ? self == '99%' : self == 42验证 int-or-string 字段的整型和字符串两种情况
self.metadata.name == 'singleton'验证对象的名称匹配特定值(使其成为单例)
self.set1.all(e, !(e in self.set2))验证两个 listSet 不相交
self.names.size() == self.details.size() && self.names.all(n, n in self.details)验证 'details' Map 的键是 'names' listSet 中的项
self.details.all(key, key.matches('^[a-zA-Z]*$'))验证 'details' Map 的键
self.details.all(key, self.details[key].matches('^[a-zA-Z]*$'))验证 'details' Map 的值

CEL 选项、语言特性和库

CEL 配置了以下选项、库和语言特性,它们是在指定的 Kubernetes 版本中引入的

CEL 选项、库或语言特性包含可用性
CEL 选项、库或语言特性包含可用性
标准宏has, all, exists, exists_one, map, filter所有 Kubernetes 版本
标准函数参阅标准定义官方列表所有 Kubernetes 版本
同质聚合字面量-所有 Kubernetes 版本
默认 UTC 时区-所有 Kubernetes 版本
急切验证声明-所有 Kubernetes 版本
扩展字符串库,版本 1charAt, indexOf, lastIndexOf, lowerAscii, upperAscii, replace, split, join, substring, trim所有 Kubernetes 版本
Kubernetes list 库参阅Kubernetes list 库所有 Kubernetes 版本
Kubernetes regex 库参阅Kubernetes regex 库所有 Kubernetes 版本
Kubernetes URL 库参阅Kubernetes URL 库所有 Kubernetes 版本
Kubernetes authorizer 库参阅Kubernetes authorizer 库所有 Kubernetes 版本
Kubernetes quantity 库参阅Kubernetes quantity 库Kubernetes 1.29 及更高版本
CEL 可选类型参阅CEL 可选类型Kubernetes 1.29 及更高版本
CEL CrossTypeNumericComparisons参阅CEL CrossTypeNumericComparisonsKubernetes 1.29 及更高版本

CEL 函数、特性和语言设置支持 Kubernetes 控制平面回滚。例如,CEL 可选值在 Kubernetes 1.29 中引入,因此只有该版本或更高版本的 API 服务器才会接受使用CEL 可选值的 CEL 表达式的写入请求。但是,当集群回滚到 Kubernetes 1.28 时,已存储在 API 资源中且使用“CEL 可选值”的 CEL 表达式将继续正确评估。

Kubernetes CEL 库

除了 CEL 社区库之外,Kubernetes 还包含了在 Kubernetes 中使用 CEL 的所有地方都可用的 CEL 库。

Kubernetes list 库

list 库包含 indexOflastIndexOf,它们的功能与同名的字符串函数类似。这些函数返回给定元素在列表中的第一个或最后一个位置索引。

list 库还包括 minmaxsum。Sum 支持所有数值类型以及 duration 类型。Min 和 max 支持所有可比较类型。

isSorted 也作为便捷函数提供,并支持所有可比较类型。

示例

使用 list 库函数的 CEL 表达式示例
CEL 表达式用途
names.isSorted()验证名称列表按字母顺序排列
items.map(x, x.weight).sum() == 1.0验证对象列表中“权重”的总和为 1.0
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min()验证两组优先级不重叠
names.indexOf('should-be-first') == 1要求列表中的第一个名称是特定值

参阅 Kubernetes List Library godoc 了解更多信息。

Kubernetes regex 库

除了 CEL 标准库提供的 matches 函数外,regex 库还提供了 findfindAll,支持更广泛的正则表达式操作。

示例

使用 regex 库函数的 CEL 表达式示例
CEL 表达式用途
"abc 123".find('[0-9]+')查找字符串中的第一个数字
"1, 2, 3, 4".findAll('[0-9]+').map(x, int(x)).sum() < 100验证字符串中的数字总和小于 100

参阅 Kubernetes regex 库 godoc 了解更多信息。

Kubernetes URL 库

为了更容易、更安全地处理 URL,添加了以下函数

  • isURL(string) 检查字符串是否是根据 Go 的 net/url 包验证的有效 URL。该字符串必须是绝对 URL。
  • url(string) URL 将字符串转换为 URL,如果字符串不是有效 URL 则导致错误。

通过 url 函数解析后,生成的 URL 对象具有 getSchemegetHostgetHostnamegetPortgetEscapedPathgetQuery 访问器。

示例

使用 URL 库函数的 CEL 表达式示例
CEL 表达式用途
url('https://example.com:80/').getHost()获取 URL 的 'example.com:80' 主机部分
url('https://example.com/path with spaces/').getEscapedPath()返回 '/path%20with%20spaces/'

参阅 Kubernetes URL 库 godoc 了解更多信息。

Kubernetes authorizer 库

对于 API 中类型为 Authorizer 的变量可用的 CEL 表达式,可以使用 authorizer 对请求的主体(已认证用户)执行授权检查。

API 资源检查如下执行

  1. 指定要检查的 group 和 resource:Authorizer.group(string).resource(string) ResourceCheck
  2. 可选地调用以下构建器函数的任意组合以进一步缩小授权检查范围。请注意,这些函数返回接收者类型,可以链式调用
    • ResourceCheck.subresource(string) ResourceCheck
    • ResourceCheck.namespace(string) ResourceCheck
    • ResourceCheck.name(string) ResourceCheck
  3. 调用 ResourceCheck.check(verb string) Decision 执行授权检查。
  4. 调用 allowed() boolreason() string 检查授权检查结果。

执行非资源授权如下使用

  1. 仅指定路径:Authorizer.path(string) PathCheck
  2. 调用 PathCheck.check(httpVerb string) Decision 执行授权检查。
  3. 调用 allowed() boolreason() string 检查授权检查结果。

对 ServiceAccount 执行授权检查

  • Authorizer.serviceAccount(namespace string, name string) Authorizer
使用 URL 库函数的 CEL 表达式示例
CEL 表达式用途
authorizer.group('').resource('pods').namespace('default').check('create').allowed()如果主体(用户或 ServiceAccount)被允许在 'default' 命名空间中创建 Pod,则返回 true。
authorizer.path('/healthz').check('get').allowed()检查主体(用户或 ServiceAccount)是否有权对 /healthz API 路径发出 HTTP GET 请求。
authorizer.serviceAccount('default', 'myserviceaccount').resource('deployments').check('delete').allowed()检查 ServiceAccount 是否有权删除 Deployment。
特性状态:Kubernetes v1.31 [alpha]

启用 alpha 特性 AuthorizeWithSelectors 后,可以在授权检查中添加字段和标签选择器。

使用选择器授权函数的 CEL 表达式示例
CEL 表达式用途
authorizer.group('').resource('pods').fieldSelector('spec.nodeName=mynode').check('list').allowed()如果主体(用户或 ServiceAccount)被允许使用字段选择器列出 Pod,则返回 true。spec.nodeName=mynode.
authorizer.group('').resource('pods').labelSelector('example.com/mylabel=myvalue').check('list').allowed()如果主体(用户或 ServiceAccount)被允许使用标签选择器列出 Pod,则返回 true。example.com/mylabel=myvalue.

参阅 Kubernetes Authz 库Kubernetes AuthzSelectors 库 godoc 了解更多信息。

Kubernetes quantity 库

Kubernetes 1.28 添加了对 Quantity 字符串(例如 1.5G、512k、20Mi)的操作支持。

  • isQuantity(string) 检查字符串是否是根据 Kubernetes 的 resource.Quantity 验证的有效 Quantity。
  • quantity(string) Quantity 将字符串转换为 Quantity,如果字符串不是有效 Quantity 则导致错误。

通过 quantity 函数解析后,生成的 Quantity 对象具有以下成员函数库

Quantity 的可用成员函数
成员函数CEL 返回值描述
isInteger()bool当且仅当 asInteger 可以安全调用且不会出错时返回 true
asInteger()int返回当前值的表示形式为int64如果可能,或者如果转换会导致溢出或精度损失,则会导致错误。
asApproximateFloat()float返回一个float64Quantity 的表示形式,可能会损失精度。如果 Quantity 的值超出float64, +Inf/-Inf将返回。
sign()int返回1如果 Quantity 为正,则返回-1如果 Quantity 为负,则返回0如果 Quantity 为零,则返回
add(<Quantity>)数量返回两个 Quantity 的总和
add(<int>)数量返回 Quantity 和整数的总和
sub(<Quantity>)数量返回两个 Quantity 之间的差
sub(<int>)数量返回 Quantity 和整数之间的差
isLessThan(<Quantity>)bool当且仅当接收者小于操作数时返回 true
isGreaterThan(<Quantity>)bool当且仅当接收者大于操作数时返回 true
compareTo(<Quantity>)int将接收者与操作数进行比较,如果相等返回 0,如果接收者较大返回 1,如果接收者较小返回 -1

示例

使用 URL 库函数的 CEL 表达式示例
CEL 表达式用途
quantity("500000G").isInteger()测试转换为整数是否会抛出错误
quantity("50k").asInteger()精确转换为整数
quantity("9999999999999999999999999999999999999G").asApproximateFloat()有损转换为浮点数
quantity("50k").add(quantity("20k"))将两个 Quantity 相加
quantity("50k").sub(20000)从 Quantity 中减去整数
quantity("50k").add(20).sub(quantity("100k")).sub(-50000)链式调用整数和 Quantity 的加减运算
quantity("200M").compareTo(quantity("0.2G"))比较两个 Quantity
quantity("150Mi").isGreaterThan(quantity("100Mi"))测试 Quantity 是否大于接收者
quantity("50M").isLessThan(quantity("100M"))测试 Quantity 是否小于接收者

类型检查

CEL 是一种渐进类型语言

一些 Kubernetes API 字段包含完全类型检查的 CEL 表达式。例如,CustomResourceDefinitions 验证规则是完全类型检查的。

一些 Kubernetes API 字段包含部分类型检查的 CEL 表达式。部分类型检查的表达式是指某些变量是静态类型的,而另一些变量是动态类型化的。例如,在 ValidatingAdmissionPolicies 的 CEL 表达式中,request 变量是类型化的,而 object 变量是动态类型化的。因此,包含 request.namex 的表达式会由于 namex 字段未定义而导致类型检查失败。然而,即使在 object 引用的资源类型中未定义 namex 字段,object.namex 也会通过类型检查,因为 object 是动态类型化的。

CEL 中的 has() 宏可在 CEL 表达式中使用,用于在尝试访问动态类型变量的字段值之前检查该字段是否可访问。例如

has(object.namex) ? object.namex == 'special' : request.name == 'special'

类型系统集成

显示 OpenAPIv3 类型和 CEL 类型之间关系的表格
OpenAPIv3 类型CEL 类型
具有 Properties 的 'object'object / "消息类型" (type(<object>)评估为selfType<uniqueNumber>.path.to.object.from.self)
具有 的 'object'additionalPropertiesmap
具有 的 'object'x-kubernetes-embedded-typeobject / "消息类型",'apiVersion'、'kind'、'metadata.name' 和 'metadata.generateName' 隐式包含在模式中
具有 x-kubernetes-preserve-unknown-fields 的 'object'object / "消息类型",未知字段在 CEL 表达式中不可访问
x-kubernetes-int-or-string的联合intstring, self.intOrString < 100 | self.intOrString == '50%'对两者均评估为 true50"50%"
'array'list
具有 的 'array'x-kubernetes-list-type=map具有基于 map 的相等性与唯一键保证的 list
具有 的 'array'x-kubernetes-list-type=set具有基于 set 的相等性与唯一条目保证的 list
'boolean'boolean
'number' (所有格式)double
'integer' (所有格式)int (64)
无对应项uint (64)
'null'null_type
'string'string
格式为 byte ('string') 的 'string' (base64 编码)bytes
格式为 date 的 'string'timestamp (google.protobuf.Timestamp)
格式为 datetime 的 'string'timestamp (google.protobuf.Timestamp)
格式为 duration 的 'string'duration (google.protobuf.Duration)

另请参阅:CEL 类型OpenAPI 类型Kubernetes 结构化模式

对于 x-kubernetes-list-typesetmap 的数组,相等性比较会忽略元素顺序。例如,如果数组代表 Kubernetes set 值,则 [1, 2] == [2, 1]

具有 x-kubernetes-list-type 的数组上的连接使用列表类型的语义

set
X + Y 执行并集操作,保留 X 中所有元素的数组位置,并附加 Y 中不相交的元素,保留它们的偏序。
map
X + Y 执行合并操作,保留 X 中所有键的数组位置,但当 XY 的键集相交时,值会被 Y 中的值覆盖。Y 中不相交的键对应的元素会被附加,保留它们的偏序。

转义

只有格式为 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的 Kubernetes 资源属性名称可以从 CEL 访问。可访问的属性名称在表达式中访问时根据以下规则进行转义

CEL 标识符转义规则表
转义序列属性名称等效项
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__CEL 保留关键字

转义 CEL 的任何 保留 关键字时,需要匹配确切的属性名称,使用下划线转义(例如,单词 sprint 中的 int 不需要转义)。

转义示例

转义的 CEL 标识符示例
属性名称带有转义属性名称的规则
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact_dself.redact__underscores__d > 0
stringself.startsWith('kube')

资源限制

CEL 是非图灵完备的,提供了各种生产安全控制来限制执行时间。CEL 的资源限制特性为开发者提供了关于表达式复杂度的反馈,并有助于保护 API 服务器在评估过程中免受过度资源消耗。CEL 的资源限制特性用于防止 CEL 评估消耗过多的 API 服务器资源。

资源限制特性的一个关键要素是 CEL 定义的成本单元,用于跟踪 CPU 利用率。成本单元与系统负载和硬件无关。成本单元也是确定性的;对于任何给定的 CEL 表达式和输入数据,CEL 解释器评估该表达式总是会产生相同的成本。

许多 CEL 的核心操作具有固定成本。最简单的操作,例如比较(例如 <)成本为 1。一些操作具有更高的固定成本,例如列表字面量声明具有 40 个成本单元的固定基础成本。

对以原生代码实现的函数的调用会根据操作的时间复杂度估算成本。例如:使用正则表达式的操作,例如 matchfind,使用 length(regexString)*length(inputString) 的近似成本进行估算。近似成本反映了 Go 的 RE2 实现的最坏情况时间复杂度。

运行时成本预算

Kubernetes 评估的所有 CEL 表达式都受限于运行时成本预算。运行时成本预算是通过在解释 CEL 表达式时递增成本单元计数器来估算的实际 CPU 利用率。如果 CEL 解释器执行的指令过多,就会超出运行时成本预算,表达式的执行将被中止,并产生错误。

一些 Kubernetes 资源定义了额外的运行时成本预算,限制多个表达式的执行。如果表达式的总成本超出预算,表达式的执行将被中止,并产生错误。例如,自定义资源的验证具有一个针对用于验证自定义资源的所有 验证规则每次验证运行时成本预算。

估算成本限制

对于某些 Kubernetes 资源,API 服务器还可能检查 CEL 表达式的最坏情况估算运行时间是否过于昂贵而无法执行。如果是这样,API 服务器会拒绝包含 CEL 表达式的创建或更新操作到 API 资源,从而阻止 CEL 表达式写入 API 资源。此特性提供了更强的保证,确保写入 API 资源的 CEL 表达式将在运行时进行评估,而不会超出运行时成本预算。

上次修改时间:2025 年 4 月 6 日 9:13 AM PST:重构 CEL 页面以使用 HTML 表格而不是 Markdown 表格 (9e578ee7ae)