3. ConstraintTemplate 基础:理解 ConstraintTemplate 的结构、Rego 语言入门、编写第一个简单的约束模板
3.1 ConstraintTemplate 核心结构
ConstraintTemplate 是 OPA Gatekeeper 中定义策略逻辑与参数模式的 CRD(Custom Resource Definition)。它由两个主要部分组成:CRD 规范(crd.spec) 和 目标代码(targets)。
| 组成部分 | 字段路径 | 作用描述 |
|---|---|---|
| CRD 规范 | spec.crd.spec |
定义该模板生成的约束(Constraint)资源所接受的参数(parameters)结构,即用户在使用约束时可以传入哪些字段。 |
| 目标代码 | spec.targets |
包含 Rego 策略逻辑,以及目标平台(如 admission.k8s.gatekeeper.sh)的标识。Rego 代码必须定义 violation 规则。 |
| 元数据 | metadata.name |
模板名称,通常采用 k8s<策略名> 的命名惯例,例如 k8srequiredlabels。 |
关键原则: ConstraintTemplate 是“模板工厂”,它不直接执行策略,而是生成一个名为 Constraint 的 CRD 实例。用户通过创建 Constraint 资源来激活具体的策略规则。
3.2 Rego 语言入门:策略即代码
Rego 是 OPA 的声明式查询语言,专为策略定义而生。与传统的命令式编程不同,Rego 关注的是“数据应该满足什么条件”,而不是“如何一步步检查数据”。
3.2.1 核心概念速览
- 规则(Rule): 以
rule_name := expression或rule_name { expression }形式定义,是 Rego 的基本逻辑单元。 - 输入(input): 在 Gatekeeper 中,
input代表被审查的 Kubernetes 资源对象(如 Pod、Deployment)。 - 数据(data): 包含外部数据,如从 Constraint 参数传入的
data.parameters。 - 默认规则(default): 用于设置规则的默认值,避免未定义情况。
- violation 规则: Gatekeeper 强制要求 Rego 代码中必须包含一个名为
violation的规则,该规则返回true表示违反策略。
3.2.2 Rego 语法速查表
| 语法元素 | 示例 | 说明 |
|---|---|---|
| 字面量 | "nginx", 8080, true |
字符串、数字、布尔值。 |
| 变量 | name := input.metadata.name |
使用 := 赋值,变量不可变。 |
| 数组/对象访问 | input.spec.containers[0].image |
点号访问嵌套字段,方括号索引。 |
| 迭代 | some container; container := input.spec.containers[_] |
some 关键字用于遍历集合。 |
| 条件 | container.image == "nginx:latest" |
使用 == 比较,!= 不等。 |
| 逻辑运算 | a == 1; b == 2 (AND) |
分号表示 AND,多个条件需同时满足。 |
| 内置函数 | startswith(image, "nginx") |
字符串、正则、集合等丰富内置函数。 |
学习建议: 将 Rego 视为“数据查询语言”而非“编程语言”。你只需要描述“什么样的数据是合规的”,OPA 引擎会自动匹配。
3.3 编写第一个约束模板:强制标签存在
下面我们编写一个完整的 ConstraintTemplate,要求所有命名空间(Namespace)必须包含 owner 标签。
3.3.1 模板 YAML 定义
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
# 获取约束参数中定义的必须存在的标签列表
required_labels := input.parameters.labels
# 遍历每个必须存在的标签
some label
label := required_labels[_]
# 检查命名空间是否缺少该标签
not input.review.object.metadata.labels[label]
# 构造违规消息
msg := sprintf("命名空间必须包含标签: %v", [label])
}
3.3.2 代码逐行解析
- 包声明:
package k8srequiredlabels必须与模板名称(去除k8s前缀)一致。 - violation 规则: 返回一个包含
msg的对象,当规则体为真时触发违规。 - 参数读取:
input.parameters.labels从 Constraint 实例中获取用户传入的标签列表。 - 迭代检查:
some label; label := required_labels[_]遍历所有必须的标签。 - 缺失判断:
not input.review.object.metadata.labels[label]检查标签是否不存在。 - 消息构造:
sprintf格式化输出违规详情。
常见错误: 忘记在
violation 规则中使用 input.parameters 读取参数,而是硬编码标签值。这会导致模板失去灵活性,无法复用。
3.4 ConstraintTemplate 与 Constraint 的关系
下图展示了从模板定义到策略生效的完整链路:
3.5 创建并使用第一个约束
完成模板部署后,需要创建对应的 Constraint 实例来激活策略。
3.5.1 部署模板
kubectl apply -f k8srequiredlabels.yaml
3.5.2 创建 Constraint 实例
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- "owner"
3.5.3 验证效果
尝试创建一个没有 owner 标签的命名空间:
kubectl create namespace test-ns
# 预期输出:Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: 命名空间必须包含标签: "owner"
调试技巧: 使用
kubectl describe constrainttemplate k8srequiredlabels 查看模板状态;使用 kubectl get constraint 查看所有已激活的约束实例。
3.6 本章小结
- ConstraintTemplate 是 Gatekeeper 策略的“蓝图”,包含 CRD 规范和 Rego 代码。
- Rego 是声明式策略语言,核心是定义
violation规则来描述违规条件。 - 通过
input.parameters读取约束参数,实现策略的灵活复用。 - 模板 + 约束实例 = 完整的策略执行单元。