CICD
Continuous Integration Continuous Deployment (Delivery)
Что такое Continuous Integration (CI)?
Continuous Integration (CI) — это практика разработки, при которой разработчики регулярно (обычно несколько раз в день) интегрируют свой код в общий репозиторий, и каждая интеграция автоматически проверяется сборкой и тестами.
Основные компоненты CI системы
CI система состоит из нескольких ключевых элементов, каждый из которых выполняет свою роль.
Первый компонент — это триггеры. CI должна знать, когда запускаться. Обычно это происходит при каждом пуше* в репозиторий, но можно настроить более сложные правила. Например, запускать полный набор тестов только для мердж-реквестов, а для обычных _коммитов_ — только быстрые проверки.
Второй компонент — окружение выполнения. Где будут запускаться наши проверки? Это может быть выделенный сервер, облачный раннер*, Docker-контейнер или даже ваш локальный компьютер. Главное требование — окружение должно быть чистым и воспроизводимым.
Третий компонент — сам набор проверок. Это сердце CI. Какие тесты запускать? Какие метрики собирать? Какие стандарты кода проверять? От этого зависит качество вашей CI системы.
Четвёртый компонент — система оповещений. CI бесполезна, если никто не знает о её результатах. Уведомления в чате, на почте, хоть где-то — всё это помогает команде быстро реагировать на проблемы.
Принципы эффективного CI
CI — это не просто автоматизация тестов. Это философия разработки, которая требует соблюдения определённых принципов.
Первый принцип — частая интеграция. Разработчики должны пушить код хотя бы раз в день. Чем дольше код живёт в изолированной ветке, тем сложнее будет его интегрировать. Представьте, что два разработчика месяц работают над разными фичами в одном модуле. Когда придёт время объединять их код, конфликтов будет масса.
Второй принцип — автоматизация всего. Если что-то можно автоматизировать, это нужно автоматизировать. Сборка, тесты, проверка кода, генерация документации, анализ производительности — всё это должна делать машина, а не человек.
Третий принцип — быстрая обратная связь. CI пайплайн должен выполняться быстро. Если разработчик ждёт результат час, он уже переключился на другую задачу и потерял контекст. Идеально — 5-10 минут на базовые проверки.
Реальные метрики эффективности CI
Как понять, что CI работает эффективно? Есть несколько ключевых метрик, на которые стоит обращать внимание.
Время выполнения пайплайна — критически важная метрика. Быстрый пайплайн (5-10 минут) позволяет быстро итерироваться. Медленный (30+ минут) тормозит разработку. Если пайплайн занимает больше 15 минут, стоит задуматься об оптимизации.
Частота запусков показывает, насколько активно команда использует CI. В хорошей команде CI запускается десятки раз в день. Если CI запускается раз в неделю перед релизом — это не continuous integration, а просто автоматизация тестов.
Процент успешных сборок говорит о качестве кода. В идеале должно быть 85-95% успешных сборок. Если меньше — разработчики пушат непроверенный код. Если 100% — возможно, тесты недостаточно строгие.
Путь к зрелому CI
CI — это не то, что внедряется за день. Это эволюционный процесс, который проходит через несколько стадий:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
Уровень  0:  Нет  CI  
└─  Всё  делается  вручную  
Уровень  1:  Базовая  автоматизация  
└─  Автоматическая  сборка  при  пуше  
└─  Уведомления  об  ошибках  
Уровень  2:  Тестирование  
└─  Unit-тесты  в  CI  
└─  Проверка  стиля  кода  
Уровень  3:  Расширенные  проверки  
└─  Интеграционные  тесты  
└─  Анализ  безопасности  
└─  Метрики  покрытия  кода  
Уровень  4:  Полная  автоматизация  
└─  Автоматический  деплой  на  тестовые  среды  
└─  Параллельное  выполнение  
└─  Динамические  окружения  для  каждой  ветки  
Уровень  5:  Оптимизация  
└─  Умное  кеширование  
└─  Выборочный  запуск  тестов  
└─  Предсказание  сбоев  на  основе  ML
```## Что такое Continuous Integration (CI)?
**Continuous Integration (CI)** — это практика разработки, при которой разработчики регулярно (обычно несколько раз в день) интегрируют свой код в общий репозиторий, и каждая интеграция автоматически проверяется сборкой и тестами.
### Основные компоненты CI системы
CI система состоит из нескольких ключевых элементов, каждый из которых выполняет свою роль.
Первый компонент — это  **триггеры**. CI должна знать,  **когда**  запускаться. Обычно это происходит при каждом  **_пуше_***  в репозиторий, но можно настроить более сложные правила. Например, запускать полный набор тестов только для  **_мердж-реквестов_***, а для обычных  **_коммитов_***  — только быстрые проверки.
Второй компонент — окружение выполнения.  **Где**  будут запускаться наши проверки? Это может быть выделенный сервер, облачный  _раннер_*, Docker-контейнер или даже ваш локальный компьютер. Главное требование — окружение должно быть чистым и воспроизводимым.
Третий компонент — сам набор проверок. Это сердце  **`CI`**. Какие тесты запускать? Какие метрики собирать? Какие стандарты кода проверять? От этого зависит качество вашей  **`CI`**  системы.
Четвёртый компонент — система оповещений.  **`CI`**  бесполезна, если никто не знает о её результатах. Уведомления в чате, на почте, хоть где-то — всё это помогает команде быстро реагировать на проблемы.
### Принципы эффективного CI
CI — это не просто автоматизация тестов. Это философия разработки, которая требует соблюдения определённых принципов.
Первый принцип — частая интеграция. Разработчики должны пушить код хотя бы раз в день. Чем дольше код живёт в изолированной ветке, тем сложнее будет его интегрировать. Представьте, что два разработчика месяц работают над разными фичами в одном модуле. Когда придёт время объединять их код, конфликтов будет масса.
Второй принцип — автоматизация всего. Если что-то можно автоматизировать, это нужно автоматизировать. Сборка, тесты, проверка кода, генерация документации, анализ производительности — всё это должна делать машина, а не человек.
Третий принцип — быстрая обратная связь. CI пайплайн должен выполняться быстро. Если разработчик ждёт результат час, он уже переключился на другую задачу и потерял контекст. Идеально — 5-10 минут на базовые проверки.
### Реальные метрики эффективности CI
Как понять, что  **`CI`**  работает эффективно? Есть несколько ключевых метрик, на которые стоит обращать внимание.
**Время выполнения пайплайна**  — критически важная метрика. Быстрый пайплайн (_5-10 минут_) позволяет быстро итерироваться. Медленный (_30+ минут_) тормозит разработку.  **Если** пайплайн занимает  **больше 15 минут**, стоит задуматься об оптимизации.
**Частота запусков показывает**, насколько активно команда использует  **`CI`**. В хорошей команде  **`CI`**  запускается десятки раз в день. Если  **`CI`**  запускается раз в неделю перед релизом — это  **не** _**continuous integration**_, а просто автоматизация тестов.
**Процент успешных сборок**  говорит о качестве кода. В идеале должно быть  **85-95% успешных сборок**. Если меньше — разработчики пушат непроверенный код.  **Если 100%**  — возможно, тесты  **недостаточно строгие**.
###  Путь к зрелому CI
**`CI`** — это не то, что внедряется за день. Это эволюционный процесс, который проходит через несколько стадий:
```text
Уровень  0:  Нет  CI  
└─  Всё  делается  вручную  
Уровень  1:  Базовая  автоматизация  
└─  Автоматическая  сборка  при  пуше  
└─  Уведомления  об  ошибках  
Уровень  2:  Тестирование  
└─  Unit-тесты  в  CI  
└─  Проверка  стиля  кода  
Уровень  3:  Расширенные  проверки  
└─  Интеграционные  тесты  
└─  Анализ  безопасности  
└─  Метрики  покрытия  кода  
Уровень  4:  Полная  автоматизация  
└─  Автоматический  деплой  на  тестовые  среды  
└─  Параллельное  выполнение  
└─  Динамические  окружения  для  каждой  ветки  
Уровень  5:  Оптимизация  
└─  Умное  кеширование  
└─  Выборочный  запуск  тестов  
└─  Предсказание  сбоев  на  основе  ML
Что такое Continuous Delivery (CD)?
Continuous Delivery (CD) — это практика, при которой код автоматически подготавливается к релизу в продакшн после прохождения всех проверок. Ключевое слово здесь — подготавливается. Код готов к деплою в любой момент, но само решение о релизе принимает человек.
Как CD расширяет CI: полная картина
CI и CD работают вместе, формируя единый конвейер доставки кода от разработчика к пользователю. Давайте посмотрим на полный процесс:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Разработчик пушит код
         ↓
