Привет, Хабр! Когда релизы выходят раз в две недели, это кажется нормой. Пока не замечаешь, что задачи копятся, а срочные правки превращаются в борьбу с бюрократией. Разработчики привыкают к долгому циклу ожидания, и низкий TTM становится фоном, на который уже никто не обращает внимания.

Мы решили, что так больше продолжаться не может. Вопрос был не просто в скорости — нужно было изменить сам подход. Автоматизировать, упростить, убрать ненужные этапы и сделать так, чтобы код попадал в продакшен быстро, прогнозируемо и без хаоса.

Меня зовут Павел Лиморенко, и сегодня я расскажу вам, как мы к этому пришли, какие проблемы пришлось решать и как нам удалось ускорить релизный процесс API Почты Mail.

Почему мы взялись за ускорение релизов

В какой-то момент стало очевидно, что наши релизные процессы работают медленно. Исторически инфраструктура была развёрнута на bare metal, а каждое развёртывание требовало ручного управления, что неизбежно приводило к задержкам. Код, который разработчики готовы выкатывать, неделями ждал своего часа, пока проходил все этапы согласования.

Релиз включал множество шагов: сборку артефактов, тестирование, тикеты для Ops, подготовку стендов. А если что-то шло не так, то приходилось ждать отката или вручную исправлять ошибки. Итог? Новые фичи попадали в эксплуатацию с огромной задержкой, а срочные исправления превращались в многоступенчатый процесс.

Всё это сказывалось на TTM. Средняя скорость доставки изменений оставалась низкой, разработчики страдали от долгого цикла ожидания, а релизы выходили лишь раз в две недели. Это стало фундаментальной проблемой, которую нужно было решать.

Пора было менять подход: сделать релизы быстрыми, минимизировать бюрократию, автоматизировать процесс и исключить зависимость от ручных этапов.

Как правильно организовать выкатки? Какие есть подходы и что выбрали мы

Вариантов организации релизного процесса предостаточно. Можно идти по схеме Release Train, добавляя изменения, строго по расписанию отрезая накопленные обновления в релиз. Можно работать в формате больших пакетов, накапливая изменения и выпуская их блоками. Но мы пошли другим путём.

Вместо редких и массивных обновлений мы выбрали стратегию частых и небольших итераций. Такой подход позволяет поддерживать мастер-ветку в рабочем состоянии, избегать сложных мерж-конфликтов и контролировать релизы.

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

Этот подход стал основой всей нашей новой схемы выкатки релизов. Теперь пришло время внедрять его на практике.

Как мы готовились к новой схеме

Чтобы перейти на частые и прогнозируемые релизы, нам понадобилось подготовить инфраструктуру, пересмотреть подходы к тестированию и учесть множество технических нюансов.

Первым шагом стало объединение всех проектов API, тестов и документации в монорепозиторий. Это позволило хранить весь код в одном месте, упростить работу с изменениями и минимизировать сложные зависимости между сервисами. Однако при этом возникла новая задача: как избежать ненужных выкатываний всего репозитория при изменениях?

Мы внедрили систему тегирования, которая фиксирует состояние кода и перед запуском конвеера проверяет, действительно ли произошли изменения. При каждом обновлении используется механизм вычисления SHA от кода (вместо commit SHA), который сверяет актуальность данных и предотвращает избыточные развёртывания. Благодаря этому конвейер разворачивает только те компоненты, которые действительно требуют выкладки, а не весь код целиком, что сокращает время релиза и повышает его предсказуемость.

Следующим шагом стал перенос сервисов в Kubernetes. Это позволило уйти от работы с bare metal и добиться гибкости в CI/CD. До этого каждое развёртывание требовало ручных действий, занимало дополнительное время и создавало риски. Теперь окружения развёртываются автоматически, а стабильность процессов значительно улучшилась.

Мы также изменили подход к тестированию. Теперь тестирование проводится полными регрессами per task: каждая новая задача полностью проверяется перед выкаткой, без дополнительных финальных этапов. Это даёт уверенность, что изменения каждой задачи стабильны и готовы к релизу.

Кроме того, мы отказались от финального тестирования готового релиза. Релиз-инженер больше не нужен для разруливания релиза, код проверяется на каждом шаге, а выкатка происходит автоматически. С учётом небольшого размера итераций считаем, что вероятность конфликта мала, плюс этот риск митигируется плавной раскаткой с помощью Argo Rollouts.

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

Как устроен конвейер

