3. Rego 基础回顾:Rego 语法、规则定义、数据文档与查询
在深入 Helm 环境下的 Rego 策略性能基准测试之前,必须牢固掌握 Rego 语言的核心基础。Rego 是一种专为云原生环境设计的声明式查询语言,其设计哲学是“策略即代码”(Policy as Code)。本章将系统回顾 Rego 的语法结构、规则定义方式、数据文档模型以及查询执行机制,为后续的性能测试奠定坚实的语言基础。
3.1 Rego 语言核心语法
Rego 的语法受 Datalog 和 JSON 的启发,强调可读性与可组合性。其核心是规则(Rules)和查询(Queries),所有操作都围绕输入文档(input)和数据文档(data)展开。
3.1.1 基本数据类型与字面量
| 类型 | 示例 | 说明 |
|---|---|---|
| 标量 | 42, "hello", true, 3.14 |
数字、字符串、布尔值、浮点数 |
| 复合 | [1, 2, 3], {"a": 1} |
数组(有序)和对象(无序键值对) |
| 集合 | {"a", "b", "c"} |
无序、无重复元素的集合 |
| 变量 | x, username, _ |
以字母或下划线开头;_ 表示匿名变量 |
| 引用 | input.user.name, data.roles |
通过点号访问嵌套文档 |
3.1.2 表达式与操作符
Rego 支持丰富的表达式,用于构建策略逻辑:
- 比较操作符:
==(等于)、!=(不等于)、<、>、<=、>= - 逻辑操作符:
not(否定)、and(合取,默认逗号分隔)、or(析取,使用分号;或显式or) - 算术操作符:
+、-、*、/ - 成员操作符:
some(迭代)、in(成员检查) - 聚合函数:
count()、sum()、max()、min()、any()、all()
关键差异:Rego 中的
= 用于统一(unification),即赋值与模式匹配;== 仅用于判断相等性。在规则体中,通常使用 = 来绑定变量。
3.2 规则定义:从简单到复杂
规则是 Rego 策略的基本构建块。每条规则定义了一个逻辑断言,当规则体中的所有条件都满足时,规则头部的表达式为真。
3.2.1 规则分类
| 规则类型 | 语法特征 | 返回值 | 典型用途 |
|---|---|---|---|
| 完整规则 | r = value { body } |
单个标量或对象 | 计算单一结果,如最大权限等级 |
| 部分规则 | r[key] = value { body } |
键值对集合 | 构建映射或索引,如用户角色映射 |
| 布尔规则 | r { body } |
true / false |
定义授权决策,如 allow |
| 增量规则 | 多个同名规则体 | 集合或对象的并集 | 分条件定义策略,如多来源白名单 |
3.2.2 规则定义示例
# 布尔规则:允许访问
allow {
input.user.role == "admin"
}
# 完整规则:计算最高权限
max_permission := "read" {
input.user.role == "viewer"
} else := "write" {
input.user.role == "editor"
} else := "admin" {
input.user.role == "admin"
}
# 部分规则:构建命名空间级别的资源映射
allowed_resources[ns] = resources {
some ns
resources := data.namespace_permissions[ns]
input.user.groups[_] == data.namespace_owners[ns][_]
}
# 增量规则:多个条件合并
deny {
input.request.method == "DELETE"
input.request.path == "/api/v1/secrets"
}
deny {
input.request.user == "anonymous"
}
性能提示:在 Helm 环境下,增量规则(多个同名规则)会被 OPA 引擎自动合并。当规则数量超过 100 条时,建议使用
some 迭代替代多个独立规则体,以减少规则编译开销。
3.3 数据文档与查询模型
Rego 的查询模型基于文档(Documents)。OPA 引擎将输入(input)和外部数据(data)视为不可变的 JSON 文档,策略规则则是对这些文档的声明式查询。
3.3.1 文档层次结构
下图展示了 Rego 查询中涉及的文档层次关系:
3.3.2 查询执行流程
Rego 查询的执行遵循以下步骤:
- 输入绑定:OPA 将外部请求(如 Kubernetes Admission Review)绑定到
input文档。 - 数据加载:从外部数据源(如 Helm values、ConfigMap、Bundle)加载
data文档。 - 规则评估:引擎遍历所有规则,对每个规则体进行逻辑统一和模式匹配。
- 结果聚合:将所有满足条件的规则结果合并为最终的 JSON 输出。
- 缓存与索引:OPA 自动对规则结果进行缓存,并建立索引以加速重复查询。
性能陷阱:在 Helm 环境下,如果
data 文档中包含大量嵌套结构(如超过 10 层),OPA 的引用解析时间会呈指数级增长。建议将数据扁平化,或使用 object.get() 函数进行安全访问。
3.4 内置函数与聚合操作
Rego 提供了丰富的内置函数,用于处理字符串、数组、对象、时间等常见操作。在性能基准测试中,以下函数的使用频率最高:
| 函数 | 签名 | 性能影响 |
|---|---|---|
count |
count(collection) |
O(n),对大型数组需注意 |
any |
any(array, predicate) |
短路求值,推荐用于存在性检查 |
all |
all(array, predicate) |
全量遍历,大数据集慎用 |
filter |
filter(array, predicate) |
返回新数组,内存开销较大 |
object.get |
object.get(obj, key, default) |
O(1),比点号访问更安全 |
regex.match |
regex.match(pattern, value) |
正则编译开销,建议预编译 |
3.5 常见模式与反模式
在 Helm 策略编写中,以下模式直接影响性能:
推荐模式:
- 使用
some显式声明迭代变量,避免隐式迭代。 - 将频繁访问的嵌套路径提取为局部变量,减少重复引用。
- 使用
object.get()替代链式点号访问,避免 nil 引用错误。 - 对静态数据使用
data文档,对动态请求使用input文档。
反模式:
- 在规则体内重复计算相同的聚合结果(如多次
count())。 - 使用
_匿名变量进行全量集合匹配,导致笛卡尔积爆炸。 - 在
input中嵌入大型静态数据,应将其移至data文档。 - 过度使用
regex.match,尤其是在热路径上。