┌─── CI часть ────────────────────────┐
│                                     │
│  1. Клонирование кода               │
│  2. Установка зависимостей          │
│  3. Линтинг и форматирование        │
│  4. Unit-тесты                      │
│  5. Интеграционные тесты            │
│  6. Анализ безопасности             │
│                                     │
└─────────────────────────────────────┘       
  Все проверки прошли
          ↓
┌─── CD часть ────────────────────────┐
│                                     │
│  7.  Сборка приложения              │
│  8.  Создание Docker-образа         │
│  9.  Публикация в реестр            │
│  10. Деплой на dev-окружение        │
│  11. Тесты на dev                   │
│  12. Деплой на стейджинг            │
│  13. Тесты на стейджинг             │
│  14. [MANUAL] Деплой на продакшн    │
│                                     │
└─────────────────────────────────────┘
CI - часть фокусируется на качестве кода. CD-часть фокусируется на подготовке к релизу. Вместе они обеспечивают быструю и безопасную доставку изменений.
Артефакты — основа CD
Центральное понятие в CD — это артефакт. Артефакт — это готовый к деплою пакет вашего приложения. Это может быть Docker-образ, JAR-файл, ZIP-архив, RPM-пакет — любой формат, который можно развернуть на сервере.
Важный принцип: “Build once, deploy many”. Артефакт собирается один раз и затем используется на всех окружениях. Никакой пересборки для продакшна! Тот же самый артефакт, который тестировался на стейджинге, идёт в продакшн. Это гарантирует, что в продакшн попадёт именно то, что было протестировано.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Принцип "Build once, deploy many":
       CI Pipeline
           │
      [Build Stage]
           │
      npm run build
           │
           ▼
  ┌────────────────────┐
  │   АРТЕФАКТ         │
  │ app-7f3a8b9.tar.gz │  ← Собирается ОДИН РАЗ
  │                    │     с уникальной версией (commit SHA)
  └────────┬───────────┘
           │
           ├─────────────────┬──────────────────┬─────────────────┐
           ▼                 ▼                  ▼                 ▼
    [Dev окружение]   [Test окружение]   [Staging]        [Production]
           │                 │                  │                 │
    Тот же артефакт   Тот же артефакт   Тот же артефакт  Тот же артефакт
    app-7f3a8b9       app-7f3a8b9       app-7f3a8b9       app-7f3a8b9
           │                 │                  │                 │
           ▼                 ▼                  ▼                 ▼
      Развернуто         Протестировано    Проверено        В продакшне
                                           заказчиком