Автоматизация релизов невозможна без чётко настроенного конвейера. Мы переработали его структуру, внедрив механизмы быстрой и предсказуемой выкатки.

Разработчик пишет код, запускает тесты и создаёт merge request. После мержа этого MR в мастер сразу собирается Docker-контейнер, заливается в registry. Далее создаётся merge request в репозиторий IaC, где прописываются теги собранных образов, а развёртывание на staging-стенд запускается триггером. После успешного прохождения всех проверок MR в репозитории IaC вливается в мастер, и далее там запускается конвейер, который выполняет финальное развёртывание.

Этапы работы конвейера:

  1. Он вычисляет SHA от каждого сервиса в монорепозитории. 

  2. Затем сверяет их с SHA в тегах из репозитория с манифестами, чтобы решить, надо ли что-то из них выкатывать. 

  3. Уведомляет в мессенджере о новом вливании ветки. 

  4. Собирает контейнеры, помещает их в реестр, создаёт merge request в репозиторий с манифестами.

  5. Ждёт выполнения там каких-то процедур, например, выкатки на stage.

  6. Затем ждёт, пока кто-то ответственный запустит вливание MR, созданного в четвёртом пункте.

  7. После этого уведомляет в мессенджере о начале выкатывания, о выполняемых задачах и ответственных за них разработчиках.

Мы сделали выкатку триггерной, чтобы исключить человеческий фактор и минимизировать время между вливанием и эксплуатацией. Код автоматически проверяется, а запуск конвейера происходит с учётом проверенного механизма сборки. Такой подход устраняет риск задержек и ошибок, связанных с ручными шагами. Таким образом, конвейер стал быстрым, надёжным и понятным.

Как мы следим за конвейером

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

  • Автоматические уведомления сигнализируют о каждом значимом изменении. Разработчик получает уведомления при вливаниях, запуске конвейеров и развёртывании, независимо от его успешности.

  • Графики и метрики помогают анализировать статус конвейеров в реальном времени, отслеживать время выполнения, долю успешных релизов и выявлять узкие места, требующие оптимизации.

  • Если что-то идёт не так, детальное изучение журналов позволяет оперативно найти причину ошибки и устранить проблему.

Таким образом, релизный процесс стал полностью прозрачным, а его контроль — удобным и предсказуемым.

Особенности организации конвейеров в CI

CI — мощный инструмент, но его использование в запуске в эксплуатацию требует особого внимания к деталям. В ходе автоматизации мы столкнулись с несколькими проблемами и нашли эффективные способы их решения:

  • Сложные задачи с несколькими зависимостями. Когда на одном этапе выполняется несколько действий, есть риск сбоя на каком-то из них и невозможности дальше использовать пайплайн. Мы вынесли ключевые операции в отдельные блоки, чтобы минимизировать точки отказа и дать возможность перезапускать эти отдельные блоки.

  • GitLab API. Вызов API из скриптов в рамках пайплайна даёт широкие возможности по автоматизации, которые недоступны никак по-другому.

  • Auto-cancel redundant pipelines. Полезная фича, которая автоматически отменяет предыдущие запущенные процессы. Но в случае релизного конвейера это может привести к нежелательным отменам релизных пайплайнов. Мы сделали задачи непрерываемыми (interruptable: false) и настроили через API отмену предыдущих процессов в рамках этих же пайплайнов.

  • Идемпотентные задачи. Чтобы избежать лишних повторов выполнения одних и тех же операций, мы внедрили механизм проверки состояния перед запуском. Если данные уже актуальны, то задача просто пропускается.

Таким образом, CI стал не просто инструментом автоматизации, а надёжной системой, которая предсказуемо управляет релизами.

Что получили в итоге

  • 20-кратное ускорение релизов! Вместо одного выпуска раз в две недели теперь мы выкатываем обновления два раза в день.

  • Упрощение процессов. В core-команде больше нет инженера релизов. Ответственность за задачу теперь лежит на разработчике, а релиз происходит автоматически.

  • Более гранулярные релизы. Вместо массивных пакетов обновлений мы выпускаем 5–6 задач за раз, что делает кодовую базу предсказуемой и управляемой.

  • Автоматизированный расчёт DORA-метрик. Теперь мы точно знаем, сколько времени занимает каждая правка, и можем оптимизировать процессы на основе реальных данных.

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

Комментарии (1)


  1. gudvinr
    11.06.2025 04:44

    "Почта Mail" - это все равно что "Почта Почта", звучит крайне странно.

    Почему не просто API Почты или Mail API?