Как мы внедряли CI/CD: GitLab CI и GitHub Actions на проектах TSKLab

Как мы внедряли CI/CD: GitLab CI и GitHub Actions на проектах TSKLab

# Как мы внедряли CI/CD: GitLab CI и GitHub Actions на проектах TSKLab

В проектах TSKLab мы сознательно использовали два разных подхода к CI/CD: GitLab CI — там, где важны единая платформа и жесткий контроль пайплайнов, и GitHub Actions — там, где удобнее жить рядом с кодом и быстро масштабировать автоматизацию. Такой гибридный подход оказался практичнее, чем попытка «стандартизировать всё на один инструмент» любой ценой.

## Зачем вообще понадобился CI/CD в TSKLab

Когда репозиториев стало больше, ручная проверка начала мешать скорости. Типичный сценарий выглядел так:

— кто-то пушит изменения;
— коллега вручную запускает сборку;
— тесты прогоняются не всегда одинаково;
— артефакты хранятся хаотично;
— релиз зависит от памяти конкретного человека.

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

## Почему у нас были и GitLab CI, и GitHub Actions

Если упростить, выбор шел по двум осям: где живет код и какая инфраструктурная модель удобнее для конкретной команды. Мы не пытались натянуть одну систему на все проекты — это почти всегда приводит к компромиссам, которые не устраивают никого.

### GitLab CI мы оставили там, где важно контролировать весь цикл

GitLab CI хорошо подошел для внутренних проектов, сервисов с более строгой дисциплиной и сценариев, где удобно держать рядом репозиторий, пайплайны, артефакты и раннеры. В GitLab отдельно важны различие между **cache** и **artifacts**: кэш используют для повторного использования зависимостей и файлов, которые не меняются часто, а артефакты — чтобы передавать результаты между этапами или сохранять итог работы job. На практике это означает, что node_modules или Gradle-кеш мы помечаем как cache, а собранный Docker-образ или отчет о тестах — как artifacts. Путаница здесь дорого обходится: если положить артефакт в кэш, он может не дожить до следующего запуска, и пайплайн упадет в самый неподходящий момент.

### GitHub Actions мы использовали там, где важна скорость старта

GitHub Actions оказался удобен для открытых или внешне ориентированных репозиториев, где нужно быстро описать workflow рядом с кодом. У GitHub похожая логика: caching нужен для редко меняющихся зависимостей и промежуточных результатов, а artifacts — для сохранения файлов после завершения workflow или передачи их между job’ами. Разница в том, что в Actions кэш привязан к ключу, и если ключ меняется, старый кэш может остаться висеть мертвым грузом, пока не истечет срок хранения. Мы на это натыкались, когда переезжали с одной версии Node.js на другую и забывали обновить ключ — пайплайн внезапно начинал пересобирать всё с нуля.

## Что мы автоматизировали в первую очередь

Ниже — набор задач, с которых стоит начинать почти в любом проекте.

| Задача | Зачем нужна | Что автоматизировали |
|—|—|—|
| Сборка | Проверить, что код вообще собирается | `build` на каждый push |
| Тесты | Ловить регрессии до мержа | unit и smoke tests |
| Линтинг | Убирать мелкие ошибки и стиль | ESLint, Stylelint, Prettier checks |
| Артефакты | Сохранять сборки и отчеты | build outputs, test reports |
| Кэш зависимостей | Ускорять повторные запуски | npm/pnpm, Gradle, Docker layers |
| Релизные шаги | Делать выкладку предсказуемой | tagging, deploy, changelog |

Первым делом мы закрывали именно эти шесть пунктов. Без них любой пайплайн — это просто набор скриптов, который не дает уверенности в результате. Например, линтинг мы внедряли еще до того, как писали сложные тесты, потому что он ловит глупые ошибки вроде неиспользуемых переменных и сломанного форматирования, которые иначе отнимают время на код-ревью.

## Как мы разделили логику пайплайнов

Самая частая ошибка — пытаться сразу собрать «идеальный» pipeline из десятка стадий. На практике лучше идти от простого к сложному.

### Базовая схема CI

1. `lint`
2. `test`
3. `build`
4. `package`
5. `deploy`

Такой порядок удобен, потому что ранние этапы дешевые и быстро отсекают очевидные проблемы. Если линт или тесты падают, сборка и деплой даже не стартуют. Мы намеренно ставили линтер перед тестами: он отрабатывает за секунды и экономит ресурсы раннера, не давая запускать тяжелые юнит-тесты на заведомо битом коде. В нескольких проектах это сократило среднее время обратной связи с 4 минут до 30 секунд.

### Как мы решали вопрос с кэшем

В обоих инструментах кэш нужен не для «красоты», а для экономии времени. Правильная логика простая:

— кэшируй то, что дорого скачивать и можно безопасно переиспользовать;
— не клади в кэш то, что должно быть строго результатом текущего запуска;
— если результат нужен следующему шагу прямо сейчас, используй артефакты, а не кэш.