Неправильно (старый подход):              Правильно (CD подход):
─────────────────────────────              ──────────────────────
                                           
Code → Build for Dev    → Deploy           Code → Build ONCE → app-v1.tar.gz
Code → Build for Test   → Deploy                              ↓
Code → Build for Stage  → Deploy                    ┌─────────┴─────────┐
Code → Build for Prod   → Deploy                    ▼         ▼         ▼
                                                   Dev      Stage     Prod
                                                   
  Каждый раз новая сборка =                 Один и тот же артефакт везде =
  = риск различий                           = гарантия идентичности
Окружения в CD
CD подразумевает наличие нескольких окружений, через которые проходит код на пути к продакшну. Типичная схема выглядит так:
Development (dev) — самое нестабильное окружение. Сюда деплоится каждый коммит в основную ветку. Здесь могут быть баги, здесь тестируются новые идеи. Разработчики используют его для проверки интеграции.
Testing/QA — окружение для тестировщиков. Более стабильное, чем dev. Сюда попадают изменения, которые прошли базовые проверки. Тестировщики проводят здесь ручное и автоматизированное тестирование.
Staging — копия продакшна. Такое же железо, такие же данные (обезличенные), такие же настройки. Здесь проводится финальная проверка перед релизом. Если работает на стейджинге, должно работать и на продакшне.
Production — боевое окружение, где работают реальные пользователи. Сюда попадает только проверенный код после всех стадий тестирования.
Стратегии деплоя в CD
CD не просто копирует файлы на сервер. Современные стратегии деплоя минимизируют риски и позволяют быстро откатиться при проблемах.
Blue-Green деплой использует два идентичных окружения. Blue — текущий продакшн, Green — новая версия. После проверки Green, трафик переключается с Blue на Green. Если что-то пошло не так, можно мгновенно вернуться на Blue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Текущее состояние:
┌─────────────┐
│   Blue v1   │ ← 100% трафика
└─────────────┘
┌─────────────┐
│  Green v2   │ ← 0% трафика (подготовка)
└─────────────┘
После переключения:
┌─────────────┐
│   Blue v1   │ ← 0% трафика (резерв)
└─────────────┘
┌─────────────┐
│  Green v2   │ ← 100% трафика
└─────────────┘
Canary деплой постепенно переводит трафик на новую версию. Сначала 5% пользователей, потом 25%, потом 50%, и так далее. Если метрики ухудшаются, деплой останавливается.
Rolling деплой обновляет реплики приложения по одному. В кластере из 10 серверов сначала обновляется первый, потом второй, и так далее. Приложение остаётся доступным в течение всего процесса.
Автоматизация релиз-процессов
CD автоматизирует не только технические аспекты деплоя, но и организационные процессы вокруг релиза.
Генерация заметок релиза происходит автоматически на основе коммитов и pull request'ов. Больше не нужно вручную собирать информацию о том, что вошло в релиз. CI/CD система знает все изменения между версиями и может сформировать читаемый список.
Версионирование тоже автоматизируется. Semantic Versioning (MAJOR.MINOR.PATCH) можно вычислять на основе типов коммитов.
 Ключевое изменение — увеличиваем MAJOR, новая фича — MINOR, багфикс — PATCH. Те самые v1.0.1 и подобные.
