• Русский
  • Manual Approval Gate для Tekton Pipelines

    Обзор функции

    Manual Approval Gate позволяет авторам pipeline приостанавливать выполнение PipelineRun до тех пор, пока назначенные утверждающие не рассмотрят и не одобрят операцию. За кулисами контроллер, предоставляемый оператором, создаёт ресурс ApprovalTask для каждого шага одобрения, отслеживает ответы утверждающих и записывает результат обратно в исходный CustomRun.

    Сценарии использования

    • Обеспечение контрольных точек с участием человека перед развертыванием в production или выполнением разрушительного обслуживания.
    • Реализация политики многоуровневого одобрения с помощью комбинации отдельных пользователей и групп с параметром numberOfApprovalsRequired.
    • Аудит того, кто одобрил или отклонил изменение, путём запроса полей статуса ApprovalTask или вывода CLI.

    Предварительные требования

    • Администратор кластера развернул Manual Approval Gate. Если нет, обратитесь к руководству по развертыванию.
    • У вас есть права на создание или редактирование объектов Pipeline/PipelineRun в целевом namespace.
    • Утверждающие могут выполнять патчи ресурсов approvaltasks.openshift-pipelines.org (обычно через kubectl) в целевом namespace. Платформы, такие как 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     # Заглушка задачи; замените на вашу логику сборки
        - 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     # Заглушка задачи; замените на вашу логику развертывания

    Когда pipeline достигает шага 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: общее состояние шлюза, например pending, approved или rejected.
    • status.approvalsRequired / status.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:

    # Первый участник (bob) из группы release-managers одобряет
    $ 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"}
        ]'

    Последующие участники добавляют свои записи в существующий массив и обновляют input группы:

    # Второй участник (carol) из группы release-managers также одобряет
    $ 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"}
        ]'

    Чтобы отклонить как участник группы:

    # Участник (david) из группы release-managers отклоняет
    $ 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"}
        ]'

    После этих патчей проверьте запись группы и статус, чтобы увидеть ответы всех участников:

    $ 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"
      }
    }

    В этом примере и bob, и carol из группы release-managers одобрили. Каждое одобрение участника группы увеличивает approvalsReceived отдельно, поэтому два одобрения членов группы считаются как два одобрения для достижения требуемого количества. Поле status.approversResponse показывает подробную информацию об одобрениях, включая ответы отдельных участников группы.

    Основные моменты для одобрения группой:

    • Каждый участник группы должен выполнить две обязательные операции: добавить свою запись в массив users И установить input группы (либо approve, либо reject). Опционально можно также задать поле message группы.
    • Первый участник создаёт массив users по пути /spec/approvers/<index>/users со значением массива.
    • Последующие участники добавляют записи в массив по пути /spec/approvers/<index>/users/-, где - означает добавление в конец массива.
    • Каждая запись пользователя в массиве users содержит только поля name и input (без поля message внутри записи пользователя).
    • Поле message на уровне группы является опциональным и общим; оно будет перезаписано последующими ответами, если они предоставят новое сообщение.
    • Каждое одобрение участника группы увеличивает approvalsReceived независимо.
    • Несколько участников из одной группы могут одобрять, и каждое одобрение учитывается в общем требуемом количестве.
    • Поле status.approversResponse отслеживает подробную информацию об одобрениях, включая отдельных участников группы.
    • Используйте --as <username> --as-group <groupname>, чтобы идентифицироваться как участник группы при патче.

    Контроллер устанавливает соответствующие 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 так, чтобы они превышали окно одобрения, чтобы выполнение не завершилось до ответа утверждающих. Пример простого 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 отражает результат одобрения: при одобрении последующие задачи продолжаются; при отклонении выполнение завершается с ошибкой, причина которой передаётся из ApprovalTask.
    • Логи диспетчера или вывод kubectl get approvaltask -o yaml предоставляют историю одобрений для аудита.

    Устранение неполадок

    • Задача одобрения не появляется: Убедитесь, что установленный администратором CR ManualApprovalGate в состоянии READY. Без контроллера объекты CustomRun остаются в ожидании.
    • Утверждающим не хватает прав: Предоставьте им доступ get, list, update и patch к ресурсам approvaltasks.openshift-pipelines.org в соответствующем namespace.
    • Pipeline завершился до окончания одобрения: Установите таймауты PipelineRun.spec.timeouts.pipeline и PipelineRun.spec.timeouts.tasks, покрывающие ожидаемое окно одобрения, и убедитесь, что таймаут задачи одобрения реалистичен. Иначе выполнение может завершиться по таймауту, даже если утверждающие не ответили.
    • Зависание в состоянии ожидания даже после одобрений: Проверьте status.approversResponse на наличие пользователей, изменивших голос или отклонивших. Возможно, потребуется обновить список утверждающих и повторно запустить pipeline.

    Идентификаторы пользователей и групп

    Manual Approval Gate опирается на провайдера идентификации вашей платформы для сопоставления имён утверждающих. Всегда используйте канонические идентификаторы, предоставляемые провайдером, а не отображаемые имена в 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). Другие платформы предоставляют аналогичные ресурсы — обратитесь к документации службы идентификации для точных названий полей.

    Дополнительная информация