11 апреля 2019
Хардкор

Как мы реализовали непрерывную доставку обновлений на платформу заказчика

Мы в True Engineering настроили процесс непрерывной доставки обновлений на сервера заказчика и хотим поделиться этим опытом. Для начала мы разработали онлайн систему для заказчика и развернули её в собственном кластере Kubernetes. Теперь наше высоконагруженное решение переехало на платформу заказчика, для чего мы настроили полностью автоматический процесс Continuous Deployment. Благодаря этому, мы ускорили time-to-market – доставку изменений в продуктовую среду. В этой статье мы расскажем обо всех этапах процесса Continuous Deployment (CD) или доставки обновлений на платформу заказчика:

  1. как стартует этот процесс,
  2. синхронизация с Git-репозиторием заказчика,
  3. сборка бекенда и фронтенда,
  4. автоматическое развертывание приложения в тестовой среде,
  5. автоматическое развертывание на Prod.

В процессе поделимся деталями настройки.

1. Старт CD

Continuous Deployment начинается с того, что разработчик выкладывает изменения в релизную ветку нашего Git-репозитория. Наше приложение работает на базе микросервисной архитектуры и все его компоненты хранятся в одном репозитории. Благодаря этому собираются и устанавливаются все микросервисы, даже если изменился один из них. Мы организовали работу через один репозиторий по нескольким причинам:

  • Удобство разработки — приложение активно развивается, поэтому можно работать сразу со всем кодом.
  • Единый пайплайн CI/CD, который гарантирует, что приложение как единая система проходит все тесты и доставляется в prod-окружение заказчика.
  • Исключаем путаницу в версиях — нам не приходится хранить карту версий микросервисов и описывать для каждого микросервиса свою конфигурацию в скриптах Helm.

2. Синхронизация с Git-репозиторием исходного кода заказчика

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

После этого нужно сделать сборку. Она состоит из нескольких этапов: сборки бекенда и фронтенда, тестирования и доставки в прод.

3. Сборка бекенда и фронтенда

Сборка бекенда и фронтенда — это две параллельные задачи, которые выполняются в системе GitLab Runner. Её конфигурация исходной сборки лежит в этом же репозитории. Туториал для написания YAML-скрипта для сборки в GitLab. GitLab Runner забирает код из нужного репозитория, командой сборки Java-приложения собрает и отправляет его в Docker registry. Здесь мы собираем бекенд и фронтенд, получаем Docker-образы, которые складываем в репозиторий на стороне заказчика. Для управления Doker-образами используем плагин Gradle. Мы синхронизируем версии наших образов с версией релиза, который будет выложен в Docker. Для гладкой работы мы внесли несколько настроек: 1. Между тестовым окружением и продуктовым контейнеры не пересобираются. Мы сделали параметризации, чтобы один и тот же контейнер мог без пересборки работать со всеми настройками, переменными окружениями и сервисами как в тестовой среде, так и на проде. 2. Для обновления приложения через Helm необходимо указать его версию. У нас сборка бекенда, фронтенда и обновление приложения – это три разные задачи, поэтому важно использовать везде одну и ту же версию приложения. Для этой задачи мы используем данные из истории Git, поскольку у нас конфигурация K8S кластера и приложения находятся в одном Git-репозитории. Версию приложения мы получаем из результатов выполнения команды git describe --tags --abbrev=7.

4. Автоматическое развертывание всех изменений в тестовой среде (UAT)

Следующим этапом в этом скрипте сборки выполняется автоматическое обновление кластера K8S. Это происходит при условии, что все приложение собралось и все артефакты опубликованы в Docker Registry. После этого запускается обновление тестового окружения. Обновление кластера запускается с помощью Helm Update. Если в результате что-то пошло не по плану, то Helm автоматически и самостоятельно откатит все свои изменения. Его работу не нужно контролировать. Мы поставляем вместе со сборкой конфигурацию кластера K8S. Поэтому следующим шагом обновляется она: configMaps, deployments, services, secrets и любые другие конфигурации K8S, которые мы изменили. После этого Helm запускает RollOut обновление самого приложения в тестовой среде. До того как приложение будет развернуто на проде. Это сделано для того, чтобы пользователи вручную проверили бизнес-фичи, которые мы выложили в тестовое окружение.