Управление конфигурацией становится частью пайплайна. Переменные окружения, секреты, настройки — всё это автоматически применяется при деплое. Никакого ручного редактирования конфигов на сервере.
Метрики и мониторинг в CD
CD не заканчивается на деплое. Важная часть — это мониторинг того, что происходит после релиза. Современные CD системы интегрируются с инструментами мониторинга и автоматически отслеживают ключевые метрики.
Если после деплоя метрики ухудшаются (растёт количество ошибок, падает производительность), система может автоматически откатить изменения. Это называется automated rollback и спасает от многих проблем.
Разница между Continuous Delivery и Continuous Deployment
Важно понимать разницу между Continuous Delivery и Continuous Deployment. В Continuous Delivery финальный деплой в продакшн требует ручного подтверждения. Человек принимает решение, когда релизить. В Continuous Deployment даже это автоматизировано — если все проверки пройдены, код автоматически идёт в продакшн.
Continuous Delivery подходит большинству компаний. Она даёт контроль над релизами, позволяет учитывать бизнес-факторы (не релизить в пятницу вечером, дождаться маркетинговой кампании). Continuous Deployment требует очень высокой зрелости процессов и подходит не всем.
Что такое Continuous Deployment?
На предыдущих уроках мы разобрали Continuous Integration и Continuous Delivery. CI автоматизирует проверку кода, CD автоматизирует подготовку к релизу. Но в CD остаётся один ручной шаг — нажатие кнопки для деплоя в продакшн. А что если убрать и его? Добро пожаловать в мир Continuous Deployment — полной автоматизации пути от коммита до продакшна.
Что такое Continuous Deployment
Continuous Deployment — это практика, при которой каждое изменение кода, прошедшее все автопроверки, автоматически попадает в продакшн без какого-либо ручного вмешательства. Написал код, запушил, прошли тесты — и через 15-30 минут изменения уже доступны потребителям.
Это радикальный подход. Никаких релиз-менеджеров, никаких бесконечных созвонов “деплоим или подождём?”. Код течёт в продакшн непрерывным потоком. Отсюда и название — непрерывное развёртывание.
Важно понимать разницу между Continuous Delivery и Continuous Deployment. В Delivery вы готовы релизить в любой момент, но решение принимает человек. В Deployment решение принимает машина на основе результатов тестов. Если всё зелёное — деплоим.
Минимальный пример Continuous Deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# .gitlab-ci.yml - полностью автоматический деплой
stages:
  - test
  - build
  - deploy
test:
  stage: test
  script:
    - npm test
    - npm run test:integration
    - npm run test:e2e
build:
  stage: build
  script:
    - docker build -t myapp:$CI_COMMIT_SHA .
    - docker push registry.example.com/myapp:$CI_COMMIT_SHA
deploy_production:
  stage: deploy
  script:
    - kubectl set image deployment/myapp myapp=registry.example.com/myapp:$CI_COMMIT_SHA
    - ./check_deployment_health.sh
  environment:
    name: production
  only:
    - main  # Только для основной ветки
Что здесь происходит:
- test— запускаются ВСЕ виды тестов (unit, интеграционные, e2e)
- build— создаётся Docker-образ с уникальным тегом
- deploy_production— автоматический деплой в Kubernetes без подтверждения
- check_deployment_health.sh— проверка, что деплой прошёл успешно
Никаких кнопок, никаких ожиданий. Код сам доезжает до продакшна, если проходит все проверки.
Как работает полный цикл Continuous Deployment
09:30  Разработчик пушит код
         │
         ├─ 09:31  Unit-тесты (2 мин)
         ├─ 09:33  Интеграционные тесты (3 мин)
         ├─ 09:36  E2E тесты (5 мин)
         ├─ 09:41  Сборка и публикация образа (2 мин)
         ├─ 09:43  Деплой не на всех пользователей (1% трафика)
         ├─ 09:45  Мониторинг метрик (5 мин)
         ├─ 09:50  Расширение на 10% трафика
         ├─ 09:55  Мониторинг метрик (5 мин)
         ├─ 10:00  Расширение на 50% трафика
         ├─ 10:05  Мониторинг метрик (5 мин)
         └─ 10:10  Полный релиз на 100% трафика
         
