9. Реальные кейсы (обязательно)
Эта тема — “практика под требования Middle+”: вы увидите, как GitLab CI/CD проектируется под типичные production‑условия. Внутри — монорепа с большим числом сервисов, миграция с Jenkins, pipeline на 100+ jobs и измеримая оптимизация времени сборки (30 → 10 минут).
Термины и ожидания
| Термин | Определение |
|---|---|
| Monorepo | Репозиторий с множеством сервисов/пакетов в одном Git‑project. |
| Multiplatform pipeline | Pipeline, который запускает разные типы шагов (lint/test/build/deploy) для разных частей монорепы. |
| Drift | Расхождение между тем, что описано в Git, и тем, что реально развернуто (часто актуально при GitOps). |
| MTTR | Среднее время восстановления после инцидента (в CI/CD — время от падения до исправления/перезапуска). |
| Job budget | Идея “у каждого job’а есть стоимость”: время/стоимость runner’а/нагрузка на сервисы. |
Кейc 1: монорепа с 20 сервисами
Типичная проблема
- pipeline “летает”, но делает лишнюю работу на каждый коммит
- сложно понять, какие job’ы имеют отношение к изменениям
- растёт стоимость runner’ов и очередь
Production‑подход: “контракт изменений” через rules:changes
Идея: для каждого сервиса запускать job’ы только если изменились файлы этого сервиса.
Пример (скелет):
# .gitlab-ci.yml
stages: [lint, build, test]
lint_base:
image: python:3.12-slim
script:
- ruff check .
build_base:
image: docker:27
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
lint_service_a:
extends: lint_base
stage: lint
rules:
- changes:
- services/service-a/**/*
when: on_success
- when: never
script:
- ruff check services/service-a
lint_service_b:
extends: lint_base
stage: lint
rules:
- changes:
- services/service-b/**/*
when: on_success
- when: never
script:
- ruff check services/service-b
build_service_a:
extends: build_base
stage: build
rules:
- changes:
- services/service-a/**/*
- Dockerfile.services-a
when: on_success
- when: never
Комментарий:
when: never— страховка, чтобы job не запускался “случайно”.- для монорепы обязательно снижайте количество job’ов, которые выполняются на каждом коммите.
Best practice: выносить логику в templates + include
Когда сервисов много, .gitlab-ci.yml превращается в кашу. Решение:
- общий template build/lint
- сервисы подключают шаблон через
include+ variables
Кейс 2: миграция с Jenkins на GitLab CI/CD
Типичная проблема миграции
- “перенесли пайплайн 1:1”, но он стал дольше и нестабильнее
- секреты/права не перенесли правильно
- потеряли артефакты, тест‑репорты и отчёты
Production‑план миграции (пошагово)
- Mapping: stages/jobs Jenkins → stages/jobs GitLab (и фиксируете ожидания: что должно быть в artifacts, что считается “успехом”).
- Runner стратегия: выбираете shared vs specific runner (особенно для docker‑build и секретов).
- Секреты: переносите в masked/protected variables или vault integration.
- Гарантия артефактов: test reports/manifest build должны быть доступны следующим job’ам.
- Параллельный запуск: сначала гоняете GitLab pipeline параллельно с Jenkins на ограниченном наборе веток.
Мини‑пример “сквозного” job’а деплоя:
deploy_production:
stage: deploy
image: bitnami/kubectl:1.30
environment: production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
- when: never
script:
- kubectl -n production apply -f k8s/
- kubectl -n production rollout status deploy/myapp --timeout=180s
Комментарий:
- manual на production + protected ветка снижает риск “случайного деплоя”.
- rollout status делает деплой проверяемым и предсказуемым.
Кейс 3: pipeline на 100+ jobs
Типичная проблема
- вы переплачиваете за “каждый job на каждый коммит”
- пайплайн становится нечитаемым
- быстрые job’ы ждут медленных из-за stage-барьеров
Production‑подход: DAG через needs + “стрижка” по rules
Пример: build завершился — можно запускать тесты, не дожидаясь конца stage.
build:
stage: build
script:
- ./build.sh
artifacts:
paths: [dist/]
test:
stage: test
needs:
- job: build
artifacts: true
script:
- ./test.sh
Best practices:
- используйте
needsтолько там, где реально ускоряет (не усложняйте граф “ради графа”) - выкидывайте job’ы из pipeline через
rules:changes/existsиwhen: never
Кейс 4: оптимизация времени сборки 30 → 10 минут
Метрика успеха
- определите, что именно сокращаем: lint/test/build/deploy
- зафиксируйте baseline и цель (например, build с 25 минут до 8–10)
Production‑техники
1) Правильный cache (ключ менять только при изменении зависимостей).
build:
image: python:3.12-slim
cache:
key:
files: ["requirements.txt", "pyproject.toml"]
paths:
- .cache/pip
script:
- pip install -r requirements.txt
- python -m build
2) Минимизация artifacts (не таскайте “лишнее” на каждый job).
3) Снижение количества запускаемых job’ов через rules:changes.
4) Docker layer caching / buildkit (если используете docker build).
Идея через buildx cache:
docker buildx build \
--cache-from=type=registry,ref="$CI_REGISTRY_IMAGE:buildcache" \
--cache-to=type=registry,ref="$CI_REGISTRY_IMAGE:buildcache",mode=max \
-t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" \
--push .
Комментарий:
- оптимизация “быстро” обычно достигается комбинацией: cache + rules + needs.
Production‑чеклист для “реальных кейсов”
- pipeline запускается только когда нужно (
rules:changes,when: never) - есть DAG ускорения (
needs) там, где это оправдано - артефакты минимальные и нужные
- кэш детерминирован по файлам зависимостей
- production deploy — только manual + protected ветка/окружение
- есть диагностический след: job logs + artifacts для расследования