Kubernetes 中的通用表达式语言
CEL 表达式在API 服务器中直接进行评估, 这使得 CEL 成为许多可扩展性用例的便捷替代方案,而无需使用类似 Webhook 这种进程外机制。 只要控制平面的 API 服务器组件保持可用状态,你的 CEL 表达式就会继续执行。
CEL 语言的语法直观简单, 类似于 C、C++、Java、JavaScript 和 Go 中的表达式。
CEL 的设计目的是嵌入应用程序中。每个 CEL “程序” 都是一个单独的表达式,其评估结果为单个值。 CEL 表达式通常是短小的 “一行式”,可以轻松嵌入到 Kubernetes API 资源的字符串字段中。
对 CEL 程序的输入是各种 “变量”。包含 CEL 的每个 Kubernetes API 字段都在 API 文档中声明了字段可使用哪些变量。例如,在 CustomResourceDefinitions 的 字段中,self
和 oldSelf
变量可用, 并且分别指代要由 CEL 表达式验证的自定义资源数据的前一个状态和当前状态。 其他 Kubernetes API 字段可能声明不同的变量。请查阅 API 字段的 API 文档以了解该字段可使用哪些变量。
CEL 表达式示例:
CEL 社区库
Kubernetes CEL 表达式能够访问以下 CEL 社区库:
除了 CEL 社区库之外,Kubernetes 还包括在 Kubernetes 中使用 CEL 时所有可用的 CEL 库。
列表库包括 indexOf
和 lastIndexOf
,这两个函数的功能类似于同名的字符串函数。 这些函数返回提供的元素在列表中的第一个或最后一个位置索引。
列表库还包括 min
、max
和 sum
。 sum
可以用于所有数字类型以及持续时间类型。 min
和 max
可用于所有可比较的类型。
isSorted
也作为一个便捷的函数提供,并且支持所有可比较的类型。
例如:
CEL 表达式 | 用途 |
---|---|
names.isSorted() | 验证名称列表是否按字母顺序排列 |
items.map(x, x.weight).sum() == 1.0 | 验证对象列表的 “weight” 总和为 1.0 |
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min() | 验证两组优先级不重叠 |
names.indexOf(‘should-be-first’) == 1 | 如果是特定值,则使用列表中的第一个名称 |
除了 CEL 标准库提供的 matches
函数外,正则表达式库还提供了 find
和 findAll
, 使得更多种类的正则表达式运算成为可能。
例如:
更多信息请查阅 Go 文档: Kubernetes 正则表达式库。
为了更轻松、更安全地处理 URL,添加了以下函数:
isURL(string)
按照 Go 的 net/url 检查字符串是否是一个有效的 URL。该字符串必须是一个绝对 URL。- 将字符串转换为 URL,如果字符串不是一个有效的 URL,则返回错误。
一旦通过 url
函数解析,所得到的 URL 对象就具有 getScheme
、getHost
、getHostname
、getPort
、getEscapedPath
和 getQuery
访问器。
例如:
CEL 表达式 | 用途 |
---|---|
url(‘) | 获取 URL 的 ‘example.com:80’ 主机部分 |
url(‘https://example.com/path with spaces/‘).getEscapedPath() | 返回 ‘/path%20with%20spaces/‘ |
更多信息请查阅 Go 文档: 。
类型检查
CEL 是一种。
一些 Kubernetes API 字段包含完全经过类型检查的 CEL 表达式。 例如,CustomResourceDefinitions 验证规则就是完全经过类型检查的。
一些 Kubernetes API 字段包含部分经过类型检查的 CEL 表达式。 部分经过类型检查的表达式是指一些变量是静态类型,而另一些变量是动态类型的表达式。 例如在 的 CEL 表达式中,request
变量是有类型的,但 object
变量是动态类型的。 因此,包含 request.namex
的表达式将无法通过类型检查,因为 namex
字段未定义。 然而,即使对于 object
所引用的资源种类没有定义 namex
字段, object.namex
也会通过类型检查,因为 object
是动态类型。
在 CEL 中,has()
宏可用于检查动态类型变量的字段是否可访问,然后再尝试访问该字段的值。 例如:
x-kubernetes-list-type
为 set
或 map
的数组进行相等比较时会忽略元素顺序。 例如,如果这些数组代表 Kubernetes 的 set
值,则 [1, 2] == [2, 1]
。
使用 x-kubernetes-list-type
的数组进行串接时,使用 list 类型的语义:
map
:X + Y
执行合并操作,保留X
中所有键的数组位置, 但是当X
和Y
的键集相交时,将Y
中的值覆盖X
中的值。 将Y
中非交集键的元素附加到X
中,保留它们的部分顺序。
转义
仅形如 [a-zA-Z_.-/][a-zA-Z0-9_.-/]*
的 Kubernetes 资源属性名可以从 CEL 中访问。 当在表达式中访问可访问的属性名时,会根据以下规则进行转义:
转义序列 | 等价的属性名 |
---|---|
underscores |
|
dot | . |
dash | - |
slash | / |
{keyword}__ | CEL 保留的 关键字 |
当你需要转义 CEL 的任一 保留的 关键字时,你需要使用下划线转义来完全匹配属性名 (例如,sprint
这个单词中的 int
不会被转义,也不需要被转义)。
转义示例:
CEL 不是图灵完备的,提供了多种生产安全控制手段来限制执行时间。 CEL 的资源约束特性提供了关于表达式复杂性的反馈,并帮助保护 API 服务器免受过度的资源消耗。 CEL 的资源约束特性用于防止 CEL 评估消耗过多的 API 服务器资源。
资源约束特性的一个关键要素是 CEL 定义的成本单位,它是一种跟踪 CPU 利用率的方式。 成本单位独立于系统负载和硬件。成本单位也是确定性的;对于任何给定的 CEL 表达式和输入数据, 由 CEL 解释器评估表达式将始终产生相同的成本。
CEL 的许多核心运算具有固定成本。例如比较(例如 <
)这类最简单的运算成本为 1。 有些运算具有更高的固定成本,例如列表字面声明具有 40 个成本单位的固定基础成本。
调用本地代码实现的函数时,基于运算的时间复杂度估算其成本。 举例而言:match
和 这类使用正则表达式的运算使用 length(regexString)*length(inputString)
的近似成本进行估算。 这个近似的成本反映了 Go 的 RE2 实现的最坏情况的时间复杂度。
所有由 Kubernetes 评估的 CEL 表达式都受到运行时成本预算的限制。 运行时成本预算是通过在解释 CEL 表达式时增加成本单元计数器来计算实际 CPU 利用率的估算值。 如果 CEL 解释器执行的指令太多,将超出运行时成本预算,表达式的执行将停止,并将出现错误。
一些 Kubernetes 资源定义了额外的运行时成本预算,用于限制多个表达式的执行。 如果所有表达式的成本总和超过预算,表达式的执行将停止,并将出现错误。 例如,自定义资源的验证具有针对验证自定义资源所评估的所有 的 每个验证 运行时成本预算。