10:10  Код в продакшне у всех пользователей
Итого: 40 минут от пуша до продакшна
Это реальный сценарий из практики крупных технологических компаний. Amazon, например, деплоит код в продакшн каждые 11.6 секунд. Netflix — каждые 16 минут. Google — тысячи раз в день. И всё это происходит автоматически.
Предпосылки для Continuous Deployment
Continuous Deployment — это фундаментальное изменение подхода к разработке. Чтобы безопасно внедрить Continuous Deployment, нужны определённые предпосылки.
Первое и главное — это культура качества. Каждый разработчик должен понимать, что его код может попасть в продакшн через полчаса. Нет времени на “потом допилю”. Код должен быть качественным с первого раза.
Второе — это покрытие тестами. И не просто формальные 80% покрытия unit-тестами. Нужны интеграционные тесты, контрактные тесты, e2e тесты, тесты производительности. Если тесты пропустили баг в продакшн, виноваты не тесты, а их недостаток.
Третье — это мониторинг и observability. Метрики, логи, трейсы — всё должно быть настроено и работать.
Стратегии безопасного Continuous Deployment
Автоматический деплой в продакшн звучит страшно. А если код сломает всё? А если тесты пропустили критичный баг? Современные стратегии минимизируют эти риски.
Progressive Rollout (постепенный релиз) — самая популярная стратегия. Новая версия сначала получает малую часть трафика, затем постепенно расширяется:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Стадии Progressive Rollout:
│
├─ Canary: 1-2% трафика
│  └─ Мониторинг 10-15 минут
│      └─ Метрики в норме? → Продолжаем
│      └─ Проблемы? → Откат
│
├─ Early Adopters: 5-10% трафика  
│  └─ Мониторинг 20-30 минут
│      └─ Часто это бета-тестеры или сотрудники
│
├─ Partial: 25-50% трафика
│  └─ Мониторинг 30-60 минут
│      └─ A/B тестирование старой и новой версии
│
└─ Full Rollout: 100% трафика
   └─ Финальный мониторинг
Feature Flags (флаги функциональности) позволяют отделить деплой кода от включения функциональности. Код может быть в продакшне, но выключен. Включение происходит через изменение конфигурации без деплоя.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Пример использования feature flags
function processPayment(order) {
    if (featureFlags.isEnabled('new-payment-system')) {
        // Новая логика оплаты
        return newPaymentProcessor.process(order);
    } else {
        // Старая проверенная логика
        return legacyPaymentProcessor.process(order);
    }
}
// Feature flags в конфигурации
{
    "new-payment-system": {
        "enabled": true,
        "percentage": 10,  // Включено для 10% пользователей
        "users": ["beta-tester-1", "beta-tester-2"]  // И для конкретных пользователей
    }
}
Инфраструктура для Continuous Deployment
Continuous Deployment требует особой инфраструктуры. Обычная впска с nginx уже не подойдёт. Нужны инструменты, которые поддерживают динамическое управление и мониторинг.
Оркестраторы контейнеров (Kubernetes) обеспечивают плавное обновление приложений. Они умеют делать rolling updates (бесшовное обновление версий), следить за подами, автоматически перезапускать упавшие контейнеры.
Service Mesh (Istio) добавляет крутую маршрутизацию трафика. Можно направить 1% трафика на новую версию, не меняя код приложения. Также Service Mesh собирает детальные метрики о каждом запросе.
Системы мониторинга (Prometheus + Grafana) отслеживают состояние приложения в реальном времени.
Когда НЕ нужен Continuous Deployment
Continuous Deployment — это не серебряная пуля. Есть ситуации, когда он не подходит или даже вреден.
Регулируемые индустрии (банки, медицина, авиация) часто требуют ручного контроля релизов. Каждое изменение должно быть задокументировано, проверено комплаенс-отделом, одобрено регулятором. Автоматический деплой здесь невозможен по определению.
B2B продукты с крупными клиентами тоже плохо подходят для Continuous Deployment. Клиенты хотят знать заранее об изменениях, проводить свои тесты, планировать обучение пользователей.
Embedded системы и десктоп-приложения технически сложно обновлять непрерывно. Пользователь должен скачать и установить обновление. IoT-устройства могут быть офлайн.
Что такое .gitlab-ci.yml
Файл .gitlab-ci.yml — это инструкция для GitLab о том, что делать с вашим кодом. Вы кладёте этот файл в корень репозитория, и при каждом пуше GitLab читает его и выполняет описанные действия.
Представьте, что вы пишете челу в телегу: “Когда придёшь, сначала проверь тесты, потом собери проект, и если всё хорошо — задеплой на сервер”. Файл .gitlab-ci.yml — это то же самое, только для GitLab CI.
Файл написан в формате YAML — это простой текстовый формат, где структура задаётся отступами. Главное правило: используйте пробелы, а не табы. Два пробела — стандарт для отступов в YAML.
Минимальный пример
Начнём с самого простого работающего файла .gitlab-ci.yml:
1
2
3
4
5
6
7
# .gitlab-ci.yml
test:
  script:
    - echo "Запускаем тесты"
    - npm test