Это важное различие. GitLab прямо разделяет сценарии: кэш — для ускорения повторных запусков, артефакты — для передачи результатов между этапами. В GitHub Actions та же идея сформулирована аналогично. Мы на практике убедились: если попытаться сэкономить и использовать кэш для передачи бинарника между job’ами, можно получить неконсистентное состояние — кэш может быть восстановлен частично или не из того коммита. Поэтому правило жесткое: всё, что должно быть передано строго от этапа к этапу, идет через artifacts.

## GitLab CI: что у нас сработало лучше всего

GitLab CI оказался сильным в проектах, где нужен более «платформенный» подход.

### Плюсы, которые мы реально почувствовали

— Удобно держать все рядом: код, pipeline, артефакты, раннеры.
— Хорошо ложится на многоэтапную сборку.
— Удобен для внутренних процессов, где важны роли, доступы и контроль.
— Проще выстроить одинаковое поведение для нескольких проектов.

Особенно порадовала возможность наследования конфигураций через `include` и шаблоны. Мы сделали базовый шаблон для всех микросервисов, и добавление нового проекта сводилось к нескольким строчкам. Это резко снизило порог входа для новых членов команды.

### Где пришлось быть аккуратнее

— Без дисциплины `.gitlab-ci.yml` быстро разрастается.
— Неправильно настроенный кэш может давать ложное ощущение скорости.
— Раннеры нужно обслуживать: следить за производительностью, хранилищем и таймаутами. В документации GitLab отдельно отмечается, что для кэша есть настройки компрессии и таймаутов, а также сценарии с распределенным кэшем.

Мы столкнулись с тем, что на одном из проектов кэш занимал 2 ГБ, потому что разработчики не чистили старые ключи. Раннер начал тормозить, и пришлось вводить политику TTL для кэша. Еще один нюанс: если раннер работает на shared-инфраструктуре, важно следить за таймаутами job’ов — долгая сборка без вывода может быть прибита системой, и артефакты не сохранятся.

### Практический вывод по GitLab CI

GitLab CI лучше всего работает, когда:
— проект живет в GitLab;
— нужен контролируемый внутренний процесс;
— пайплайны должны быть частью единой платформы;
— есть готовность поддерживать раннеры и инфраструктуру.

## GitHub Actions: в чем он оказался удобнее

GitHub Actions мы полюбили за скорость внедрения и близость к репозиторию.

### Сильные стороны

— Быстро стартовать даже с небольшим проектом.
— Удобно хранить workflow рядом с кодом.
— Хорошо подходит для типовых автоматизаций: тесты, сборка, релизные проверки.
— Отлично работает в проектах, где команда уже живет в экосистеме GitHub.

Для опенсорсных библиотек Actions вообще стал спасением: contributors видят, что проверки прозрачны, и могут локально воспроизвести то же самое. Маркетплейс экшенов позволяет не изобретать велосипеды для тривиальных задач вроде настройки Node.js или публикации в npm.

### Ограничения, о которых важно помнить

— При росте числа workflow легко получить «зоопарк» YAML-файлов.
— Без единых шаблонов логика дублируется.
— Если не следить за ключами кэша, можно получить неожиданные промахи и лишние пересборки.

GitHub официально рекомендует использовать caching для зависимостей и других файлов, которые дорого воспроизводить, а artifacts — для файлов, созданных job’ой и нужных после завершения workflow или в других job’ах. Мы на одном проекте столкнулись с тем, что разные workflow использовали разные ключи кэша для одних и тех же зависимостей, и в итоге кэш не переиспользовался, а время сборки выросло вдвое. Пришлось унифицировать ключи через переменные окружения.

## Сравнение: GitLab CI и GitHub Actions на практике

| Критерий | GitLab CI | GitHub Actions |
|—|—|—|
| Где удобнее жить | Внутри GitLab-платформы | Рядом с кодом в GitHub |
| Быстрота старта | Средняя | Очень высокая |
| Удобство для внутренних процессов | Высокое | Среднее |
| Простота типовых workflow | Высокая, но требует дисциплины | Очень высокая |
| Работа с кэшем и артефактами | Разделены и хорошо документированы | Аналогично разделены |
| Подходит для больших команд | Да | Да, если есть стандарты |
| Главный риск | Сложность инфраструктуры раннеров | Разрастание YAML и дублирование |

## Как мы настраивали кэш и артефакты

Это один из самых полезных практических блоков.

### Кэш

Кэш мы использовали для:
— зависимостей package manager’а;
— промежуточных файлов, которые можно пересоздать;
— ускорения повторных билдов на одинаковых ветках.

Например, для Node.js-проектов мы кэшировали `~/.npm` или `pnpm-store`, а для Java — директорию `.gradle/caches`. Ключ кэша обычно включал хеш lock-файла, чтобы при изменении зависимостей кэш автоматически инвалидировался. В GitLab CI мы дополнительно настраивали `cache:key` с учетом ветки, чтобы разные ветки не перетирали друг другу кэш.

