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

Возможны ситуации, когда в состав разрабатываемого продукта могут попадать нежелательные изменения, которые содержатся во внешних зависимостях. Это особенно актуально во время сертификации продукта. Как следствие — затягивание сертификаций, сбои ночных тестов и интеграционного тестирования, поломки on-premise production (производственной среды, расположенной на собственных ресурсах организации) при накатывании хотфикса и прочее. В новой статье мы описали подход, который позволит избежать таких проблем.

Чего мы хотели добиться


Прежде чем приступать к описанию подхода, пара слов о задачах, которые мы хотели решить:

  • Получить полный контроль над составом внешних пакетов в релизе (предсказуемость).
  • Зафиксировать составы внешних репозиториев для быстрой выкатки хотфиксов с минимальным дополнительным тестированием (скорость).
  • Обеспечить продуктовые стенды QA повторяемым предсказуемым фиксированным окружением (повторяемость).
  • Независимость от наличия внешнего канала связи (автономность).
  • Моментальное переключение на официальные репозитории при аварии (отказоустойчивость).
  • Гарантированная проверка ключей внешних репозиториев в сборочных конвейерах (доверие).
  • И самое главное, передать управление и контроль над составом внешних пакетов в руки продуктовых команд и релиз-менеджеров (самоуправление).

Анализ жизненного цикла feature-сборок


Наш подход решает задачу фиксации состава внешних репозиториев на конкретную дату, под релиз или фичу. Следующая схема наглядно показывает управление жизненным циклом релиза, feature-сборки и хотфикса.

Для примера возьмем условный репозиторий Debian Stretch. Данный подход применим и для репозиториев Docker, SaltStack и т. п. На временной шкале зафиксировали три среза на даты T1, T2 и T3.


T1 T2 T3
stretch 20200305 20200420 20200615
Feature1 20200304 20200304 20200501
Feature2 20200304 20200304 20200601
Feature3 20200301 20200406 20200406

Мы свели в таблицу состав внешнего репозитория Debian Stretch для сборки дистрибутивов Feature1, Feature2 и Feature3. Из таблицы видно, что состав внешнего репозитория контролируется каждой веткой независимо. Мы приняли соглашение для себя фиксировать ветку master для Debian Stretch ежедневно и давать метки каждому срезу в формате YYYYMMDD, например 2020304 для среза на 4 марта 2020 года. Итого в таблице приведены используемые для дистрибутива в каждой ветке срезы внешнего репозитория в трех разных моментах времени и состав в мастере для Debian Stretch. Команда для каждой фичи или для каждого релиза обновляет состав внешних репозиториев по своему усмотрению и согласно своему циклу разработки.

На примере Feature1: продуктовая команда приступает к разработке новой фичи и фиксирует в конфигурационных файлах состав внешнего репозитория на дату 20200228 (см. на схеме выше).

Переключаем на 20200228
deb http://repository.co/debian-stretch-20200228 stretch main contrib non-free

В процессе разработки в связи с появлением новых пакетов возникает необходимость обновить пакетную базу до даты 20200304. Переключаем рабочий репозиторий на нужную дату.

Переключаем на 20200304
deb http://repository.co/debian-stretch-20200304 stretch main contrib non-free

Далее происходит еще одно переключение пакетной базы на дату 20200501.

Переключаем на 20200501
deb http://repository.co/debian-stretch-20200501 stretch main contrib non-free

Если теперь мы проведем временные срезы, то увидим, что в моменты времени T1 и Т2 разработка Feature1 идет на пакетной базе, «замороженной» 4 марта 2020 года. А в срезе T3 разработка идет уже на новой пакетной базе за 1 мая 2020 года.

Продуктовое мультирелизное управление зависимостями


Теперь рассмотрим управление зависимостями нескольких активных релизов продукта. На поддержке представлены три релиза 2.5, 2.6 и 2.7.

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


Релиз Состав
2.5.128, 2.5.135, 2.5.207 20200301
2.6.201, 2.6.215, 2.6.315 20200301
2.7.210, 2.7.217, 2.7.305 20200404

Вместо именования срезов по датам YYYYMMDD мы также используем именование тегами в формате <ProjectName.ReleaseVersion> (название_релиза.версия_релиза продукта, например name.2.2) или <ProjectName-FeatureNumber> (добавляем номер фичи, например 3).

Снапшот для 2.5 по состоянию на 20200301
deb http://repository.co/debian-stretch-projectname-2.5 stretch main contrib non-free # 20200301