Этого достаточно, чтобы GitLab создал пайплайн. Что здесь происходит:
- test:— название джобы (можете назвать как угодно)
- script:— ключевое слово, говорящее “вот команды для выполнения”
- - echo "..."и- - npm test— команды, которые выполнятся по порядку
При пуше этого файла GitLab запустит Docker-контейнер, выполнит эти команды и покажет результат — прошли тесты или нет.
Добавляем структуру: стейджи
Теперь сделаем пайплайн с тремя джобами и правильной структурой:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# .gitlab-ci.yml
# Определяем порядок стейджей
stages:
  - test
  - build
  - deploy
# Джоба для тестов
run_tests:
  stage: test
  script:
    - echo "Запускаем тесты..."
    - npm install
    - npm test
# Джоба для сборки
build_app:
  stage: build
  script:
    - echo "Собираем приложение..."
    - npm run build
    - echo "Сборка готова!"
# Джоба для деплоя
deploy_app:
  stage: deploy
  script:
    - echo "Деплоим на сервер..."
    - echo "Деплой завершён!"
  when: manual  # Требует ручного запуска
Разберём новые элементы:
- stages:— определяет последовательность выполнения. Сначала test, потом build, потом deploy
- stage: test— привязывает джобу к стейджу
- when: manual— эта джоба не запустится автоматически, нужно будет нажать кнопку в интерфейсе GitLab
Как это работает: пошаговое выполнение
Что происходит при пуше:
1. GitLab видит новый коммит
       ↓
2. Читает файл .gitlab-ci.yml
       ↓
3. Создаёт пайплайн с тремя стейджами
       ↓
4. Запускает джобу run_tests
       ↓
5. Если тесты прошли → запускает build_app
       ↓
6. Если сборка успешна → показывает кнопку для deploy_app
       ↓
7. Если нажать кнопку → выполняет деплой
Важный момент: если любая джоба упадёт (вернёт ошибку), пайплайн остановится. Следующие стейджи не запустятся. Это защищает от деплоя сломанного кода.
Основные инструкции GitLab CI
В GitLab CI есть десятки инструкций, но для начала достаточно знать самые важные. Вот топ-7, которые покрывают 80% потребностей:
script — команды для выполнения. Это единственная обязательная инструкция:
1
2
3
4
test:
  script:
    - npm test
image — Docker-образ, в котором запустится джоба:
1
2
3
4
5
test:
  image: node:18  # Используем Node.js версии 18
  script:
    - npm test
only/except — условия запуска джобы:
1
2
3
4
5
6
deploy:
  script:
    - ./deploy.sh
  only:
    - main  # Только для ветки main
variables — переменные окружения:
1
2
3
4
5
6
test:
  variables:
    NODE_ENV: test
  script:
    - npm test
artifacts — файлы, которые нужно сохранить:
1
2
3
4
5
6
7
build:
  script:
    - npm run build
  artifacts:
    paths:
      - dist/  # Сохраняем папку dist
cache — кеширование для ускорения:
1
2
3
4
5
6
7
8
test:
  cache:
    paths:
      - node_modules/  # Кешируем зависимости
  script:
    - npm install
    - npm test
needs — зависимости между джобами:
1
2
3
4
5
deploy:
  needs: ["build"]  # Запустится сразу после build
  script:
    - ./deploy.sh
Возможности пайплайнов: визуальные примеры
GitLab CI позволяет создавать очень сложные пайплайны. Давайте посмотрим на визуальные примеры того, что можно настроить.
Параллельное выполнение
Обычный пайплайн (медленно, ~15 минут):
[Тесты 5м] → [Линтер 2м] → [Сборка 5м] → [Деплой 3м]
Параллельный пайплайн (быстро, ~8 минут):
         ┌─[Тесты 5м]─┐
Старт ───┼─[Линтер 2м]┼─→ [Сборка 5м] → [Деплой 3м]
         └─[E2E 3м]───┘
        
Джобы в одном стейдже идут параллельно
Матричные сборки
Тестирование на разных версиях:
test:             ┌─→ [Node 16 + Chrome]
  matrix:         ├─→ [Node 16 + Firefox]  
    node: [16,18] ├─→ [Node 18 + Chrome]
    browser:      └─→ [Node 18 + Firefox]
      - chrome
      - firefox
      
Создаёт 4 джобы автоматически
Динамические пайплайны
Child pipelines - пайплайн может создавать другие пайплайны:
Главный пайплайн
    ├─→ Анализ кода
    ├─→ Определение, что изменилось
    └─→ Запуск нужных пайплайнов:
         ├─→ Frontend pipeline (если изменился frontend/)
         ├─→ Backend pipeline (если изменился backend/)
         └─→ Docs pipeline (если изменились документы)
