Skip to content
страница целиком, в Markdown — вставьте в чат с моделью

Общие шаблоны GitLab ​

Сборка образов вынесена в общий шаблон, который поддерживается в репозитории meduza/infra. Деплой каждый сервис описывает у себя — он короткий и явный. Эта страница разбирает и то, и другое.

Подключение шаблона ​

.gitlab-ci.yml сервиса начинается с подключения шаблона сборки:

yaml
include:
  - project: 'meduza/infra'
    ref: main
    file: '.gitlab/ci/docker-build.yml'

Это делает скрытую задачу .docker_build доступной для наследования.

Шаблон nomad-deploy.yml не подключаем

В том же каталоге лежит .gitlab/ci/nomad-deploy.yml с задачей .nomad_deploy — она копирует файлы по SSH на старый KVM-хост и запускает Nomad там. Для нового кластера он не годится: деплой идёт прямым обращением к Nomad API.


Шаблон .docker_build ​

Расположение: .gitlab/ci/docker-build.yml

Собирает Docker-образ и отправляет его в registry.

Исходный код шаблона ​

yaml
.docker_build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  before_script:
    - docker login $REGISTRY_HOST -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
    # DUAL_PUSH: пока часть сервисов живёт на старом кластере, образ
    # дублируется в старый registry. Переехавшие ставят DUAL_PUSH: "false".
  script:
    - cd ${SERVICE_DIR}
    # Тег окружения определяется по ветке: main → prod, stage → stage,
    # dev → dev. Любая другая ветка — выход без сборки.
    - >-
      docker build ${BUILD_ARGS}
      --platform linux/amd64
      -t $REGISTRY_IMAGE/${SERVICE_NAME}:${ENV_TAG} .
    - docker push $REGISTRY_IMAGE/${SERVICE_NAME}:${ENV_TAG}
  only:
    - main
    - stage
    - dev

Переменные ​

ПеременнаяОбязательнаОписание
SERVICE_NAMEДаИмя, используемое в пути образа (например, backend, frontend).
SERVICE_DIRДаПодкаталог с Dockerfile относительно корня репозитория.
BUILD_ARGSНетДополнительные аргументы для docker build (например, --build-arg VITE_API_URL=...).
REGISTRY_HOSTДаЗадаётся в проекте: registry.amztop.io.
REGISTRY_IMAGEДаЗадаётся в проекте: $REGISTRY_HOST/$CI_PROJECT_PATH.
DUAL_PUSHНетПо умолчанию "true" — копия образа уходит в старый registry. Для сервисов нового кластера ставьте "false".
CI_REGISTRY_USERАвтоgitlab-ci-token — подставляется GitLab.
CI_REGISTRY_PASSWORDАвтоЭфемерный токен джобы. Личные токены в переменные класть не нужно.

Авторизация в registry

registry.amztop.io не имеет собственной базы учёток — токены ему подписывает GitLab. Поэтому в пайплайне работает штатная пара $CI_REGISTRY_USER / $CI_REGISTRY_PASSWORD, а для доступа извне пайплайна (ноды, ansible, локальная машина) берётся deploy-токен со скоупом read_registry или write_registry.

Как наследовать ​

yaml
build:backend:
  extends: .docker_build
  tags:
    - new-server
  variables:
    SERVICE_NAME: backend
    SERVICE_DIR: back

build:frontend:
  extends: .docker_build
  tags:
    - new-server
  variables:
    SERVICE_NAME: frontend
    SERVICE_DIR: front
    BUILD_ARGS: "--build-arg VITE_API_URL=https://api.amzhub.ai"

Каждый блок extends создаёт конкретную CI-задачу, наследующую image, services, before_script и script из шаблона. Вы переопределяете только variables и добавляете тег раннера.

Тег new-server обязателен

Без него задачу заберёт раннер старого сервера, который не ходит в новый кластер. Тег нужен и задачам сборки, и задачам деплоя.

Кавычки в BUILD_ARGS

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

yaml
BUILD_ARGS: "--build-arg FOO=bar --build-arg BAZ=qux"

Не используйте YAML-списки — BUILD_ARGS подставляется непосредственно в команду docker build как одна строка.

Поведение тегирования образов ​

Образ получает один тег — по имени ветки:

ВеткаТег образа
main:prod
stage:stage
dev:dev

Пуш в любую другую ветку шаблон пропускает без сборки. Отдельного тега по commit SHA больше нет: за то, чтобы Nomad увидел новую сборку под неизменившимся тегом, отвечает переменная deploy_ts.


Задачи деплоя ​

Деплой описывается в самом сервисе: контейнер с Nomad CLI обращается к API кластера напрямую, без SSH и без копирования файлов на хост.

yaml
.nomad_new:
  stage: deploy
  tags:
    - new-server
  image:
    name: hashicorp/nomad:1.11.3
    entrypoint: [""]
  before_script:
    - export NOMAD_ADDR=$NEW_NOMAD_ADDR
    - export NOMAD_TOKEN=$NEW_NOMAD_TOKEN

Переменные ​

ПеременнаяОбязательнаОписание
NEW_NOMAD_ADDRАвтоАдрес Nomad API. Заведена на группу integrations.
NEW_NOMAD_TOKENАвтоТокен деплоя. Заведена на группу integrations.
CI_PIPELINE_IDАвтоПередаётся в джобу как deploy_ts, чтобы Nomad увидел новую выкатку.

Галка Protect у переменных

Protected-переменная видна только защищённым веткам, а dev обычно не защищена — деплой в dev тогда падает с пустым адресом Nomad. Переменные NEW_NOMAD_* держим без этой галки.

Как наследовать ​

yaml
deploy:dev:
  extends: .nomad_new
  script:
    - nomad job run -namespace=dev -var image_tag=dev
      -var deploy_ts=$CI_PIPELINE_ID
      -var-file=nomad/vars-dev.hcl nomad/hub.nomad.hcl
    - sleep 5
    - nomad job status -namespace=dev hub
  only:
    - dev

deploy:stage:
  extends: .nomad_new
  script:
    - nomad job run -namespace=stage -var image_tag=stage
      -var deploy_ts=$CI_PIPELINE_ID
      -var-file=nomad/vars-stage.hcl nomad/hub.nomad.hcl
    - sleep 5
    - nomad job status -namespace=stage hub
  only:
    - stage

deploy:prod:
  extends: .nomad_new
  script:
    - nomad job run -namespace=prod -var image_tag=prod
      -var deploy_ts=$CI_PIPELINE_ID
      -var-file=nomad/vars-prod.hcl nomad/hub.nomad.hcl
    - sleep 5
    - nomad job status -namespace=prod hub
  only:
    - main
  when: manual

Продакшн — только по кнопке

when: manual оставляет задачу deploy:prod ждать нажатия в пайплайне ветки main. Пуш в main сам по себе прод не выкатывает.

Соответствие веток и окружений ​

Ключевое слово only ограничивает каждую задачу деплоя одной веткой:

ВеткаОкружениеNamespaceЗадача деплоя
devdevdevdeploy:dev
stagestagestagedeploy:stage
mainprodproddeploy:prod (вручную)

Retry не передеплоит

Повторный запуск задачи в том же пайплайне отдаёт прежний deploy_ts. Nomad не создаёт новый деплой и сразу возвращает прошлый результат. Нужен новый пайплайн — Run pipeline или пустой коммит.


Полный пример ​

Готовый .gitlab-ci.yml целиком, с переменными и обеими стадиями, собран на странице Новый сервис: от репозитория до прода. Рабочий образец в проде — репозиторий integrations/docs.

Документация по инфраструктуре