3. OPA 基础回顾:Rego 语言入门、Policy 结构、Data 与 Input 概念
在深入 Helm 与 OPA 集成之前,必须牢固掌握 OPA 的核心——Rego 语言。Rego 是一种为云原生策略引擎设计的声明式查询语言,其设计灵感来源于 Datalog。本章将快速梳理 Rego 的核心概念,包括策略结构、data 与 input 的区分,以及如何编写可测试的策略规则。
3.1 Rego 语言核心特征
| 特征 | 说明 | 与通用语言(如 Python/Go)的差异 |
|---|---|---|
| 声明式 | 只描述“策略应该是什么”,不描述“如何执行” | 无循环、无变量赋值(变量只绑定一次) |
| 基于 Datalog | 规则由一组逻辑子句组成,支持递归与聚合 | 无副作用,所有规则都是纯函数 |
| 嵌套对象查询 | 使用点号(.)和方括号([])访问嵌套 JSON |
类似 JavaScript 对象路径,但不可变 |
| 内置聚合 | count、sum、any、all 等 |
无需外部库,原生支持集合操作 |
关键记忆点: Rego 中没有“if-else”语句,而是通过规则匹配和默认值(
default)实现条件分支。所有规则要么为真(true),要么未定义(undefined),要么返回具体值。
3.2 Policy 结构:包、规则与元数据
一个标准的 OPA 策略文件(.rego)由三个基本层次构成:
- 包声明(Package):定义策略的命名空间,避免规则冲突。
- 导入语句(Import):引入
data或input中的子路径,简化规则书写。 - 规则定义(Rule):核心逻辑,分为完整规则(返回单个值)和部分规则(返回集合)。
package kubernetes.admission
import data.kubernetes.namespaces
# 完整规则:返回布尔值
default allow = false
# 部分规则:返回一个集合
violations[msg] {
input.request.kind.kind == "Pod"
not input.request.object.metadata.namespace == "default"
msg := sprintf("Pod %v 不允许部署在非 default 命名空间", [input.request.object.metadata.name])
}
规则类型速查:
- 完整规则:
allow = true { ... }—— 返回一个标量值。 - 部分规则:
violations[msg] { ... }—— 返回一个集合(数组或对象)。 - 函数规则:
greet(name) = "hello " + name—— 带参数的规则,可复用。
3.3 Data 与 Input 概念
这是 OPA 策略执行中最容易混淆的两个概念。理解它们的区别是编写正确策略的前提。
| 维度 | data |
input |
|---|---|---|
| 来源 | 外部加载的静态/动态数据(如集群资源清单、配置 JSON) | 每次查询时由调用方传入的请求上下文 |
| 生命周期 | 持久化,跨多次查询保持不变(除非重新加载) | 瞬态,仅存在于单次查询中 |
| 典型用途 | 白名单、黑名单、命名空间元数据、RBAC 规则 | 准入 Webhook 请求体、API 调用参数、用户身份 |
| 访问方式 | data.<package>.<path> |
input.<path> |
常见陷阱: 在 Helm 集成场景中,
data 通常用于存储集群的基线配置(如允许的镜像仓库列表),而 input 则代表 Helm 渲染后的资源对象。切勿将 input 中的字段误认为 data 中的全局变量,否则会导致策略在不同环境下行为不一致。
3.4 Rego 策略执行流程(SVG 图)
下图展示了 OPA 在接收到查询请求后,如何将 input 与 data 结合,通过规则引擎生成决策结果。
3.5 编写第一个 Rego 策略:准入控制示例
以下是一个完整的 Kubernetes 准入控制策略,用于检查 Deployment 是否设置了资源限制。该策略同时使用了 input(请求对象)和 data(全局配置)。
package kubernetes.resources
import data.kubernetes.allowed_registries
# 默认拒绝
default deny = false
# 规则1:检查资源限制
deny {
input.request.kind.kind == "Deployment"
container := input.request.object.spec.template.spec.containers[_]
not container.resources.limits
not container.resources.requests
}
# 规则2:检查镜像来源
deny {
input.request.kind.kind == "Deployment"
container := input.request.object.spec.template.spec.containers[_]
image := container.image
not startswith(image, data.kubernetes.allowed_registries[_])
}
# 规则3:生成详细违规信息
violations[msg] {
deny
msg := sprintf("Deployment %v 违反策略:%v", [
input.request.object.metadata.name,
"缺少资源限制或镜像来源不在白名单中"
])
}
测试策略: 使用
opa eval 命令可以快速验证策略逻辑:
# 模拟 input 文件
opa eval --data policy.rego --input input.json "data.kubernetes.resources.deny"
# 查看所有违规
opa eval --data policy.rego --input input.json "data.kubernetes.resources.violations"
3.6 Rego 常见模式与避坑指南
- 迭代模式:使用
some i或_通配符遍历数组/集合。例如container := input.containers[_]。 - 否定模式:使用
not关键字检查条件不成立。注意not只能用于规则体内部,不能用于顶层。 - 聚合模式:
count(violations) > 0判断是否有违规,all(some_condition)检查所有元素是否满足条件。 - 避坑:变量作用域:Rego 中变量一旦绑定就不能重新赋值。如果需要“或”逻辑,使用多条同名规则(OR 语义)。
- 避坑:未定义 vs false:规则未定义(undefined)不等于
false。在策略中应显式使用default声明默认值。
3.7 本章小结
通过本章回顾,你应该能够:
- 区分 Rego 中的完整规则与部分规则。
- 明确
data与input在策略执行中的角色差异。 - 编写基础的 Kubernetes 准入控制策略。
- 使用
opa eval进行本地策略验证。