Multi-project pipelines
Пайплайн может триггерить пайплайны в других проектах:
Проект API                  Проект Frontend
[Тесты] → [Деплой] ────────→ [Триггер: пересобрать]
                                    ↓
                             [Тесты] → [Сборка] → [Деплой]
Ручные подтверждения и откаты
Безопасный деплой с возможностью отката:
[Тесты] → [Сборка] → [Stage] → ⏸️ [Ручное подтверждение] → [Прод]
                                          ↓
                                   При проблемах
                                          ↓
                                   🔄 [Откат на предыдущую версию]
Реальные возможности GitLab CI
То, что мы рассмотрели — это только верхушка айсберга. GitLab CI может гораздо больше:
Работа с Docker и Kubernetes — можно собирать Docker-образы, пушить их в хранилище, деплоить в Kubernetes кластеры. Всё это нативно поддерживается.
Безопасность и качество кода — встроенные сканеры уязвимостей, проверка зависимостей, статический анализ кода, проверка лицензий. GitLab может автоматически находить проблемы безопасности.
Окружения и Review Apps — для каждой ветки можно создавать временное окружение с превью. Удобно для тестирования и код-ревью.
Мониторинг и метрики — GitLab собирает метрики о времени выполнения, частоте падений, покрытии тестами. Можно отслеживать, как меняется качество кода.
Интеграции — GitLab CI интегрируется с Jira, Prometheus, Grafana и десятками других инструментов. Можно отправлять уведомления, создавать задачи, собирать метрики.
Эволюция вашего .gitlab-ci.yml
Как обычно развивается пайплайн:
Неделя 1: "Просто запустим тесты"
└─ 5 строк конфигурации
Месяц 1: "Добавим сборку и деплой"
└─ 30 строк конфигурации
Месяц 3: "Нужны разные окружения"
└─ 100 строк конфигурации
Месяц 6: "Оптимизируем и структурируем"
└─ 200 строк + внешние шаблоны
Год: "Полная автоматизация всего"
└─ Несколько файлов, сложные правила, интеграции
Начинается с простого — автоматизация тестов. Потом добавляем сборку. Потом деплой.
Первый Jenkinsfile: разбор структуры
Jenkinsfile — это файл с инструкциями для Jenkins, написанный на языке Groovy. Если .gitlab-ci.yml — это простая декларативная конфигурация, то Jenkinsfile — это почти полноценная программа. Это даёт больше гибкости, но и требует больше знаний.
В Jenkins есть два типа синтаксиса для пайплайнов: Declarative Pipeline (декларативный) и Scripted Pipeline (скриптовый). Декларативный — более новый и простой, именно с него мы начнём. Скриптовый — более гибкий, но и более сложный, это по сути чистый Groovy-код.
Главное отличие от GitLab CI: в Jenkins пайплайн описывается как код с чёткой структурой, а не как набор независимых джоб. Здесь есть обязательные блоки, которые должны быть в определённом порядке.
Минимальный пример
Вот самый простой работающий Jenkinsfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Jenkinsfile
pipeline {
    agent any
    
    stages {
        stage('Test') {
            steps {
                echo 'Запускаем тесты...'
                sh 'npm test'
            }
        }
    }
}
Что здесь происходит:
- pipeline { }— обязательный корневой блок, всё должно быть внутри
- agent any— на каком агенте выполнять (any = на любом доступном)
- stages { }— контейнер для всех стейджей
- stage('Test') { }— один стейдж с именем Test
- steps { }— шаги внутри стейджа
- echoи- sh— команды для выполнения
Обратите внимание: в Jenkins используются фигурные скобки вместо отступов, как в обычном коде. Это Groovy — язык, похожий на Java.
Сравнение синтаксиса: GitLab CI vs Jenkins
Давайте сравним одинаковые пайплайны в разных системах:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
GitLab CI (.gitlab-ci.yml):          Jenkins (Jenkinsfile):
─────────────────────────────         ─────────────────────────────
stages:                               pipeline {
  - test                                  agent any
  - build                                 
  - deploy                                stages {
                                             stage('Test') {
test_job:                                        steps {
  stage: test                                        sh 'npm test'
  script:                                        }
    - npm test                               }
                                             
build_job:                                   stage('Build') {
  stage: build                                   steps {
  script:                                            sh 'npm run build'
    - npm run build                              }
                                             }
deploy_job:                                  
  stage: deploy                              stage('Deploy') {
  script:                                        steps {
    - echo "Deploy"                                  echo 'Deploy'
  when: manual                                   }
                                             }
                                         }
                                     }
