Kubernetes 中的通用表达式语言

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

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

    CEL 的设计目的是嵌入应用程序中。每个 CEL “程序” 都是一个单独的表达式,其评估结果为单个值。 CEL 表达式通常是短小的 “一行式”,可以轻松嵌入到 Kubernetes API 资源的字符串字段中。

    对 CEL 程序的输入是各种 “变量”。包含 CEL 的每个 Kubernetes API 字段都在 API 文档中声明了字段可使用哪些变量。例如,在 CustomResourceDefinitions 的 字段中,selfoldSelf 变量可用, 并且分别指代要由 CEL 表达式验证的自定义资源数据的前一个状态和当前状态。 其他 Kubernetes API 字段可能声明不同的变量。请查阅 API 字段的 API 文档以了解该字段可使用哪些变量。

    CEL 表达式示例:

    CEL 社区库

    Kubernetes CEL 表达式能够访问以下 CEL 社区库:

    除了 CEL 社区库之外,Kubernetes 还包括在 Kubernetes 中使用 CEL 时所有可用的 CEL 库。

    列表库包括 indexOflastIndexOf,这两个函数的功能类似于同名的字符串函数。 这些函数返回提供的元素在列表中的第一个或最后一个位置索引。

    列表库还包括 minmaxsumsum 可以用于所有数字类型以及持续时间类型。 minmax 可用于所有可比较的类型。

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

    例如:

    使用列表库函数的 CEL 表达式例子
    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 函数外,正则表达式库还提供了 findfindAll, 使得更多种类的正则表达式运算成为可能。

    例如:

    更多信息请查阅 Go 文档: Kubernetes 正则表达式库

    为了更轻松、更安全地处理 URL,添加了以下函数:

    • isURL(string) 按照 Go 的 net/url 检查字符串是否是一个有效的 URL。该字符串必须是一个绝对 URL。
    • 将字符串转换为 URL,如果字符串不是一个有效的 URL,则返回错误。

    一旦通过 url 函数解析,所得到的 URL 对象就具有 getSchemegetHostgetHostnamegetPortgetEscapedPathgetQuery 访问器。

    例如:

    使用 URL 库函数的 CEL 表达式例子
    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-typesetmap 的数组进行相等比较时会忽略元素顺序。 例如,如果这些数组代表 Kubernetes 的 set 值,则 [1, 2] == [2, 1]

    使用 x-kubernetes-list-type 的数组进行串接时,使用 list 类型的语义:

    • mapX + Y 执行合并操作,保留 X 中所有键的数组位置, 但是当 XY 的键集相交时,将 Y 中的值覆盖 X 中的值。 将 Y 中非交集键的元素附加到 X 中,保留它们的部分顺序。

    转义

    仅形如 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的 Kubernetes 资源属性名可以从 CEL 中访问。 当在表达式中访问可访问的属性名时,会根据以下规则进行转义:

    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 资源定义了额外的运行时成本预算,用于限制多个表达式的执行。 如果所有表达式的成本总和超过预算,表达式的执行将停止,并将出现错误。 例如,自定义资源的验证具有针对验证自定义资源所评估的所有 的 每个验证 运行时成本预算。