2. OPA 与 Rego 基础:OPA 架构、Rego 语言核心概念、策略即代码的理念
本章是整门课程的基石。在深入 Kubernetes 容量规划与巡检自动化之前,必须理解 OPA(Open Policy Agent)的架构定位、Rego 语言的表达方式,以及“策略即代码”(Policy as Code)这一核心理念如何改变云原生环境下的治理模式。
2.1 策略即代码(Policy as Code)的理念
传统策略管理依赖人工审核、静态配置文件或硬编码在业务逻辑中的条件判断。这种方式在微服务和 Kubernetes 动态环境中暴露出严重问题:策略变更周期长、版本管理缺失、审计困难、与基础设施耦合过紧。
策略即代码将策略从应用代码和基础设施配置中解耦,以声明式语言(如 Rego)编写,纳入 GitOps 工作流,实现版本控制、自动化测试和持续交付。其核心优势包括:
| 维度 | 传统策略管理 | 策略即代码(OPA) |
|---|---|---|
| 存储方式 | 配置文件、数据库、硬编码 | 代码仓库(Git) |
| 变更流程 | 手动修改、重启服务 | PR 审查、CI/CD 自动化部署 |
| 可审计性 | 日志分散、难以追溯 | 完整变更历史、每次决策可审计 |
| 耦合度 | 与业务代码或基础设施绑定 | 完全解耦、独立于应用 |
| 测试能力 | 依赖集成测试、覆盖不全 | 单元测试、模拟输入、快速反馈 |
关键认知: 策略即代码不是简单的“把策略写成 YAML”,而是将策略视为一等公民——可测试、可版本化、可复用、可组合。OPA 是这一理念在云原生生态中最成熟的实现。
2.2 OPA 架构:解耦策略决策
OPA 是一个通用的、轻量级的策略引擎,其核心架构遵循 决策请求-决策响应 模式。OPA 本身不执行策略,而是根据输入数据和策略规则做出决策,将决策结果返回给调用方。
部署模式: OPA 可以以 Sidecar、DaemonSet 或独立服务的形式运行。在 Kubernetes 环境中,最常用的集成方式是 OPA Gatekeeper(基于 Admission Webhook),它会在资源创建/更新时自动调用 OPA 进行策略检查。
2.3 Rego 语言核心概念
Rego 是 OPA 专用的声明式查询语言,灵感来源于 Datalog。它不描述“如何做”,而是描述“策略是什么”。理解 Rego 的关键在于掌握其数据模型和基本语法结构。
2.3.1 数据模型:文档与规则
在 Rego 中,所有数据都被视为文档(Document)。输入数据(input)、外部数据(data)以及策略规则本身都是文档。规则定义实际上是在生成新的文档。
| 概念 | 说明 | 示例 |
|---|---|---|
| input | 每次策略查询时传入的请求数据 | input.request.operation |
| data | OPA 加载的外部数据(集群状态、配置) | data.namespaces |
| 规则 | 定义策略逻辑,生成布尔值或结构化结果 | deny[msg] { ... } |
| 包 | 组织规则的命名空间 | package kubernetes.admission |
2.3.2 基本语法结构
一个 Rego 策略文件通常包含包声明、导入语句和规则定义。以下是一个完整的策略示例:
package kubernetes.admission
import future.keywords.in
# 规则:禁止使用 latest 标签
deny[msg] {
input.request.kind.kind == "Pod"
some container in input.request.object.spec.containers
container.image == "nginx:latest"
msg := sprintf("容器 %v 使用了 latest 标签,禁止部署", [container.name])
}
# 规则:必须包含资源限制
deny[msg] {
input.request.kind.kind == "Pod"
some container in input.request.object.spec.containers
not container.resources.limits
msg := sprintf("容器 %v 未设置资源限制", [container.name])
}
语法要点:
deny[msg]是部分规则,当条件满足时生成一条拒绝消息。some ... in用于迭代数组或集合。not表示逻辑否定,检查某个条件不成立。sprintf用于格式化字符串,构建可读的错误信息。
2.3.3 规则类型
Rego 支持三种规则类型,理解它们的区别是编写正确策略的前提:
| 类型 | 返回值 | 典型用途 | 示例 |
|---|---|---|---|
| 完整规则 | 单个值(布尔、字符串、对象) | 全局开关、配置项 | allowed := true |
| 部分规则 | 集合(一组值) | 收集所有违规消息 | deny[msg] { ... } |
| 函数规则 | 带参数的计算结果 | 复用逻辑、复杂计算 | valid_port(p) { p > 0 } |
2.4 Rego 查询与决策流程
理解 OPA 如何处理一个策略查询请求,有助于调试和优化策略性能。下图展示了从输入到决策的完整路径:
2.5 常见模式与最佳实践
在编写 Rego 策略时,以下模式会频繁出现在容量规划和巡检场景中:
- 迭代检查: 使用
some关键字遍历 Pod 中的容器列表,检查每个容器的资源请求。 - 聚合统计: 使用
count()函数统计满足条件的资源数量,例如统计所有未设置requests的容器。 - 数据引用: 通过
data.inventory访问集群中已有的资源清单,实现跨资源策略(如检查某个 Namespace 是否已存在特定标签)。 - 错误消息构建: 使用
sprintf和concat生成结构化的审计信息,便于后续告警和日志分析。
避坑指南:
- 避免在规则中使用
== true或== false,直接使用布尔值即可。 - 注意
not的优先级:not p and q会被解析为not (p and q),应使用括号明确意图。 - 部分规则(如
deny[msg])的求值是惰性的,只有被查询时才会执行。