Ключевые отличия: Jenkins требует жёсткую структуру (pipeline → stages → stage → steps), в GitLab джобы независимы, в Jenkins стейджи вложены друг в друга. В GitLab используется YAML, в Jenkins — Groovy.
Полный пример с тремя стейджами
Теперь создадим более реалистичный пайплайн с тестированием, сборкой и деплоем:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Jenkinsfile
pipeline {
    // Где выполнять пайплайн
    agent any
    
    // Переменные окружения
    environment {
        NODE_ENV = 'production'
        APP_NAME = 'my-app'
    }
    
    // Стейджи пайплайна
    stages {
        stage('Test') {
            steps {
                echo 'Запускаем тесты...'
                sh 'npm install'
                sh 'npm test'
            }
        }
        
        stage('Build') {
            steps {
                echo 'Собираем приложение...'
                sh 'npm run build'
                echo "Сборка ${APP_NAME} завершена!"
            }
        }
        
        stage('Deploy') {
            steps {
                echo 'Деплоим на сервер...'
                sh './deploy.sh'
            }
        }
    }
    
    // Действия после пайплайна
    post {
        success {
            echo 'Пайплайн успешно завершён!'
        }
        failure {
            echo 'Пайплайн упал :('
        }
    }
}
Новые элементы:
- environment { }— блок для переменных окружения
- ${APP_NAME}— использование переменной в строке
- post { }— действия после завершения пайплайна
- successи- failure— условия для post-действий
Основные блоки и директивы Jenkins
В декларативном пайплайне Jenkins есть строгая структура блоков. Вот основные из них:
agent — где выполнять пайплайн:
1
2
3
4
5
agent any                    // На любом доступном агенте
agent none                   // Не назначать агента (для стейджей)
agent { label 'linux' }      // На агенте с меткой linux
agent { docker 'node:18' }   // В Docker-контейнере
environment — переменные окружения:
1
2
3
4
5
6
environment {
    PATH = "/usr/local/bin:$PATH"
    BUILD_ID = "${env.BUILD_NUMBER}"
    SECRET = credentials('my-secret')  // Из Jenkins credentials
}
options — настройки пайплайна:
1
2
3
4
5
6
options {
    timeout(time: 1, unit: 'HOURS')   // Максимальное время выполнения
    retry(3)                          // Повторить при падении
    timestamps()                      // Добавить временные метки в лог
}
triggers — когда запускать пайплайн:
1
2
3
4
5
triggers {
    cron('H 4 * * 1-5')      // По расписанию (будни в 4 утра)
    pollSCM('H/5 * * * *')   // Проверять Git каждые 5 минут
}
parameters — параметры для ручного запуска:
parameters {
    string(name: 'VERSION', defaultValue: '1.0.0')
    choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'prod'])
    booleanParam(name: 'RUN_TESTS', defaultValue: true)
}
Возможности Jenkins
Blue Ocean интерфейс
1
2
3
4
5
6
7
8
9
10
Jenkins превращает ваш Jenkinsfile в красивую визуализацию:
○──────○──────○──────○──────●
Test   Build  Deploy Stage  Production
✓      ✓      ✓      ✓      ⏸ (ждёт подтверждения)
2m     5m     1m     30s    
Кликаете на любой этап — видите логи
Мультибранч пайплайны (Multibranch)
Jenkins автоматически создаёт пайплайны для каждой ветки:
main       ○──○──○──○  ✓ Прошёл
develop    ○──○──●     ✗ Упал на тестах  
feature/1  ○──○──○     ⚡ В процессе
feature/2  ○──○──○──○  ✓ Прошёл
Каждая ветка = отдельный пайплайн
Скриптовый vs Декларативный синтаксис
В Jenkins есть два способа писать пайплайны. Мы изучили декларативный, но есть ещё скриптовый:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Декларативный (структурированный):     Скриптовый (гибкий):
────────────────────────────────        ──────────────────────
pipeline {                              node {
    agent any                               stage('Test') {
    stages {                                    sh 'npm test'
        stage('Test') {                     }
            steps {                         
                sh 'npm test'               if (env.BRANCH_NAME == 'main') {
            }                                   stage('Deploy') {
        }                                           sh './deploy.sh'
    }                                           }
}                                           }
                                       }
Декларативный — проще                  Скриптовый — жесткий
Отличия Jenkins от GitLab CI: итоговая таблица
1
2
3
4
5
6
7
8
9
Аспект          GitLab CI                   Jenkins
──────────────────────────────────────────────────────────
Файл            .gitlab-ci.yml              Jenkinsfile
Формат          YAML                        Groovy (код)
Структура       Плоская (джобы)             Вложенная (pipeline→stages)
Гибкость        Средняя                     Очень высокая
Сложность       Простой старт               Требует изучения
UI              Встроен в GitLab            Отдельный (+ Blue Ocean)
Агенты          Runner'ы                    Master + Agents