Таким образом в процессе разработки релиза 2.5 команда фиксирует состав зависимых репозиториев на дату 20200301. Где-то в апреле команда начинает новый релиз 2.6 и решает использовать состав пакетов внешнего репозитория от 2.5. Создаем новый снапшот для 2.6 из снапшота для 2.5. В будущем составы репозиториев для релизов 2.5 и 2.6 могут легко разойтись. Мы сделали для 2.6 свой тег debian-stretch-projectname-2.6.

Снапшот для 2.6 по состоянию на 20200301
deb http://repository.co/debian-stretch-projectname-2.6 stretch main contrib non-free # 20200301

В случае релиза 2.7 команда может начать разработку с ветки master — ежедневного снапшота оригинального репозитория.

Снапшот для 2.7 по состоянию на 20200404
deb http://repository.co/debian-stretch-projectname-2.7 stretch main contrib non-free # 20200404

Мультипродуктовое управление зависимостями


Рассмотрим мультипродуктовое управление зависимостями на примере двух продуктов с разными релизными циклами и своими продуктовыми командами: Stealth и Infiniti.



Прокомментируем таблицу, что и когда происходит.
Продукт Релиз Состав
stealth2.2 r2.2.124 20200301
stealth2.2 r2.2.131, r2.2.162 20200305
infiniti4.0 r4.0.235, r4.0.241 20200303
infiniti4.0 r4.0.250 20200308

1. Пусть с 1 марта 2020 года стартовала разработка версии 2.2 проекта Stealth, для этого был создан снапшот состава пакетной базы на текущую дату. Выпуск релиза 2.2.124 выполнен с пакетной базой внешнего репозитория от 20200301.

Stealth 2.2
deb http://repository.co/debian-stretch-stealth-2.2 stretch main contrib non-free # 20200301

2. Пятого числа производится обновление пакетной базы. Рабочий репозиторий debian-stretch-stealth-2.2 одномоментно переключается на нужную дату, выпуск релизов 2.2.131 и 2.2.162 выполнен с составом пакетов внешнего репозитория от 20200305. Без дополнительных манипуляций в окружении все 100500 микросервисов продукта одномоментно получили в сборочном конвейере новое окружение 20200305.

Stealth 2.2
deb http://repository.co/debian-stretch-stealth-2.2 stretch main contrib non-free # 20200305

3. Параллельно третьего числа стартует разработка проекта Infiniti версии 4.0 и для нее создается срез состава репозитория на дату 20200303. Версии 4.0.235 и 4.0.241 выпускаются с составом пакетов внешнего репозитория на 20200303.

Infiniti 4.0
deb http://repository.co/debian-stretch-infiniti-4.0 stretch main contrib non-free # 20200303

4. После выпуска версии 4.0.241 команда решает обновить состав репозитория до 20200308 и выпустить новый релиз с новым составом внешних пакетов. Версия 4.0.250 выходит с составом пакетов на 20200308.

Infiniti 4.0
deb http://repository.co/debian-stretch-infiniti-4.0 stretch main contrib non-free # 20200308

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

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

Скачать список всех репозиториев

# For example
curl repository.co/info/sources.list | grep $(lsb_release -cs) > /etc/apt/sources.list

Автоматическое создание срезов внешних репозиториев


Обновление репозиториев происходит каждую ночь по планировщику GitLab. При добавлении нового репозитория изменения автоматически применяются на сервере.



В момент фиксации нового среза внешнего репозитория проверяется его сертификат, если он отличается от сохраненного у нас, то обновления не происходит, а нам поступает сообщение об ошибке.

Итоги


  1. Подготовка новой версии дистрибутива к сертификации больше не является головной болью. На период сертификации мы фиксируем состав дистрибутива, и если нужно что-то пофиксить оперативно, то с большой вероятностью в выпущенном хотфиксе не будет ошибок из-за изменения окружения.
  2. Все feature-сборки получают управляемое состояние внешних репозиториев.
  3. Ускоряются выкатка хотфиксов и проверка через QA с предсказуемым, быстрым и успешным результатом.
  4. Feature-стенды получают после развертывания заранее определенную, неизменяемую среду выполнения.
  5. Продуктовые команды получили полностью самостоятельный контроль над составом внешних репозиториев для любых задач.

Отметим, что у Debian есть официальный ресурс snapshot.debian.org с ежедневными срезами и большой глубиной хранения. Для определенных задач этого достаточно.

Благодарим Сергея Смирнова и сообщество за прекрасный инструмент для управления составом внешних репозиториев Aptly. От нас — небольшой вклад в лучшие практики использования этого полезного инструмента в производственных конвейерах.

В следующих статьях расскажем про связку Aptly + Simple-CDD для подготовки ISO-образов дистрибутивов, делегирование управления внешними зависимостями в продуктовые команды и проблемы применения Aptly в процессе управления внешними зависимостями.

Авторы: Никита Драчёв, Александр Паздников, Тимур Гильмуллин