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 查询中涉及的文档层次关系:

Rego 数据文档与查询模型 Rego 数据文档与查询模型 input 文档 input.user.name input.user.role input.request.method input.request.path data 文档 data.roles.admin data.namespaces data.policies.deny_list data.cluster.config Rego 规则引擎 规则编译 → 查询执行 → 结果聚合 查询结果 allow: true max_permission: "admin"

3.3.2 查询执行流程

Rego 查询的执行遵循以下步骤:

  1. 输入绑定:OPA 将外部请求(如 Kubernetes Admission Review)绑定到 input 文档。
  2. 数据加载:从外部数据源(如 Helm values、ConfigMap、Bundle)加载 data 文档。
  3. 规则评估:引擎遍历所有规则,对每个规则体进行逻辑统一和模式匹配。
  4. 结果聚合:将所有满足条件的规则结果合并为最终的 JSON 输出。
  5. 缓存与索引: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,尤其是在热路径上。

3.6 本章小结