CI/CD для корпоративного ПО: как построить независимую инженерную платформу

Автор: Инженер-программист

Практическое руководство с примерами, расчётами и чек-листами

Почему CI/CD — это не про DevOps-моду, а про выживание

Реальный кейс:
Российский банк потратил 80 млн руб. на разработку системы управления рисками. Всё работало через GitLab Cloud и AWS. В марте 2022 — блокировка. Система встала. Данные недоступны. Восстановление с нуля заняло 4 месяца и ещё 35 млн руб.

Что пошло не так:

  • CI/CD был в облаке вендора
  • Артефакты хранились в Docker Hub
  • Secrets в HashiCorp Cloud
  • Документация в Confluence Cloud

Урок: Без собственной инженерной платформы нет контроля над продуктом.

Компании, которые начали с платформы, а не с продукта:

  • Palantir: Собственная CI/CD с 2004 года, развёртывание в изолированных контурах
  • Anduril: Полный цикл от сборки до деплоя на edge-устройства
  • Яндекс: Собственная платформа разработки задолго до сервисов

Что такое CI/CD на практике (без воды)

CI (Continuous Integration) — конвейер качества

Что происходит при каждом коммите:

1. Commit → Git
   ↓
2. Trigger → CI-сервер активируется
   ↓
3. Checkout → Код загружается в чистое окружение
   ↓
4. Build → Сборка (Maven/Gradle/npm)
   ↓
5. Unit Tests → 500+ тестов за 2 минуты
   ↓
6. SAST → Сканирование уязвимостей (SonarQube)
   ↓
7. Dependency Check → Проверка библиотек
   ↓
8. Package → Создание артефакта (Docker image, JAR)
   ↓
9. Push → Артефакт в registry
   ↓
10. Report → Результаты в MR/PR

Время выполнения: 5–15 минут для среднего проекта.

Критерий успеха CI: Если pipeline зелёный — изменение безопасно интегрировать.

CD (Continuous Delivery/Deployment) — управляемая доставка

Два варианта:

Continuous Delivery (ручной релиз):

Артефакт готов → Одобрение → Деплой в staging → 
Тестирование → Одобрение → Деплой в production

Continuous Deployment (автоматический релиз):

Артефакт готов → Автодеплой в staging → 
Проверки пройдены → Автодеплой в production

Для корпоративных систем: Используют Continuous Delivery с обязательным одобрением для production.


Архитектура CI-сервера: минимум для старта

Железо и софт

Конфигурация CI-сервера (on-premise):

КомпонентТребованияСтоимость
Сервер16 CPU, 64GB RAM, 1TB NVMe300–500 тыс. руб.
ОСUbuntu 22.04 LTS / RHEL 9Бесплатно / лицензия
GitLab CE/EESelf-hostedБесплатно / от 500 тыс./год
Docker/PodmanContainer runtimeБесплатно
RegistryHarbor / GitLab RegistryБесплатно
МониторингPrometheus + GrafanaБесплатно

Итого на старт: 800 тыс.–1.5 млн руб. (единоразово) + 500 тыс.–1 млн руб./год на поддержку.

Базовая схема инфраструктуры

┌─────────────────────────────────────────┐
│  Разработчики (VPN)                     │
└────────────┬────────────────────────────┘
             ↓
┌────────────────────────────────────────┐
│  Git Server (GitLab on-prem)           │
│  • Репозитории                         │
│  • MR/PR процесс                       │
│  • Webhooks → CI                       │
└────────────┬───────────────────────────┘
             ↓
┌────────────────────────────────────────┐
│  CI Server (GitLab Runner)             │
│  • Executor: Docker                    │
│  • Pipeline выполнение                 │
│  • Артефакты → Registry                │
└────────────┬───────────────────────────┘
             ↓
┌────────────────────────────────────────┐
│  Artifact Storage                       │
│  • Docker Registry (Harbor)            │
│  • Maven/npm Repository (Nexus)        │
└────────────┬───────────────────────────┘
             ↓
┌────────────────────────────────────────┐
│  Environments                           │
│  • Staging (auto-deploy)               │
│  • Production (manual approval)        │
└─────────────────────────────────────────┘

Pipeline как код: пример из реальной жизни

Задача: Java-приложение с PostgreSQL

Файл .gitlab-ci.yml (упрощённый):

yaml

stages:
  - build
  - test
  - security
  - package
  - deploy

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

build:
  stage: build
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn clean compile
  cache:
    paths:
      - .m2/repository
  only:
    - main
    - merge_requests

