Ми в True Engineering налаштували безперервну доставку оновлень на сервери замовника і хочемо поділитися цим досвідом.
Для початку ми розробили онлайн систему для замовника та розгорнули її у власному кластері Kubernetes. Тепер наше навантажене рішення переїхало на платформу замовника, для чого ми налаштували повністю автоматичний процес Continuous Deployment. Завдяки цьому ми прискорили time-to-market – доставку змін до продуктового середовища.
У цій статті ми розповімо про всі етапи процесу Continuous Deployment (CD) або доставку оновлень на платформу замовника:
- як стартує цей процес,
- синхронізація з Git-репозиторієм замовника,
- збірка бекенда та фронтенду,
- автоматичне розгортання програми у тестовому середовищі,
- автоматичне розгортання на Prod.
У процесі поділімося деталями налаштування.
1. Старт CD
Continuous Deployment починається з того, що розробник викладає зміни до релізної гілки нашого Git-репозиторію.
Наш додаток працює на базі мікросервісної архітектури та всі його компоненти зберігаються в одному репозиторії. Завдяки цьому збираються та встановлюються всі мікросервіси, навіть якщо змінився один із них.
Ми організували роботу через один репозиторій з кількох причин:
- Зручність розробки - програма активно розвивається, тому можна працювати відразу з усім кодом.
- Єдиний пайплайн CI/CD, який гарантує, що програма як єдина система проходить усі тести та доставляється в prod-оточення замовника.
- Виключаємо плутанину у версіях – нам не доводиться зберігати карту версій мікросервісів та описувати для кожного мікросервісу свою конфігурацію у скриптах Helm.
2. Синхронізація з Git-репозиторієм вихідного коду замовника
Зроблені зміни автоматично синхронізуються із Git-репозиторієм замовника. Там налаштовано складання програми, яка запускається після оновлення гілки, та деплоймент у прод. Обидва процеси відбуваються в їхньому оточенні з Git-репозиторію.
Ми не можемо працювати з репозиторієм замовника безпосередньо, оскільки нам потрібні власні середовища для розробки та тестування. Ми використовуємо для цього свій Git-репозиторій — він синхронізований з їх Git-репозиторієм. Як тільки розробник викладає зміни у відповідну гілку нашого репозиторію, GitLab відразу ж надсилає ці зміни замовнику.
Після цього потрібно зробити складання. Вона складається з декількох етапів: складання бекенду та фронтенду, тестування та доставки в прод.
3. Складання бекенду та фронтенду
Складання бекенду та фронтенду – це два паралельні завдання, які виконуються в системі GitLab Runner. Її конфігурація вихідного складання лежить у тому ж репозиторії.
GitLab Runner забирає код із потрібного репозиторію, командою зборки Java-додатки зібрає та відправляє його в Docker registry. Тут ми збираємо бекенд та фронтенд, отримуємо Docker-образи, які складаємо у репозиторій на стороні замовника. Для керування Doker-образами використовуємо
Ми синхронізуємо версії наших образів із версією релізу, який буде викладено у Docker. Для гладкої роботи ми внесли кілька налаштувань:
1. Між тестовим оточенням та продуктовим контейнерами не перезбираються. Ми зробили параметризації, щоб один і той же контейнер міг без перескладання працювати з усіма налаштуваннями, змінними оточеннями та сервісами як у тестовому середовищі, так і на проді.
2. Для оновлення програми через Helm необхідно вказати її версію. У нас збірка бекенда, фронтенда та оновлення програми – це три різні завдання, тому важливо використовувати скрізь одну й ту саму версію програми. Для цього завдання ми використовуємо дані з історії Git, оскільки у нас конфігурація K8S кластера та програми знаходяться в одному Git-репозиторії.
Версію програми ми отримуємо з результатів виконання команди
git describe --tags --abbrev=7
.
4. Автоматичне розгортання всіх змін у тестовому середовищі (UAT)
Наступним етапом у цьому скрипті збирання виконується автоматичне оновлення кластера K8S. Це відбувається за умови, що вся програма зібралася і всі артефакти опубліковані в Docker Registry. Після цього запускається оновлення тестового оточення.
Оновлення кластера запускається за допомогою
Ми постачаємо разом зі збиранням конфігурацію кластера 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:
Разом
Отже, Continuous Deployment налаштований та працює. Усі оновлення відбуваються за одним натисканням клавіші. Доставка змін у продуктове середовище – автоматична. І, що важливо, поновлення не зупиняють роботи системи.
Плани на майбутнє: автоматична міграція бази
Ми задумалися про апгрейд бази та можливість ці зміни відкотити. Адже одночасно працюють дві різні версії програми: стара працює, а нова піднімається. І стару ми виключимо лише коли переконаємося, що нова версія працює. Міграція бази повинна дозволяти працювати з обома версіями програми.
Тому ми не можемо просто так змінити назву колонки чи інші дані. Але ми можемо створити нову колонку, скопіювати в неї дані зі старої колонки і написати тригери, які при оновленні даних одночасно копіюватимуть і оновлюватимуть їх в іншій колонці. І після успішного деплою нової версії програми, після періоду post launch support, ми зможемо видалити стару колонку і тригер, що став непотрібним.
Якщо нова версія програми працює некоректно, ми можемо відкотитися до попередньої версії, у тому числі й до попередньої версії бази. Словом, наші зміни дозволять працювати одночасно з кількома версіями програми.
Ми плануємо зробити автоматизацію міграції бази через K8S job, вбудувавши їх у процес CD. І обов'язково поділимося цим досвідом на Хабрі.
Джерело: habr.com