Два pipeline одновременно деплоят приложение: очередь через resource_group

Pipeline A начал выкладывать приложение, затем pipeline B дошёл до той же машины. Первый копирует файлы, второй меняет ссылку на релиз. Каждый скрипт по отдельности выглядит правильным, а итоговое состояние зависит от порядка отдельных команд. stage: deploy не помогает: порядок стадий действует внутри pipeline, тогда как A и B выполняются независимо.

Для общего изменяемого окружения нужен один владелец на время операции. В GitLab это можно описать через resource_group. Работы с одинаковым ключом в одном проекте получают ресурс по очереди. Сборки и тесты при этом продолжают использовать параллелизм.

Поставим очередь вокруг всей операции

Начните с безопасной демонстрации, которая ничего не деплоит. Создайте такой .gitlab-ci.yml в тестовом репозитории и быстро запустите два pipeline. Образ Alpine даёт одинаковое окружение для date и sleep на контейнерном Runner.

stages: [deploy]

deploy_probe:
  stage: deploy
  image: alpine:3.21
  resource_group: production
  script:
    - printf 'start pipeline=%s\n' "$CI_PIPELINE_ID"
    - date -u
    - sleep 20
    - date -u
    - printf 'finish pipeline=%s\n' "$CI_PIPELINE_ID"

Пока одна job находится между start и finish, вторая должна ждать освобождения resource_group. Сопоставьте время обоих журналов: интервалы выполнения script не должны пересекаться. Этот пример проверяет сам механизм очереди. Для настоящего деплоя замените демонстрационные команды вызовом вашего релизного скрипта, сохранив одну job вокруг изменения конфигурации, миграции и переключения версии, если они требуют общего исключительного доступа.

Если разделить одну критическую операцию на две jobs с тем же ключом, между ними может вклиниться работа другого pipeline. Блокировка удерживается задачей, а не всем набором jobs с одинаковым именем environment. Поэтому границу критической секции нужно выбирать по реальному конфликту, а не по удобству названий стадий.

Один ключ на одно общее состояние

production в примере — имя ресурса очереди. environment: production отдельно описывает окружение деплоя и его историю, но само по себе очередь не создаёт. Для staging и production обычно нужны разные ключи, иначе независимые среды будут ждать друг друга.

Ключ с CI_PIPELINE_ID тоже выглядит уникальным и аккуратным, но уничтожает нужный эффект: каждый pipeline получает собственную очередь. Напротив, одинаковый ключ между jobs разных проектов не создаёт межпроектную блокировку. Если несколько репозиториев меняют одну инфраструктуру, договоритесь об общем владельце деплоя или используйте координацию на стороне самой системы.

Очередь ещё не задаёт порядок версий

По умолчанию используется unordered. Это означает, что взаимного пересечения нет, однако порядок выбора готовых jobs не гарантирован. Старый pipeline может дождаться ресурса после нового и выложить более ранний commit. Если команда ожидает хронологическое применение всех релизов, изучите oldest_first.

Режим resource_group меняется через API редактирования существующей группы, а не добавлением произвольного process_mode в YAML job. Доступны также newest_first и newest_ready_first; их выбор требует понимания повторяемости деплоя и политики устаревших работ. Точные различия приведены в описании resource groups.

Даже oldest_first не заменяет правила допуска релиза. Работа, которая долго ждала ручного подтверждения, может стать неактуальной. Перед изменением production полезно сопоставить SHA кандидата с принятой политикой версий и текущим релизом. Очередь решает конфликт одновременных записей, а решение «можно ли ещё выкладывать этот commit» относится к релизному процессу.

Что считать окончанием деплоя

Если скрипт отправляет асинхронную команду внешнему оркестратору и сразу завершается, GitLab освободит ресурс, хотя внешнее изменение продолжается. Чтобы очередь отражала реальную занятость, job должна дождаться определённого результата: например, завершения rollout и проверки версии. Конкретная команда зависит от системы, но её код возврата должен влиять на завершение job.

В схемах с child pipelines проверьте, кто удерживает ресурс и кого ожидает. Родитель, ожидающий дочернюю работу, и ребёнок, которому нужен удерживаемый родителем ресурс, могут создать взаимное ожидание. Нарисуйте последовательность захвата и освобождения прежде, чем переносить resource_group на trigger job.

После настройки оставьте в релизном журнале ID pipeline, SHA и фактически развернутую версию. Эти данные позволяют отличить ожидание очереди от медленного релиза и восстановить порядок изменений. Если второй deployment начался только после завершения первого и итоговый SHA соответствует политике, проверены оба важных свойства: исключительность выполнения и правильность выбранного релиза.

Ещё по теме

Продолжить изучение

Хотите собрать проверяемый путь от коммита до релиза? Jobs, runners, артефакты и деплой — темы программы «GitLab CI/CD: автоматизация сборки и деплоя».

Продолжить на практике

  • GitLab CI/CD: автоматизация сборки и деплоя ↗

    От первого .gitlab-ci.yml до pipeline, который тестирует код, собирает образ и выкладывает приложение. Runners, artifacts, cache, registry, окружения и безопасный откат — на одном практическом проекте.