4. OPA Gatekeeper 概述:架构、Webhook 与 Rego 策略语言
OPA Gatekeeper 是 Kubernetes 生态中最成熟的 策略即代码 实现方案。它基于 开放策略代理(OPA) 引擎,通过 Admission Webhook 机制拦截所有资源变更请求,并使用 Rego 策略语言 编写声明式规则,最终决定请求是允许还是拒绝。本章将拆解 Gatekeeper 的核心架构、Webhook 工作流程以及 Rego 语言的核心概念。
4.1 Gatekeeper 架构总览
Gatekeeper 以 Operator 模式 运行在 Kubernetes 集群内,由三个核心组件构成:
| 组件 | 部署形态 | 核心职责 |
|---|---|---|
| Gatekeeper Controller | Deployment(通常 1 副本) | 监听 ConstraintTemplate 和 Constraint 自定义资源,编译 Rego 策略并缓存;管理 Webhook 配置的注入与更新。 |
| Gatekeeper Webhook | Deployment(建议 2+ 副本) | 接收 APIServer 发起的 AdmissionReview 请求,执行 Rego 策略评估,返回 allowed 或 denied 结果。 |
| Gatekeeper Audit | 周期性 Job / 常驻 Pod | 对集群中已有资源进行 事后审计,标记违反策略的资源,并写入 Constraint 对象的 status 字段。 |
关键设计原则: Gatekeeper 将策略定义(
ConstraintTemplate)与策略实例(Constraint)分离。前者是 Rego 代码的模板,后者是带有具体参数(如故障域标签值)的实例化对象。这种设计使得策略逻辑可复用,参数可灵活配置。
4.2 Admission Webhook 工作原理
Kubernetes 提供了两种 Webhook 钩子来扩展准入控制流程:
- MutatingAdmissionWebhook:在对象持久化之前,允许修改(mutate)请求对象。Gatekeeper 默认不使用此钩子,但可通过
mutations功能扩展。 - ValidatingAdmissionWebhook:在对象持久化之前,验证请求是否合规。Gatekeeper 的核心准入控制基于此钩子实现。
一个完整的 Webhook 请求处理流程包含以下步骤:
- 用户发起请求:
kubectl apply -f pod.yaml或 API 调用。 - APIServer 路由:请求到达 APIServer,经过身份认证、鉴权后,进入准入控制链。
- Webhook 拦截:APIServer 根据
ValidatingWebhookConfiguration中配置的规则(匹配资源、操作、命名空间等),将请求封装为AdmissionReview对象,通过 HTTPS POST 发送给 Gatekeeper Webhook 服务。 - 策略评估:Gatekeeper Webhook 从请求中提取对象内容,与已缓存的 Rego 策略进行匹配和评估。
- 返回结果:Webhook 返回
AdmissionReview响应,包含allowed: true/false以及可选的status.message(拒绝原因)。 - 最终处理:APIServer 根据 Webhook 的响应决定是允许对象写入 etcd,还是拒绝并返回错误给用户。
性能考量: Webhook 的超时时间默认为 30 秒。如果 Gatekeeper 响应超时或服务不可用,APIServer 的行为由
failurePolicy 决定:Fail(拒绝所有请求,集群进入“防崩溃”模式)或 Ignore(跳过 Webhook,允许请求通过)。生产环境建议设置为 Fail 并确保 Webhook 高可用。
4.3 Rego 策略语言简介
Rego 是 OPA 项目专门设计的 声明式查询语言,灵感来源于 Datalog。它并非通用编程语言,而是专注于 策略决策 的领域特定语言(DSL)。在 Gatekeeper 中,所有策略逻辑都用 Rego 编写。
4.3.1 核心概念
| 概念 | 说明 | Gatekeeper 中的角色 |
|---|---|---|
| 规则(Rule) | 形如 rule_name = value { condition } 的声明,当条件为真时,规则被赋值。 |
定义策略的评估逻辑,如 violation[msg] { ... }。 |
| 查询(Query) | 对规则进行求值,返回所有满足条件的绑定。 | Gatekeeper 引擎自动执行查询,检查是否有 violation 规则被触发。 |
| 输入(input) | 表示当前被评估的 Kubernetes 对象(AdmissionReview 中的 Object)。 | 通过 input.review.object 访问 Pod、Namespace 等资源字段。 |
| 数据(data) | 表示外部数据,包括集群中的其他资源、参数等。 | 通过 data.inventory 访问集群资源;通过 data.parameters 访问 Constraint 参数。 |
4.3.2 一个简单的 Rego 策略示例
以下策略检查 Pod 是否设置了 failure-domain.beta.kubernetes.io/zone 标签:
package k8s.faultdomain
violation[msg] {
# 获取输入对象
obj := input.review.object
# 检查是否为 Pod
obj.kind == "Pod"
# 检查标签是否存在
not obj.metadata.labels["failure-domain.beta.kubernetes.io/zone"]
# 构造违规消息
msg := sprintf("Pod '%v' 缺少故障域标签 'failure-domain.beta.kubernetes.io/zone'", [obj.metadata.name])
}
常见陷阱: Rego 中的
= 是 赋值/统一 操作符,而非比较。比较应使用 == 或 !=。另外,Rego 默认所有变量都是 不可变 的,循环依赖递归实现,而非 for 循环。
4.3.3 Rego 在 Gatekeeper 中的执行上下文
在 Gatekeeper 的 ConstraintTemplate 中,Rego 代码被包裹在一个固定的框架内:
- package:必须声明包名,通常遵循
k8s.<category>命名规范。 - violation 规则:这是 Gatekeeper 约定的入口规则。如果
violation规则被触发(即产生至少一条消息),则请求被拒绝。 - 参数注入:通过
data.parameters访问用户在 Constraint 对象中定义的参数,实现策略的差异化配置。
下表总结了 Rego 与常见编程语言的关键差异:
| 特性 | Rego | Python / Go |
|---|---|---|
| 编程范式 | 声明式(逻辑编程) | 命令式 / 面向对象 |
| 变量可变性 | 不可变(一次赋值) | 可变 |
| 循环结构 | 集合推导(Comprehension) | for / while |
| 副作用 | 无副作用(纯函数) | 允许 I/O、状态修改 |
| 错误处理 | 规则未定义即返回 false | try / except / panic |
学习建议: 对于有 Python 或 Go 背景的开发者,Rego 的集合推导式(如
[x | x := array[_]; condition])是最容易上手的切入点。建议先在 OPA Playground 中练习基础语法,再编写 Gatekeeper 策略。