### Артефакты

Артефакты мы использовали для:
— собранных бинарников;
— отчетов тестирования;
— сборок, которые должны дожить до деплоя;
— файлов, которые нужно передать между job’ами.

Ключевой принцип: если файл нужен как **результат этапа**, это артефакт; если нужен как **ускоритель следующего запуска**, это кэш. Мы всегда явно указывали срок хранения артефактов, чтобы не забивать дисковое пространство. В GitLab для этого есть `expire_in`, в GitHub Actions — `retention-days`.

## Типовые ошибки, которые мы видели

### 1. Кладут в кэш всё подряд

Из-за этого кэш разрастается, а выигрыш по времени становится минимальным. Видели проект, где кэшировали всю директорию `build`, включая временные файлы, и в итоге восстановление кэша занимало больше времени, чем сама сборка.

### 2. Путают артефакты и кэш

В итоге один и тот же файл то пересоздается, то теряется между job’ами. Классика: бинарник кладут в кэш, а на следующем этапе ожидают его как артефакт — и получают ошибку, потому что кэш мог не восстановиться.

### 3. Делают слишком длинный pipeline на старте

Сначала нужно автоматизировать базу: сборка, тесты, линт, затем уже добавлять релизы и окружения. Мы однажды попытались сразу заложить деплой в Kubernetes с канареечным развертыванием, и пайплайн стал настолько сложным, что отладка занимала полдня. Откатились до минимальной версии и добавляли этапы итеративно.

### 4. Не стандартизируют имена job’ов и шаблоны

Через несколько месяцев безоговорочно выигрывает не лучший инструмент, а более аккуратный pipeline. Когда в команде каждый называет job’ы как хочет, найти нужный лог или понять, что упало, становится квестом.

### 5. Не измеряют время выполнения

Без цифр невозможно понять, что реально ускоряет пайплайн, а что просто усложняет YAML. Мы ввели простой мониторинг: каждый job пишет свою длительность в логи, и раз в неделю смотрим на тренды. Это помогло выявить, что добавление одного лишнего шага с линтингом CSS увеличило общее время на 40 секунд без ощутимой пользы.

## Чек-лист внедрения CI/CD в проекте

— Определите, что считается успехом: сборка, тесты, релиз, деплой.
— Начните с минимального pipeline.
— Разделите cache и artifacts по смыслу.
— Настройте защиту от падения на ранних этапах.
— Измеряйте время выполнения каждого job.
— Храните workflow/YAML в одном стиле.
— Описывайте, какие ветки и события запускают pipeline.
— Периодически чистите устаревшие кэши и ненужные артефакты.
— Проверяйте, что pipeline воспроизводим на чистом окружении.

## Когда выбирать GitLab CI, а когда GitHub Actions

### GitLab CI подходит, если:
— репозитории уже в GitLab;
— нужен единый контроль над CI/CD;
— важны раннеры и внутренние процессы;
— проект живет в более «корпоративной» модели.

### GitHub Actions подходит, если:
— код в GitHub;
— нужен быстрый старт;
— workflow должен быть максимально близко к репозиторию;
— автоматизации пока немного, но она будет расти.

## Итог: что мы поняли на своих проектах

Главный вывод оказался простым: хорошая CI/CD-система — это не самая навороченная конфигурация, а предсказуемый и понятный процесс. GitLab CI дал нам больше контроля и удобства в инфраструктурно зрелых проектах, а GitHub Actions — скорость и гибкость там, где важен быстрый запуск и тесная связь с репозиторием.

Если свести опыт TSKLab к одной фразе, она будет такой: **инструмент выбирают не по моде, а по тому, как он решает конкретную задачу команды**.

## FAQ

**Чем artifacts отличаются от cache?**
Artifacts нужны для сохранения результата job и передачи его дальше, а cache — для ускорения повторных запусков за счет переиспользования зависимостей и промежуточных файлов.

**Можно ли обойтись только GitHub Actions?**
Да, если весь код и процесс живут в GitHub и не нужен более жесткий контроль инфраструктуры. Для многих проектов этого достаточно.

**Можно ли обойтись только GitLab CI?**
Да, если репозитории и процессы сосредоточены в GitLab и важна единая платформа с управляемыми раннерами.

**Что внедрять первым делом?**
Сначала линтинг, тесты и сборку. Потом артефакты, кэш и только затем деплой.

**Почему пайплайн начинает тормозить?**
Чаще всего из-за неправильного кэша, лишних job’ов, тяжелых образов или отсутствия разделения по этапам.

## Вывод

CI/CD начинает приносить пользу только тогда, когда он экономит время команды и снижает вероятность ошибки. В TSKLab мы пришли к гибридной модели не потому, что это «модно», а потому что разные проекты требуют разной степени контроля, скорости и инфраструктурной зрелости. GitLab CI и GitHub Actions — не конкуренты в вакууме, а инструменты для разных рабочих сценариев, и самый практичный подход — выбирать их под задачу, а не под привычку.