unit-tests:
  stage: test
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn test
  artifacts:
    reports:
      junit: target/surefire-reports/TEST-*.xml
  coverage: '/Total.*?([0-9]{1,3})%/'

integration-tests:
  stage: test
  image: maven:3.9-eclipse-temurin-17
  services:
    - postgres:15
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
  script:
    - mvn verify -Pintegration-tests

security-scan:
  stage: security
  image: sonarqube:community
  script:
    - sonar-scanner -Dsonar.projectKey=$CI_PROJECT_NAME
  only:
    - main

dependency-check:
  stage: security
  image: owasp/dependency-check:latest
  script:
    - dependency-check.sh --project "$CI_PROJECT_NAME" --scan .
  artifacts:
    paths:
      - dependency-check-report.html

package:
  stage: package
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA .
    - docker tag $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA 
                 $CI_REGISTRY/$CI_PROJECT_PATH:latest
    - docker push $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY/$CI_PROJECT_PATH:latest
  only:
    - main

deploy-staging:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache openssh
    - ssh deploy@staging "docker pull $CI_REGISTRY/$CI_PROJECT_PATH:latest"
    - ssh deploy@staging "docker-compose up -d"
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - main

deploy-production:
  stage: deploy
  image: alpine:latest
  script:
    - ssh deploy@prod "docker pull $CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA"
    - ssh deploy@prod "docker-compose up -d"
  environment:
    name: production
    url: https://example.com
  when: manual
  only:
    - main

Что делает этот pipeline:

  1. Собирает Java-приложение
  2. Прогоняет unit-тесты (500+ тестов)
  3. Запускает интеграционные тесты с PostgreSQL
  4. Сканирует код на уязвимости (SAST)
  5. Проверяет зависимости (OWASP)
  6. Собирает Docker-образ
  7. Автоматически деплоит в staging
  8. Деплоит в production по кнопке

Время выполнения: 8–12 минут.


Main-ветка: точка максимального контроля

Правила работы с main

Запреты:

  • Прямой push в main
  • Force push
  • Коммиты без issue/task ID
  • Merge без прохождения CI

Обязательные проверки перед merge:

  • CI pipeline успешен
  • Code review от минимум 2 человек
  • Тесты покрывают изменения
  • Документация обновлена (если нужно)

Настройка в GitLab:

yaml

# .gitlab/merge_request_templates/Default.md
## Описание
<!-- Что изменено и зачем -->

## Чек-лист
- [ ] Тесты добавлены/обновлены
- [ ] Документация обновлена
- [ ] CI pipeline зелёный
- [ ] Нет breaking changes (или они задокументированы)

## Связанные issue
Closes #123
```

**Protected branches settings:**
- Allowed to merge: Maintainers
- Allowed to push: Nobody
- Require approval: 2 reviewers
- Require passing pipeline: Yes

---

## Безопасность: VPN, ключи и секреты

### Схема доступа к Git-серверу
```
Разработчик → VPN → GitLab (on-prem)
              ↓
         SSH-ключ (Ed25519)
              ↓
         2FA (обязательно)

Настройка SSH-ключа:

bash

# Генерация ключа
ssh-keygen -t ed25519 -C "developer@company.com"

# Добавление в SSH-agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Добавление в GitLab
cat ~/.ssh/id_ed25519.pub
# Копировать в GitLab → Settings → SSH Keys

Управление секретами

Плохо (так делать нельзя):

yaml

# .gitlab-ci.yml
script:
  - export DB_PASSWORD="super_secret_123"  # ❌ НИКОГДА

Правильно:

yaml

# GitLab → Settings → CI/CD → Variables
# Добавить: DB_PASSWORD (masked, protected)

# .gitlab-ci.yml
script:
  - echo "Connecting to DB..."
  - java -jar app.jar --db.password=$DB_PASSWORD

Для production секретов: HashiCorp Vault (self-hosted) или GitLab-integrated secrets.


GitLab vs GitHub: что выбрать

Сравнительная таблица

КритерийGitLab (on-prem)GitHub Enterprise
АвтономностьПолнаяЧастичная
Контроль данных100%Зависит от контракта
Встроенный CI/CDИз коробкиActions (cloud-first)
RegistryВстроенныйОтдельно GHCR
Стоимость (500 юзеров)2–3 млн/год4–6 млн/год
КастомизацияПолнаяОграничена
Блокировка рисковОтсутствуютВозможны санкции