5. Автоматическое развертывание всех изменений на Prod

Чтобы развернуть обновление в продуктовое окружение, остаётся лишь нажать одну кнопку в GitLab — и контейнеры сразу доставляются в продуктовую среду. Одно и то же приложение может без пересборки работать в разных окружениях — тестовом и проде. Мы используем одни и те же артефакты, не меняя ничего в приложении, а параметры задаём извне. Гибкая параметризация настроек приложения зависит от того окружения, в котором это приложение будет выполняться. Мы вынесли все настройки окружений вовне: всё параметризуется через конфигурацию K8S и параметры Helm. Когда Helm разворачивает сборку в тестовое окружение, к ней применяются тестовые параметры, а продуктовом окружении — продуктовые параметры. Самым сложным было параметризовать все используемые сервисы и переменные, которые зависят от окружения, и перевести их в переменные окружения и описание-конфигурации параметров окружения для Helm. В параметрах приложения используются переменные окружения. Их значения задаются в контейнерах при помощи K8S configmap, который шаблонизируется при помощи Go-шаблонов. К примеру, задание переменной окружения на звание домена можно сделать так:


 

APP_EXTERNAL_DOMAIN: {{ (pluck .Values.global.env .Values.app.properties.app_external_domain | first) }}

.Values.global.env – в этой переменной хранится название окружения (prod, stage, UAT). .Values.app.properties.app_external_domain – в этой переменной мы в файле .Values.yaml задаем нужный домен При обновлении приложения Helm выполняет создание из шаблонов файла configmap.yaml и заполняет значение APP_EXTERNAL_DOMAIN нужным значением в зависимости от окружения, в котором стартует обновление приложения. Эта переменная проставляется уже в контейнере. Доступ к ней есть из приложения, соответственно, в каждом окружении приложения будет разное значение этой переменной. Относительно недавное в Spring Cloud появилась поддержка K8S, в том числе работа с configMaps: Spring Cloud Kubernetes. Пока проект активно развивается и изменяется кардинально, мы не можем использовать его в проде. Но активно мониторим его состояние и используем его в DEV конфигурациях. Как только он стабилизируется - будем переключаться с использования переменных окружения на него.

 

Итого

Итак, Continuous Deployment настроен и работает. Все обновления происходят по одному нажатию клавиши. Доставка изменений в продуктовую среду — автоматическая. И, что важно, обновления не останавливают работы системы.

Планы на будущее: автоматическая миграция базы

Мы задумались о об апгрейде базы и возможности эти изменения откатить. Ведь одновременно работают две разные версии приложения: старая работает, а новая поднимается. И старую мы выключим только когда убедимся, что новая версия — работает. Миграция базы должна позволять работать с обеими версиями приложения. Поэтому мы не можем просто так изменить название колонки или другие данные. Но мы можем создать новую колонку, скопировать в нее данные из старой колонки и написать триггеры, которые будут при обновлении данных будут одновременно копировать и обновлять их в другой колонке. И после успешного деплоя новой версии приложения, по прошествии периода post launch support, мы сможем удалить старую колонку и ставший ненужным триггер. Если новая версия приложения работает некорректно, мы можем откатиться до прежней версии, в том числе и прежней версии базы. Словом, наши изменения позволят работать одновременно с несколькими версиями приложения. Мы планируем сделать автоматизацию миграции базы через K8S job, встроив её в процесс CD. И обязательно поделимся этим опытом на Хабре. ых докладах.

 

Оригинал опубликован на Habr.ru