PAYATHON 2026

如何在 Go 中本地评估 Kubernetes CEL 表达式?

数据小许

结论

在 Go 程序中,可以使用 k8s.io/apiserver/pkg/cel/environment 的 EnvSet 创建 Kubernetes 基础 CEL 环境。它会配置 Kubernetes 共用的 CEL 函数库、语言设置和运行时选项,但不会声明 object 等变量,也不会确定这些变量对应的资源类型。因此,表达式不能像调用 regexp.Compile 那样,只传入一个字符串就获得完整的 admission policy 上下文。Kubernetes CEL environment 文档 Kubernetes CEL 函数库

下面的示例将 object 声明为动态类型,可用于评估基于 unstructured.Unstructured.Object 一类 map 数据的简单表达式:

package main

import (
	"fmt"
	"log"

	"github.com/google/cel-go/cel"
	"k8s.io/apiserver/pkg/cel/environment"
)

func main() {
	compat := environment.DefaultCompatibilityVersion()
	base := environment.MustBaseEnvSet(compat)

	// 基础环境提供 Kubernetes CEL 库;object 变量仍需由应用声明。
	envSet, err := base.Extend(environment.VersionedOptions{
		IntroducedVersion: compat,
		EnvOptions: []cel.EnvOption{
			cel.Variable("object", cel.DynType),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	env := envSet.NewExpressionsEnv()

	// 配置加载时编译一次,处理资源时重复使用 program。
	ast, issues := env.Compile(
		`!object.spec.initContainers.exists(ic, ic.name == "mesh-proxy")`,
	)
	if issues != nil && issues.Err() != nil {
		log.Fatal(issues.Err())
	}
	program, err := env.Program(ast)
	if err != nil {
		log.Fatal(err)
	}

	object := map[string]any{
		"spec": map[string]any{
			"initContainers": []any{
				map[string]any{"name": "app-init"},
			},
		},
	}
	result, _, err := program.Eval(map[string]any{"object": object})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result) // true
}

变量和资源类型

示例将 object 声明为 cel.DynType,适合处理动态 map,但静态类型检查能力有限。若要更接近 Kubernetes admission policy 的类型检查,可以根据资源的 OpenAPI schema 创建 DeclType,再将类型和变量声明加入 EnvSet.Extend。表达式也可能需要 oldObject、request、params 等上下文变量;评估时,传给 Eval 的 map 中也要提供相应的值。MustBaseEnvSet 只设置基础环境,不会自动构建这些 admission 上下文。EnvSet 类型与扩展选项说明

使用时注意

  • 在应用启动时初始化环境,并缓存编译后的 program 以便复用。每处理一个资源都重新创建环境、编译表达式会增加开销;cel-go 也建议在配置不变时复用环境。cel-go 编译与评估说明
  • k8s.io/apiserver、k8s.io/apimachinery 等 Kubernetes 模块应使用相互匹配的版本,CEL 环境版本也应与目标集群兼容。StoredExpressionsEnv 用于已存储的表达式;新配置表达式通常使用适用的 NewExpressionsEnv。
  • Kubernetes CEL 函数库提供 quantity 函数和 Quantity 方法。表达式中的 object 字段路径仍由你提供的数据和声明决定。示例配置中的 CPU 表达式末尾多了一个 ];indexOf 则需要以列表作为接收者,不能单独写成 indexOf('my-container')。Kubernetes Quantity CEL 函数说明
  • 在本地使用相同的基础库,不会自动复现 API server 的全部行为。要获得一致结果,变量、资源 schema、兼容版本和求值输入都需要符合目标场景。

备注:内容仅供参考。