Рекомендация

Для корпоративного ПО:
GitLab CE/EE (self-hosted) — стратегически устойчивее.

Для open-source проектов:
GitHub — больше комьюнити, лучше для публичных репозиториев.


Путь от нуля до инженерной платформы (6 месяцев)

Месяц 1: Фундамент

Инфраструктура:

  • Поднять GitLab on-prem
  • Настроить VPN-доступ
  • Развернуть CI-сервер (GitLab Runner)
  • Настроить Docker Registry

Команда:

  • Обучить разработчиков Git Flow
  • Провести workshop по CI/CD
  • Назначить ответственных за инфраструктуру

Месяц 2–3: Первые пайплайны

Технические задачи:

  • Написать базовый pipeline для 2–3 проектов
  • Настроить автоматические тесты
  • Внедрить SAST (SonarQube)
  • Настроить staging-среду

Процессы:

  • Зафиксировать правила работы с main
  • Внедрить code review как обязательный этап
  • Создать шаблоны MR

Месяц 4–5: Зрелость

Автоматизация:

  • Автодеплой в staging
  • Ручной деплой в production
  • Rollback процедуры
  • Мониторинг пайплайнов (Grafana)

Безопасность:

  • Настроить Vault для секретов
  • Ротация ключей
  • Audit logs

Месяц 6: Масштабирование

Команда:

  • Документировать все процессы
  • Создать runbook для инцидентов
  • Внедрить метрики (DORA, lead time)
  • Подключить оставшиеся проекты

Результат: Полностью контролируемая инженерная платформа.


Метрики: как измерить зрелость CI/CD

DORA метрики (DevOps Research and Assessment)

МетрикаНачальный уровеньЗрелый уровень
Deployment Frequency1 раз в месяцНесколько раз в день
Lead Time for Changes1–6 месяцев< 1 дня
Change Failure Rate46–60%0–15%
Time to Restore1 неделя–1 месяц< 1 часа

Дополнительные метрики

Для команды:

  • Время выполнения pipeline (цель: < 15 мин)
  • % успешных пайплайнов (цель: > 90%)
  • Покрытие тестами (цель: > 70%)

Для бизнеса:

  • Time-to-market для фич (цель: < 2 недель)
  • Количество инцидентов после релиза (цель: < 5%)

Антипаттерны CI/CD

Антипаттерн 1: CI на dev-машинах

Проблема:
«У меня работает» — потому что окружение уникально.

Решение:
Чистое окружение для каждого запуска (Docker/Kubernetes).

Антипаттерн 2: Секреты в коде

Проблема:
Пароли и ключи в репозитории = утечка данных.

Решение:
Vault, GitLab CI/CD Variables, AWS Secrets Manager (self-hosted).

Антипаттерн 3: Пайплайны на 2 часа

Проблема:
Разработчики перестают ждать результатов CI.

Решение:
Параллелизация, кэширование, оптимизация тестов.

Антипаттерн 4: Ручной деплой с ноутбука

Проблема:
«Только я знаю, как задеплоить» = bus factor = 1.

Решение:
Деплой только через CI/CD, кнопка для production.


Экономика: почему платформа окупается

Расчёт на 5 лет (команда 20 разработчиков)

Без CI/CD (ручные процессы):

СтатьяГод 1Год 2–5Итого
Ручное тестирование3 млн3 млн/год15 млн
Инциденты на production2 млн2 млн/год10 млн
Простои при деплое1 млн1 млн/год5 млн
Всего6 млн6 млн/год30 млн

С CI/CD платформой:

СтатьяГод 1Год 2–5Итого
Внедрение платформы5 млн5 млн
Поддержка1 млн1 млн/год5 млн
Инциденты (снижение на 80%)400 тыс.400 тыс./год2 млн
Всего6.4 млн1.4 млн/год12 млн

Экономия: 18 млн руб. (60%) за 5 лет.


Главные принципы

  1. CI/CD — это не инструмент, а дисциплина: Без правил работы платформа бесполезна.
  2. Автономность критична: On-premise инфраструктура = отсутствие vendor lock-in.
  3. Pipeline — это бизнес-процесс: Формализуйте через код, а не документы.
  4. Безопасность с первого дня: VPN, SSH-ключи, Vault для секретов.
  5. Начинайте с малого: 1 проект → pipeline → масштабирование на всю команду.

Главная мысль:
Компании, которые владеют инженерной платформой, владеют своим будущим. CI/CD — первый шаг к технологическому суверенитету.