3. Rego 语言入门:语法基础、规则定义、查询与数据绑定

Rego 是 OPA 的策略描述语言,专为在复杂嵌套数据上表达策略约束而设计。与通用编程语言不同,Rego 基于逻辑推理和模式匹配,其核心思想是“将策略视为数据查询”。本章从最基础的语法结构开始,逐步构建对规则定义、查询执行和数据绑定机制的理解。

3.1 Rego 语法基础

Rego 文件由 包声明、导入语句 和 规则定义 组成。其语法风格受 Prolog 和 JSON 的启发,强调声明式而非命令式。

3.1.1 包与导入

  • 包(package):每个 Rego 文件以 package <path> 开头,用于组织规则命名空间,避免冲突。
  • 导入(import):使用 import data.<path> 引入其他包的数据或规则;使用 import input 引入 OPA 的输入数据。
package kubernetes.admission

import data.kubernetes.namespaces
import input.request.object as pod

3.1.2 字面量与注释

类型 示例 说明
字符串 "production" 双引号包裹
数字 42, 3.14 整数和浮点数
布尔 true, false 小写
数组 [1, 2, 3] 有序集合
对象 {"key": "value"} 键值对,键可为字符串或变量
集合 {"a", "b"} 无序唯一值
注释 # 这是一条注释 单行,以 # 开头

3.2 规则定义:从简单到完整

规则是 Rego 的基本构建块。每条规则定义一个布尔值、一个标量值或一个复合数据结构。规则可以是有条件的,也可以是无条件的。

3.2.1 布尔规则

最简单的规则返回 true 或 false。当规则体中的所有表达式都满足时,规则为真。

# 如果命名空间标签包含 "env: production",则视为生产环境
package example

production_ns {
    input.metadata.labels.env == "production"
}

3.2.2 完整规则 vs 部分规则

类型 语法 返回值 示例
完整规则 r = <value> { ... } 单个标量或对象 max_replicas = 10 { true }
部分规则 r[<key>] = <value> { ... } 生成键值对集合 allowed_ports[port] { ... }
布尔规则 r { ... } 隐式 true is_admin { input.role == "admin" }
# 完整规则:返回一个对象
allowed_images = {"nginx": "1.21", "redis": "6.2"} { true }

# 部分规则:动态生成集合
allowed_containers[container] {
    container := input.containers[_]
    container.image != "latest"
}

3.2.3 默认值

使用 default 关键字为规则提供回退值,避免规则未定义导致的查询失败。

default allow = false

allow {
    input.user == "admin"
}

3.3 查询与数据绑定

OPA 通过查询引擎执行 Rego 规则。查询可以针对 input(请求数据)和 data(内置数据或外部加载的数据)进行。数据绑定是 Rego 将变量与具体值匹配的过程。

3.3.1 查询执行流程

关键概念: OPA 的查询引擎使用回溯算法遍历规则体中的表达式。当遇到变量时,引擎尝试从 input 或 data 中寻找匹配的值;如果找不到,则变量绑定失败,该规则路径被剪枝。
Rego 查询与数据绑定流程 查询请求 (input) 数据源 (data) Rego 规则引擎 回溯 + 模式匹配 变量绑定与统一 策略决策 变量未绑定时 规则路径被剪枝

3.3.2 变量绑定示例

以下规则展示了变量 ns 如何从 input 中绑定值,并用于后续条件判断。

package example

# 变量 ns 绑定到 input.metadata.namespace
allow {
    ns := input.metadata.namespace
    ns != "kube-system"
    input.metadata.labels.owner == "team-a"
}

3.3.3 迭代与集合绑定

使用 some 关键字或下划线 _ 进行迭代,将数组或集合中的元素绑定到变量。

# 检查 Pod 是否使用了任何不被允许的镜像
violation[msg] {
    container := input.spec.containers[_]
    container.image == "nginx:latest"
    msg := sprintf("容器 %v 使用了 latest 标签", [container.name])
}
性能提示: 在 K8s 准入控制场景中,input.spec.containers[_] 会遍历所有容器。如果集群中大量 Pod 包含多个容器,建议在规则中尽早过滤(例如先检查命名空间),减少不必要的迭代。

3.4 内置函数与聚合

Rego 提供丰富的内置函数,用于字符串处理、集合操作、时间解析等。在 K8s 策略中,最常用的包括:

  • count():计算集合或数组长度
  • split():字符串分割
  • sprintf():格式化字符串
  • regex.match():正则匹配
  • time.now_ns():获取当前时间戳
# 检查 Pod 的 hostAliases 数量是否超过限制
too_many_aliases {
    count(input.spec.hostAliases) > 5
}

# 验证镜像仓库地址
valid_registry {
    regex.match(`^registry\.example\.com/`, input.spec.containers[_].image)
}

3.5 常见陷阱与避坑指南

陷阱 错误示例 正确做法
变量未定义 input.x == y(y 未绑定) 使用 y := input.x 先绑定
规则覆盖 两个同名规则同时为真 使用 else 或 not 明确分支
误用 = x = 1 用于赋值 使用 := 进行局部赋值
忽略默认值 规则未定义时查询报错 添加 default allow = false

3.6 本章小结

Rego 的语法虽然简洁,但其声明式逻辑和变量绑定机制需要一定的思维转换。掌握包结构、规则类型(完整/部分/布尔)、以及通过迭代进行数据绑定,是编写高效 K8s 策略的基础。下一阶段将把这些基础规则组合成完整的准入控制策略,并开始关注规则复杂度对 API 延迟的影响。