2. Rego语言基础:Rego语法概览、规则定义、查询与数据模型
Rego 是 Open Policy Agent (OPA) 的策略语言,专为在云原生环境中表达策略而设计。它脱胎于 Datalog,是一种声明式查询语言。理解 Rego 的语法、规则定义方式、查询机制以及数据模型,是编写有效策略即代码(Policy as Code)的基石。本章将系统性地介绍这些核心概念,为后续与 Helm Chart 的集成实战打下坚实基础。
2.1 Rego语法概览
Rego 的语法简洁且高度可读,其核心思想是“将策略视为对数据的查询”。一个 Rego 文件(通常以 .rego 为后缀)由 包声明、导入语句 和 规则定义 组成。
- 声明式: 你描述“想要什么”,而非“如何计算”。
- 基于文档模型: 输入(
input)和数据(data)都是嵌套的 JSON 文档。 - 不可变数据: 变量一旦被赋值,在同一作用域内不可更改。
- 迭代与推导: 使用
some关键字进行迭代,使用推导式(Comprehensions)构建集合。
| 语法元素 | 示例 | 说明 |
|---|---|---|
| 包声明 | package kubernetes.admission |
定义规则所属的命名空间,用于组织策略。 |
| 导入 | import data.kubernetes.pods |
导入其他包中的数据或规则,简化引用路径。 |
| 注释 | # 这是一条注释 |
单行注释,用于解释策略逻辑。 |
| 标量值 | "nginx", 80, true |
字符串、数字、布尔值。 |
| 复合值 | {"name": "app", "port": 8080} |
对象(Object)、数组(Array)、集合(Set)。 |
| 变量 | deployment_name |
以字母或下划线开头,用于绑定值。 |
| 引用 | input.request.operation |
通过点号或方括号访问嵌套文档中的值。 |
| 数组推导式 | [x | x := data.containers[_]; x.image] |
从集合中筛选并转换数据,生成新数组。 |
2.2 规则定义:策略的核心
规则是 Rego 的基本构建块。一个规则定义了一个布尔值、一个值或一个集合。规则可以是有条件的,也可以是无条件的。
2.2.1 规则类型
- 完整规则(Complete Rules): 为规则名赋予一个单一值。如果规则体为真,则规则为真;否则为假(或未定义)。
- 部分规则(Partial Rules): 定义一个从键到值的映射,类似于一个函数或字典。常用于定义允许的操作列表或配置映射。
- 默认规则(Default Rules): 为规则提供一个默认值,当其他规则体都不满足时使用,避免未定义错误。
# 完整规则:判断是否允许部署
package example
default allow = false # 默认拒绝
allow = true { # 如果满足以下条件,则允许
input.request.operation == "CREATE"
input.request.kind == "Deployment"
input.request.name == "allowed-app"
}
# 部分规则:定义允许的镜像仓库列表
allowed_registries = {"docker.io/verified", "gcr.io/my-company"}
# 部分规则:检查镜像是否来自允许的仓库
valid_image(image) = true {
registry := split(image, "/")[0]
allowed_registries[registry]
}
# 完整规则:结合部分规则进行决策
allow = true {
input.request.operation == "CREATE"
valid_image(input.request.object.spec.template.spec.containers[_].image)
}
2.2.2 规则体与表达式
规则体由一系列表达式组成,表达式之间是逻辑与(AND)关系。如果所有表达式都为真,则规则体为真。表达式可以是:
- 等式:
x = 5或x == 5。在 Rego 中,=用于赋值和比较,==仅用于比较。 - 不等式:
x != 5 - 比较:
x > 3,y <= 10 - 成员关系:
x = ["a", "b"][_]或"a" in ["a", "b"] - 函数调用:
count(list) > 0,split(image, "/")
2.3 查询与数据模型
Rego 的策略评估本质上是一个查询过程。OPA 将输入文档(input)和外部数据(data)加载到内存中,然后执行 Rego 查询,返回结果。
2.3.1 数据模型:input 与 data
理解 input 和 data 的层级结构是编写策略的前提。
input: 代表当前请求的上下文。在 Kubernetes 准入控制场景中,它通常是 AdmissionReview 对象的 JSON 表示,包含request.operation、request.object(被操作的资源)等字段。data: 代表 OPA 加载的所有外部数据。这包括:- 通过
opa run -d加载的 JSON 或 YAML 文件。 - 通过 Bundle API 分发的策略包和数据。
- 通过
data.kubernetes.*路径访问的 Kubernetes 集群状态(当 OPA 与 Kubernetes 集成时)。
- 通过
2.3.2 查询示例
假设我们有一个简单的 Rego 策略,用于检查 Deployment 的副本数是否大于 3:
package example
# 规则:检查副本数是否合规
replica_count_valid = true {
input.replicas > 3
}
当我们向 OPA 发送查询请求时,需要提供 input 文档:
# 查询请求 (JSON)
{
"input": {
"replicas": 5
}
}
# 查询结果 (JSON)
{
"result": {
"example/replica_count_valid": true
}
}
如果 input.replicas 为 2,则规则体不满足,查询结果中 example/replica_count_valid 将为 false(或未定义,取决于规则定义)。
2.4 迭代与推导式
在处理 Kubernetes 资源时,经常需要遍历数组或对象。Rego 提供了强大的迭代机制。
2.4.1 使用 some 关键字
some 关键字用于声明局部变量,并用于迭代集合中的元素。
package example
# 检查所有容器镜像是否来自允许的仓库
allow = true {
some container
container := input.containers[_] # 迭代 containers 数组
startswith(container.image, "docker.io/verified/")
}
2.4.2 推导式(Comprehensions)
推导式允许你从一个集合中构建一个新的集合(数组、对象或集合)。这在数据转换和过滤时非常有用。
package example
# 获取所有容器的镜像名称列表
container_images = [image |
some container
container := input.containers[_]
image := container.image
]
# 获取所有使用 "latest" 标签的容器名称
latest_containers = {name |
some container
container := input.containers[_]
container.image == "nginx:latest"
name := container.name
}
2.5 内置函数与聚合
Rego 提供了丰富的内置函数,用于字符串处理、正则匹配、时间操作、加密等。聚合函数如 count、sum、max、min 等,常用于对集合进行统计分析。
| 函数类别 | 常用函数 | 用途示例 |
|---|---|---|
| 字符串 | startswith, endswith, contains, regex.match |
检查镜像名称是否以特定前缀开头。 |
| 聚合 | count, sum, max, min, any, all |
统计 Deployment 中容器的数量,或检查所有容器是否都设置了资源限制。 |
| 集合 | union, intersection, difference |
合并多个允许的端口列表,或找出禁止的端口。 |
| 编码 | base64.decode, json.marshal, yaml.unmarshal |
解码 Secret 中的 Base64 数据,或解析 YAML 格式的注解。 |
2.6 本章小结
本章涵盖了 Rego 语言的核心基础:
- 语法概览: 包、导入、注释、标量与复合值、变量与引用。
- 规则定义: 完整规则、部分规则、默认规则,以及规则体中的表达式逻辑。
- 查询与数据模型:
input和data的层级结构,以及如何通过查询获取策略决策。 - 迭代与推导式: 使用
some和推导式高效处理集合数据。 - 内置函数: 利用丰富的函数库简化策略逻辑。
掌握这些基础后,你将能够阅读和编写基本的 Rego 策略。在下一阶段,我们将把这些知识应用到 Helm Chart 的集成中,实现真正的策略即代码部署。