Ответ
Для поддержания единообразия и избежания дублирования в нескольких репозиториях я использовал подход с шаблонами (templates), подключаемыми через директиву include. Это позволяет централизованно управлять логикой сборки, тестирования и деплоя.
1. Создаем общий шаблон в отдельном репозитории или проекте (gitlab-ci-templates):
Файл: .gitlab-ci-common.yml
# Общие переменные для всех проектов
variables:
DOCKER_REGISTRY: "registry.example.com"
DOCKER_BUILDKIT: 1
# Шаблон для сборки Docker-образа
.docker-build:
image: docker:20.10
services:
- docker:20.10-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
script:
- docker build --pull -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Шаблон для запуска unit-тестов (пример для Node.js)
.unit-tests:
image: node:16-alpine
script:
- npm ci
- npm run test:unit
artifacts:
when: always
reports:
junit: reports/junit.xml
paths:
- coverage/
2. В .gitlab-ci.yml каждого микросервиса подключаем и расширяем шаблоны:
# Подключаем общие шаблоны
include:
- project: 'devops/gitlab-ci-templates'
ref: main
file: '/.gitlab-ci-common.yml'
# Определяем стадии для этого конкретного проекта
stages:
- test
- build
- deploy
# Job для линтера (может быть уникальным для проекта)
eslint:
stage: test
image: node:16-alpine
script:
- npm ci
- npm run lint
# Job для сборки — наследуем и расширяем общий шаблон
build-image:
extends: .docker-build
stage: build
# Добавляем специфичные для проекта переменные
variables:
DOCKERFILE_PATH: ./Dockerfile.prod
# Job для деплоя в Kubernetes (также может быть шаблонным)
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging
- kubectl set image deployment/$CI_PROJECT_NAME app=$DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA -n staging
environment:
name: staging
url: https://staging.example.com
Преимущества такого подхода:
- Единый источник истины: Изменения в процессе CI/CD вносятся в одном месте.
- Гибкость: Каждый проект может переопределять или расширять шаблонные jobs под свои нужды.
- Снижение ошибок: Минимизируется копипаста конфигураций между репозиториями.
Ответ 18+ 🔞
Слушай, ну вот реально, как же заебало каждый раз в каждом проекте одно и то же писать! Как будто на дворе 2002-й год, и мы в каждом репозитории копируем один и тот же хуй в пальто. Терпения ноль ебать, честно.
Так вот, чтобы не быть распиздяем и не плодить этот пиздец, я сделал всё по уму. Создал отдельную папку с шаблонами — типа такой общей библиотеки для всех наших CI/CD-скриптов. Это, блядь, как общая кухня в коммуналке, только чтобы никто не срал.
1. Создал общий файл с шаблонами (.gitlab-ci-common.yml):
Вот смотри, что там внутри. Там всё, что нужно всем, собрано в одном месте.
# Общие переменные для всех проектов
variables:
DOCKER_REGISTRY: "registry.example.com"
DOCKER_BUILDKIT: 1
# Шаблон для сборки Docker-образа
.docker-build:
image: docker:20.10
services:
- docker:20.10-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
script:
- docker build --pull -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Шаблон для запуска unit-тестов (пример для Node.js)
.unit-tests:
image: node:16-alpine
script:
- npm ci
- npm run test:unit
artifacts:
when: always
reports:
junit: reports/junit.xml
paths:
- coverage/
2. А теперь в каждом микросервисе я просто подключаю эту общую хуйню и дополняю своим.
Вот как выглядит .gitlab-ci.yml в каком-нибудь сервисе:
# Подключаем общие шаблоны. Всё, бля, как по маслу.
include:
- project: 'devops/gitlab-ci-templates'
ref: main
file: '/.gitlab-ci-common.yml'
# А тут уже своя специфика, если нужна
stages:
- test
- build
- deploy
# Например, линтер свой, потому что у всех проекты разные, ёпта
eslint:
stage: test
image: node:16-alpine
script:
- npm ci
- npm run lint
# А сборка — это просто расширение общего шаблона. Красота!
build-image:
extends: .docker-build
stage: build
# И если надо, свой Dockerfile подсунуть
variables:
DOCKERFILE_PATH: ./Dockerfile.prod
# И деплой, который тоже можно было бы в шаблон вынести, если припрет
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging
- kubectl set image deployment/$CI_PROJECT_NAME app=$DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA -n staging
environment:
name: staging
url: https://staging.example.com
И в чём, собственно, профит, чувак?
- Один раз написал — везде работает. Изменил что-то в общем шаблоне — и на всех проектах автоматом обновилось. Никакой рутины, ебать мои старые костыли.
- Гибкость полная. Хочешь — используй шаблон как есть. Хочешь — дополнишь или перекроешь. Никто тебя в сраку не гонит.
- Меньше ошибок. Раньше можно было в одном месте опечататься, и потом полдня искать, почему сборка не запускается. А теперь доверия ебать ноль к копипасте, зато к шаблонам — полное. Сам от себя охуел, насколько стало проще.