Manual Approval Gate 用于 Tekton Pipelines
功能概览
Manual Approval Gate 允许 pipeline 作者暂停一个 PipelineRun,直到指定的审批人完成审核并批准该操作。其背后由 Operator 提供的 controller 为每个审批步骤创建一个 ApprovalTask 资源,跟踪审批人的响应,并将结果写回到原始的 CustomRun。
使用场景
- 在部署到生产环境或执行破坏性维护操作之前,强制进行人工检查点。
- 通过结合个人和组,并使用
numberOfApprovalsRequired,实现多人审批策略。
- 通过查询
ApprovalTask 的状态字段或 CLI 输出,审计谁批准或拒绝了某项变更。
前提条件
- 集群管理员已部署
Manual Approval Gate。如果没有,请参考 部署指南。
- 你可以在目标命名空间中创建或编辑
Pipeline/PipelineRun 对象。
- 审批人可以在目标命名空间中对
approvaltasks.openshift-pipelines.org 资源执行 patch(通常通过 kubectl)。像 Alauda Container Platform 这类平台默认提供此能力;只有在你明确移除了内置权限时,才需要自定义 RBAC。
步骤
1. 在 pipeline 中声明审批步骤
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: deploy-with-approval
spec:
tasks:
- name: build
taskRef:
name: build-bundle # 占位 Task;请替换为你的构建逻辑
- name: wait-for-approval
runAfter:
- build
taskRef:
apiVersion: openshift-pipelines.org/v1alpha1
kind: ApprovalTask
timeout: "24h"
params:
- name: approvers
value:
- alice
- group:release-managers
- name: numberOfApprovalsRequired
value: "1"
- name: description
value: "批准进入生产环境"
- name: deploy
runAfter:
- wait-for-approval
taskRef:
name: deploy-prod # 占位 Task;请替换为你的部署逻辑
当 pipeline 到达 wait-for-approval 时,Tekton 会生成一个 CustomRun。审批 controller 会使用你的参数创建一个 ApprovalTask,并保持 PipelineRun 处于等待状态,直到达到法定人数或发生拒绝。
2. 监控审批状态
$ kubectl get approvaltasks.openshift-pipelines.org
NAME AGE
deploy-with-approval-run-wait-for-approval 4m16s
$ kubectl describe approvaltask deploy-with-approval-run-wait-for-approval
Name: deploy-with-approval-run-wait-for-approval
Status:
Approvals Received: 1
Approvals Required: 1
Approvers:
alice
release-managers
Approvers Response:
Name: alice
Response: approved
Type: User
Start Time: 2025-11-17T10:37:01Z
State: approved
Events: <none>
重要的状态字段:
status.state:整体门禁状态,例如 pending、approved 或 rejected。
status.approvalsRequired / status.approvalsReceived:法定人数跟踪(approvalsReceived 计数仅在至少有一名审批人响应后出现)。
status.approversResponse:按用户/组记录结果及消息,便于审计。
3. 批准或拒绝
用户审批
首先,查看审批人列表以确定正确的索引:
$ kubectl get approvaltask deploy-with-approval-run-wait-for-approval -o json | jq '.spec.approvers'
[
{
"name": "alice",
"type": "User",
"input": "pending",
},
{
"name": "release-managers",
"type": "Group",
"input": "pending",
"users": []
}
]
以用户身份批准:
$ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
--type='json' \
--as alice \
-p='[
{"op":"replace","path":"/spec/approvers/0/input","value":"approve"},
{"op":"replace","path":"/spec/approvers/0/message","value":"QA complete"}
]'
以用户身份拒绝:
$ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
--type='json' \
--as alice \
-p='[
{"op":"replace","path":"/spec/approvers/0/input","value":"reject"},
{"op":"replace","path":"/spec/approvers/0/message","value":"Found regression"}
]'
组审批
当某个组被配置为审批人时,该组的单个成员可以通过将自己的响应添加到组条目下的 users 数组来提交审批。多个组成员可以独立批准,每次批准都会计入 approvalsReceived 总数。
最开始,组审批人条目没有 users 字段:
spec:
approvers:
- name: alice
type: User
input: pending
message: ""
- name: release-managers
type: Group
input: pending
message: ""
numberOfApprovalsRequired: 2
第一个组成员创建 users 数组,并将该组的 input 设置为 approve:
# release-managers 组的第一位成员(bob)批准
$ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
--type='json' \
--as bob \
--as-group release-managers \
-p='[
{"op":"add","path":"/spec/approvers/1/users","value":[{"name":"bob","input":"approve"}]},
{"op":"replace","path":"/spec/approvers/1/input","value":"approve"},
{"op":"replace","path":"/spec/approvers/1/message","value":"Approved by bob"}
]'
后续组成员将追加到现有的 users 数组,并更新该组的 input:
# release-managers 组的第二位成员(carol)也批准
$ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
--type='json' \
--as carol \
--as-group release-managers \
-p='[
{"op":"add","path":"/spec/approvers/1/users/-","value":{"name":"carol","input":"approve"}},
{"op":"replace","path":"/spec/approvers/1/input","value":"approve"},
{"op":"replace","path":"/spec/approvers/1/message","value":"Approved by carol"}
]'
以组成员身份拒绝:
# release-managers 组的一位成员(david)拒绝
$ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
--type='json' \
--as david \
--as-group release-managers \
-p='[
{"op":"add","path":"/spec/approvers/1/users/-","value":{"name":"david","input":"reject"}},
{"op":"replace","path":"/spec/approvers/1/input","value":"reject"},
{"op":"replace","path":"/spec/approvers/1/message","value":"Security concerns found"}
]'
完成这些 patch 后,检查组条目和状态,以查看所有成员的响应:
$ kubectl get approvaltask deploy-with-approval-run-wait-for-approval -o json | jq '{spec: .spec.approvers[1], status: .status}'
{
"spec": {
"input": "approve",
"message": "Approved by carol",
"name": "release-managers",
"type": "Group",
"users": [
{
"input": "approve",
"name": "bob"
},
{
"input": "approve",
"name": "carol"
}
]
},
"status": {
"approvalsReceived": 2,
"approvalsRequired": 2,
"approvers": [
"alice",
"release-managers"
],
"approversResponse": [
{
"groupMembers": [
{
"message": "Approved by carol",
"name": "bob",
"response": "approved"
},
{
"message": "Approved by carol",
"name": "carol",
"response": "approved"
}
],
"message": "Approved by carol",
"name": "release-managers",
"response": "approved",
"type": "Group"
}
],
"startTime": "2025-11-18T14:07:48Z",
"state": "approved"
}
}
在这个示例中,release-managers 组中的 bob 和 carol 都已批准。组内每个成员的批准都会分别增加 approvalsReceived,因此两个组成员的批准会计为两次批准,并计入所需总数。status.approversResponse 会显示详细的审批信息,包括单个组成员的响应。
组审批要点:
- 每个组成员都必须执行两个必需操作:将自己的条目添加到
users 数组,并设置该组的 input(approve 或 reject)。可选地,还可以设置该组的 message
- 第一位组成员使用路径
/spec/approvers/<index>/users 和数组值创建 users 数组
- 后续成员使用路径
/spec/approvers/<index>/users/- 追加到数组末尾,其中 - 表示追加到数组尾部
users 数组中的每个用户条目只包含 name 和 input 字段(用户条目中不包含 message 字段)
- 组级别的
message 字段是可选且共享的;如果后续响应提供了新消息,它会被覆盖
- 每个组成员的批准都会独立增加
approvalsReceived
- 同一组的多个成员都可以批准,且每次批准都会计入所需总数
status.approversResponse 字段会跟踪详细的审批信息,包括单个组成员
- 在 patch 时使用
--as <username> --as-group <groupname> 来标识为组成员
controller 会将对应的 CustomRun 和 PipelineRun 分别设置为 Succeeded 或 Failed:审批会持续累积,直到满足 numberOfApprovalsRequired;而任何一次拒绝都会立即使该 pipeline 阶段失败。
提示: 当你需要以特定身份进行审批时,请使用 --as <username>(必需)和 --as-group <group>。校验 webhook 只允许你修改与该被模拟身份和组相匹配的条目。RBAC 必须授予你模拟身份的权限。例如,kubectl patch ... --as bob --as-group release-managers 表示你以用户 bob 的身份,并在 release-managers 组内操作。
4. 为长时间审批延长 PipelineRun 超时
如果某次审批可能需要数小时或数天,请将 PipelineRun.spec.timeouts.pipeline 和 PipelineRun.spec.timeouts.tasks 都配置得长于审批窗口,以确保 run 不会在审批人响应之前终止。用于验证审批门的一个简单 PipelineRun 如下:
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: deploy-with-approval-run
spec:
pipelineRef:
name: deploy-with-approval
timeouts:
pipeline: 72h
tasks: 72h
确保审批任务的 timeout 参数短于 pipeline 超时时间。否则,PipelineRun 可能会先过期,导致审批无法完成。
操作结果
kubectl get approvaltasks -o yaml 会显示每个审批门的 state 和与法定人数相关的字段(approvalsReceived 列会在有人响应后出现)。
PipelineRun 状态会反映审批结果:批准后,下游任务会继续执行;拒绝后,run 会失败,并带有从 ApprovalTask 传播过来的原因。
- 调度日志或
kubectl get approvaltask -o yaml 的输出可提供审批历史,便于审计。
故障排查
- 审批任务始终不出现: 确认管理员安装的
ManualApprovalGate CR 处于 READY 状态。没有 controller 时,CustomRun 对象会一直处于 pending。
- 审批人权限不足: 为他们授予在相关命名空间中对
approvaltasks.openshift-pipelines.org 的 get、list、update 和 patch 访问权限。
- pipeline 在审批完成前结束: 将
PipelineRun.spec.timeouts.pipeline 和 PipelineRun.spec.timeouts.tasks 都设置为覆盖预期的审批窗口,并确保审批 timeout 设置合理。否则,即使审批人尚未响应,run 也可能超时。
- 即使已批准仍卡在 pending: 检查
status.approversResponse,查看是否有用户更改了投票或进行了拒绝。你可能需要更新审批人列表并重新运行 pipeline。
用户和组标识符
Manual Approval Gate 依赖平台的 Identity Provider 来匹配审批人名称。请始终使用提供方暴露的规范标识符,而不是 UI 中显示的名称。例如,在 Alauda Container Platform 上:
$ kubectl get users.auth.alauda.io
NAME TYPE USERNAME AGE
21232f297a57a5a743894a0e4a801fc3 local admin 19d
在添加用户审批人时,请使用 USERNAME 列(例如 admin)。
$ kubectl get groups.auth.alauda.io
NAME DISPLAYNAME CONNTYPE CONNID AGE
g-v9mfs test-group local local 19d
在引用组审批人时,请使用 NAME 列(例如 g-v9mfs),例如 group:g-v9mfs。其他平台也会暴露类似资源——请查阅 identity 服务文档以确认准确的字段名称。
了解更多