3. Rego语言基础:Rego语法入门、规则、表达式、查询

Rego 是 OPA 的策略语言,专为在复杂云原生环境中表达可读、可组合的访问控制与合规规则而设计。本章从零开始,系统讲解 Rego 的核心语法、规则结构、表达式类型以及查询方式,为后续实现故障域划分的架构边界验证打下坚实基础。

3.1 Rego 语言核心设计理念

Rego 并非通用编程语言,而是一种声明式查询语言,其设计围绕三个核心理念:

理念 说明 与故障域验证的关系
声明式 描述“策略应该是什么”,而非“如何执行策略” 直接声明“Pod 必须与 Node 在同一可用区”,无需编写遍历逻辑
不可变数据 输入数据只读,规则只产生新值,无副作用 保证多次评估结果一致,适合审计与合规场景
基于文档模型 所有数据(输入、输出、规则)都是嵌套的 JSON 文档 天然适配 Kubernetes 资源清单、云服务商元数据等 JSON 格式
学习提示: 如果你有 SQL 或 XPath 经验,会发现 Rego 的声明式风格非常相似——你描述“想要什么结果”,引擎自动推导出如何得到它。

3.2 Rego 语法入门:包、导入与注释

每个 Rego 文件以 package 声明开始,用于组织策略模块。导入语句用于引入其他包或数据源。

package example.availability_zones

import data.kubernetes.pods
import data.cloud.provider.zones

# 这是一个注释,用于说明规则意图
# 检查 Pod 是否部署在允许的可用区中
语法元素 示例 说明
package package boundary.zone 定义当前模块的命名空间,用于规则引用
import import data.kubernetes 引入外部数据或子包,简化后续引用路径
# # 这是一行注释 单行注释,用于解释规则逻辑

3.3 规则:Rego 的核心构建块

规则是 Rego 的基本单元,分为完整规则和部分规则。每条规则定义一组条件,当条件满足时产生一个值。

3.3.1 完整规则

完整规则返回一个布尔值或标量,常用于定义全局策略开关或常量。

# 完整规则:返回布尔值
default allowed_zones := ["us-east-1a", "us-east-1b"]

# 完整规则:返回字符串
zone_separator := "-"

3.3.2 部分规则

部分规则返回一个集合或对象,通常用于过滤数据或生成结构化结果。

# 部分规则:返回所有符合条件 Pod 的集合
valid_pods[pod] {
    pod := input.pods[_]
    pod.metadata.labels.tier == "frontend"
}

# 部分规则:返回对象,键为 Pod 名称,值为所在可用区
pod_zone_map[pod_name] = zone {
    pod := input.pods[_]
    pod_name := pod.metadata.name
    zone := pod.spec.nodeSelector["topology.kubernetes.io/zone"]
}
关键区别: 完整规则使用 := 赋值,部分规则使用 [变量] 或 [键] = 值 形式,且必须包含至少一个迭代条件。

3.4 表达式:构建规则逻辑的基石

Rego 表达式用于在规则体内进行条件判断、数据访问和计算。以下是故障域验证中最常用的表达式类型:

表达式类型 语法示例 用途
相等/不相等 zone == "us-east-1a" / zone != "us-east-1c" 检查可用区是否匹配或排除
比较 count(replicas) > 2 验证副本数是否满足跨区要求
成员关系 zone in allowed_zones 检查可用区是否在允许列表中
迭代 pod := input.pods[_] 遍历 Pod 列表,逐项评估
聚合 count(distinct_zones) >= 2 统计不同可用区数量,确保跨区部署
逻辑与/或 zone == "a" ; zone == "b"(与) / zone == "a" | zone == "b"(或) 组合多个条件
# 综合表达式示例:验证 Pod 是否跨至少两个可用区
cross_zone_deployment {
    pods := input.pods
    zones := {zone | pod := pods[_]; zone := pod.spec.nodeSelector["topology.kubernetes.io/zone"]}
    count(zones) >= 2
}

3.5 查询:评估策略并获取结果

查询是 Rego 的执行入口,通过 opa eval 命令或 API 调用触发。查询可以针对完整规则、部分规则或任意表达式。

3.5.1 基本查询

# 查询完整规则
opa eval "data.example.availability_zones.allowed_zones"

# 查询部分规则,返回所有匹配 Pod
opa eval "data.example.availability_zones.valid_pods"

3.5.2 带输入数据的查询

# 使用 --input 传入 JSON 数据
opa eval --input deployment.json "data.boundary.zone.cross_zone_deployment"

# 查询结果示例
{
  "result": [
    {
      "expressions": [
        {
          "value": true,
          "text": "data.boundary.zone.cross_zone_deployment",
          "location": {"row": 1, "col": 1}
        }
      ]
    }
  ]
}

3.5.3 部分查询与绑定

# 查询并绑定变量,获取具体值
opa eval --input deployment.json "zone := data.boundary.zone.pod_zone_map[\"web-app-0\"]"

# 返回 zone 的具体值,如 "us-east-1a"
常见错误: 查询时忘记使用 data. 前缀引用规则。Rego 中所有规则和数据都位于 data 命名空间下,必须通过 data.<package>.<rule> 访问。

3.6 Rego 规则执行流程可视化

下图展示了 Rego 从输入到输出的完整执行流程,以及规则、表达式、查询之间的协作关系:

Rego 策略执行流程 输入数据 K8s 资源 / 云元数据 Rego 规则集 包 / 规则 / 表达式 查询引擎 opa eval / API 规则内部结构 完整规则:default allowed_zones 部分规则:valid_pods[pod] 表达式:zone in allowed_zones 输出结果 (JSON) 输入数据不可变,规则无副作用

3.7 故障域验证中的 Rego 模式

在故障域划分场景中,以下 Rego 模式会反复出现,建议提前掌握:

模式 Rego 实现 故障域验证场景
集合去重 zones := {zone | ...} 统计 Pod 分布在不同可用区的数量
键值映射 pod_zone[pod] = zone { ... } 建立 Pod 到可用区的映射关系
否定检查 not zone in forbidden_zones 确保 Pod 不在禁止的故障域中
聚合计数 count(zones) >= min_zones 验证跨区副本数是否满足最低要求
全量匹配 every pod in input.pods { condition } 确保所有 Pod 都满足故障域约束
# 全量匹配示例:每个 Pod 都必须有可用区标签
every pod in input.pods {
    pod.spec.nodeSelector["topology.kubernetes.io/zone"] != ""
}

3.8 本章小结