Tekton PipelinesManual Approval Gate

功能概述

Manual Approval Gate 允许流水线作者暂停 PipelineRun,直到指定的审批人完成审查并批准该操作。在后台,Operator 提供的控制器会为每个审批步骤创建一个 ApprovalTask 资源,跟踪审批人的响应,并将结果写回到原始的 CustomRun

使用场景

  • 在部署到生产环境或执行破坏性维护操作之前,强制设置人工检查点。
  • 通过将个人和组与 numberOfApprovalsRequired 结合,实现多人审批策略。
  • 通过查询 ApprovalTask 的状态字段或 CLI 输出,审计谁批准或拒绝了某次变更。

前提条件

  • 集群管理员已部署 Manual Approval Gate。如果没有,请参考 部署指南
  • 你可以在目标命名空间中创建或编辑 Pipeline/PipelineRun 对象。
  • 审批人可以在目标命名空间中对 approvaltasks.openshift-pipelines.org 资源执行 patch(通常通过 kubectl)。像 Alauda Container Platform 这样的平台默认授予此能力;只有在你显式移除了内置权限时,才需要自定义 RBAC。

步骤

1. 在流水线中声明审批步骤

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: deploy-with-approval
spec:
  tasks:
    - name: build
      taskRef:
        name: build-bundle     # Placeholder Task; replace with your build logic
    - 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: "Approve promotion into production"
    - name: deploy
      runAfter:
        - wait-for-approval
      taskRef:
        name: deploy-prod     # Placeholder Task; replace with your deployment logic

当流水线到达 wait-for-approval 时,Tekton 会发出一个 CustomRun。审批控制器会使用你的参数创建一个 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:门禁的整体状态,例如 pendingapprovedrejected
  • 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:

# First member (bob) from release-managers group approves
$ 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

# Second member (carol) from release-managers group also approves
$ 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"}
    ]'

以组成员身份拒绝:

# A member (david) from release-managers group rejects
$ 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 组中的 bobcarol 都已批准。组成员的每次批准都会分别增加 approvalsReceived,因此两个组成员的批准会计为两次批准,并计入所需总数。status.approversResponse 会显示详细的审批信息,包括单个组成员的响应。

组审批要点:

  • 每个组成员都必须执行 两个必需操作:将自己的条目添加到 users 数组,并设置该组的 inputapprovereject)。可选地,还可以设置组的 message
  • 第一个组成员使用路径 /spec/approvers/<index>/users 和数组值创建 users 数组
  • 后续成员使用路径 /spec/approvers/<index>/users/- 追加到数组末尾
  • users 数组中的每个用户条目只包含 nameinput 字段(用户条目内不包含 message 字段)
  • 组级别的 message 字段是可选且共享的;如果后续响应提供了新消息,它将被覆盖
  • 每个组成员的批准都会独立增加 approvalsReceived
  • 同一组中的多个成员可以批准,并且每次批准都计入所需总数
  • status.approversResponse 字段会跟踪详细的审批信息,包括单个组成员
  • 在执行 patch 时,使用 --as <username> --as-group <groupname> 来标识为组成员

控制器会相应地将对应的 CustomRunPipelineRun 设置为 SucceededFailed:审批会一直累积,直到满足 numberOfApprovalsRequired;而任何一次拒绝都会立即使流水线的该部分失败。

提示: 当你需要以特定身份进行审批时,请使用 --as <username>(必需)以及 --as-group <group>。验证 webhook 只允许你修改与该模拟用户和组相匹配的条目。RBAC 必须授予你模拟权限。例如,kubectl patch ... --as bob --as-group release-managers 表示你以用户 bob 的身份,并在 release-managers 组内执行操作。

4. 为长时间审批延长 PipelineRun 超时时间

如果审批可能需要数小时或数天,请同时配置 PipelineRun.spec.timeouts.pipelinePipelineRun.spec.timeouts.tasks,使其长于审批窗口,这样运行就不会在审批人响应之前终止。用于演示审批门禁的一个简单 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 参数短于流水线超时时间。否则,PipelineRun 可能会先过期,导致审批未完成。

操作结果

  • kubectl get approvaltasks -o yaml 会显示每个审批门禁及其 state 和与法定人数相关的字段(当有人响应后,approvalsReceived 列才会出现)。
  • PipelineRun 状态会反映审批结果:批准时,下游任务会继续执行;拒绝时,运行会失败,并传播来自 ApprovalTask 的原因。
  • 调度日志或 kubectl get approvaltask -o yaml 的输出可提供审批历史,用于审计。

故障排查

  • 审批任务始终不出现: 确认管理员安装的 ManualApprovalGate CR 处于 READY。没有控制器时,CustomRun 对象会一直处于 pending。
  • 审批人缺少权限: 为他们授予在相关命名空间中对 approvaltasks.openshift-pipelines.orggetlistupdatepatch 访问权限。
  • 流水线在审批完成前结束:PipelineRun.spec.timeouts.pipelinePipelineRun.spec.timeouts.tasks 都设置为覆盖预期的审批窗口,并确保审批 timeout 是合理的。否则,即使审批人尚未响应,运行也可能超时。
  • 即使已审批仍卡在 pending: 检查 status.approversResponse,查看是否有用户更改了投票或进行了拒绝。你可能需要更新审批人列表并重新运行流水线。

用户和组标识符

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)。其他平台也会暴露类似资源——请查阅身份服务文档以了解确切的字段名称。

了解更多