← CI/CD: паттерны
11 мин · средне · Урок 3 из 5

Паттерны пайплайнов: gates, approvals, fan-out

Ручные подтверждения, защищённые окружения, параллельные ветки fan-out/fan-in и условный запуск job.

Когда линейного пайплайна мало

Простой пайплайн идёт сверху вниз одной цепочкой. На реальном проекте этого не хватает: какие-то шаги должны идти параллельно, какие-то требуют человеческого «да», а деплой в прод нельзя пускать с любой ветки. Для этого есть несколько паттернов.

Manual gate: ручное подтверждение

Gate (ворота) останавливает пайплайн и ждёт, пока человек нажмёт кнопку. Типичный случай: до staging катим автоматически, а в прод только после ручного подтверждения.

deploy-prod:
  stage: deploy
  script:
    - ./deploy.sh production
  when: manual
  allow_failure: false      # без этой строки job не блокирует пайплайн
  environment:
    name: production

when: manual превращает job в кнопку в UI. А вот блокирует ли эта кнопка пайплайн, зависит от allow_failure, и дефолт тут контринтуитивный.

У job с when: manual, написанным на верхнем уровне, allow_failure по умолчанию равен true. GitLab считает такую job необязательной: пайплайн не останавливается, идёт дальше и красится в зелёный, даже если кнопку никто не нажал. Получается не гейт, а просто доступное действие. Классический сюрприз: человек думает, что поставил защиту перед продом, а пайплайн спокойно завершился успехом мимо неё.

Чтобы это был настоящий гейт, нужно явно поставить allow_failure: false. Тогда пайплайн встаёт в статус ожидания и дальше не пойдёт, пока кнопку не нажмут.

Внутри rules: дефолт обратный: там when: manual идёт с allow_failure: false, то есть блокирует сразу.

deploy-prod:
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual        # тут allow_failure по умолчанию false — гейт

Проще не держать это в голове, а писать allow_failure явно в обоих случаях. Читающему конфиг сразу видно, гейт это или необязательная кнопка.

Защищённые окружения и approvals

Кнопку нажать может не любой. Environment в GitLab/GitHub связывают с правами: деплой в production разрешён только мейнтейнерам, и часто нужно подтверждение второго человека (approval). Так выкат в прод проходит ревью, как код.

Сюда же относится правило «деплой только из защищённой ветки»: job деплоя вообще не появляется в пайплайне, если он идёт не из main. Ровно это и делает условие if: $CI_COMMIT_BRANCH == "main" из примера выше. Из случайной ветки кнопку не увидит даже мейнтейнер.

Fan-out и fan-in

Fan-out значит разветвить пайплайн на параллельные job, fan-in собрать их обратно. Классика: после сборки одновременно гонять юнит-тесты, линтер и аудит зависимостей, а дальше один общий шаг, который ждёт их все.

build unit-tests lint audit package fan-out · разветвили fan-in · собрали
Три проверки идут разом и не зависят друг от друга, а package стартует, только когда все три зелёные.

Параллельные job не зависят друг от друга и идут разом, поэтому этап проходит за время самой долгой проверки, а не за их сумму. Шаг package стартует, только когда все три зелёные.

Условный запуск

Не всё нужно гонять всегда. rules запускают job по условию: только на тег, только при изменении определённых файлов, только в MR.

build-docs:
  rules:
    - changes:
        - docs/**       # собирать доки, только если их трогали

Так пайплайн не тратит время на то, что не менялось: правка в docs/ не запускает пересборку образа приложения, и наоборот.

Что важно запомнить

Manual gate (when: manual) ставит человека на последний контроль перед продом, не ломая автоматизацию остального. Защищённые окружения и approval превращают выкат в ревью.

Сам по себе when: manual на верхнем уровне job пайплайн не блокирует: allow_failure там по умолчанию true, и это просто необязательная кнопка. Гейт получается с явным allow_failure: false. Внутри rules: дефолт обратный, поэтому проще писать allow_failure явно везде.

Fan-out разветвляет пайплайн на параллельные независимые job, fan-in собирает их обратно: этап идёт за время самой долгой проверки, а не за сумму.

rules запускают job по условию (ветка, тег, изменённые файлы), чтобы не гонять лишнее.

В следующем уроке разберём кеш и артефакты: как ускорять сборки и передавать результат между job.

Прошли материал до конца? Отметьте урок пройденным.