Чи варто деплоїтись у п’ятницю
Привіт! Мене звати Сергій, я — Tech Lead у Solidgate. Наші розробники щодня роблять
Питання безпечного та ефективного CI/CD процесу — ключове для багатьох компаній. На співбесідах я часто запитую: «Чи деплоїтеся ви в прод у п’ятницю?». Близько 90% кандидатів відповідають: «Ні», і лише 9% кажуть: «Так, але мене змусили» або «Так, але це ненормально».
Чому така реакція? Найчастіше чую: «А раптом все ляже у вихідні?» або «Не хочу виправляти помилки, коли всі відпочивають».
Це страх, який з’являється через невпевненість у своєму продукті або процесах. Чому не п’ятниця? Бо треба мати час:
- підготувати реліз;
- добряче його протестувати;
- розгорнути його на тестовому оточенні;
- перевірити, що все працює як слід;
- задеплоїти в прод;
- знову перевірити, що все працює як слід;
- якщо щось станеться — швидко розібратися та викатити hotfix;
- якщо потрібно, повторити всю процедуру ще раз.
На всі ці ритуали може вистачити й двох днів, тож у четвер до обіду деплоїтися можна. Але що, якщо все впаде вночі з четверга на п’ятницю?...
Solidgate щороку зростає у
До речі, прочитати про це більше ви можете у статті Head of Engineering Solidgate Макса Багінського, в якій він розповідає про втрату $10,000 за хвилину даунтайму нашої системи.
То як при збільшенні та ускладненні системи не сповільнювати розробку? Може здатися, що найкраще рішення — найняти більше людей. Але збільшення команди без зміни процесів може призвести до:
- ускладнення комунікацій;
- зменшення швидкості прийняття рішень;
- розмиття зон відповідальності;
- збільшення кількості одночасних МR-ів від різних людей;
- merge conflicts;
- черги на спільних оточеннях;
Всередині команди ми дійшли розуміння: щоб не втрачати, а прискорювати темп розробки, треба дотримувати підходу Trunk-Based Development (TBD) та мати повністю автоматизований процес CI/CD.
Як це працює: я виконую свою задачу, створюю МR, чекаю апрув, мержу — і далі запускається повністю автоматизований процес:
- білд;
- розгортання на staging;
- виконання автоматичних тестів;
- розгортання в прод;
- виконання автоматичних тестів на прод.
У цьому процесі CI/CD виключені всі ручні операції — зокрема, у нас немає мануальних тестувальників, всі тести автоматизовані.
Через
А тепер — про те, як ми зрозуміли, що саме потрібно покращувати та чи потрібно нам це взагалі. В цьому допомогла DORA.
DORA — набір метрик, розроблених Google, які допомагають виміряти та покращити ефективність процесів розробки та доставки коду.
Час внесення змін, частота розгортання, відсоток невдалих розгортань, час відновлення після збою — ці метрики дозволяють командам аналізувати та оптимізувати свої практики CI/CD.
Ми постійно відстежуємо DORA-метрики, щоб переконатися, що рухаємося в правильному напрямку, і постійно вдосконалюємо процеси. Не можна дозволяти собі ручних ритуалів релізів чергових версій з обмеженнями у днях.
Отже, переконуємось, що для продукту, який активно розвивається, налагоджений процес CI/CD — це маст хев. То що ж потрібно, щоб спокійно натиснути «merge», піти пити каву — і знати, що цей процес пройде успішно?
Далі розглянемо 7 практичних кроків, які допомогли нам досягти рівня, коли
Контроль якості
Почнемо з найочевиднішого. Високу якість і надійність продукту можна забезпечити лише за наявності достатнього тестового покриття.
Скільки тестів — достатньо? Як виміряти «достатність»?
Зазвичай усі говорять про покриття коду, але високого покриття можна досягти дуже легко, написавши кілька порожніх тестів. Тобто «coverage» сам по собі мало що говорить про якість тестів. Так, юніт-тестів має бути якомога більше — але вони не покривають усе. Наприклад, вони не перевірять коректність написання чи виконання SQL-запитів.
Для цього потрібні інтеграційні тести, які запускають реальні запити до реально піднятої БД і перевіряють:
— чи повернулося/змінилося саме те, що мало змінитися;
— і навпаки — чи не змінилося те, що змінюватися не повинно.
Якось наприкінці осені один з наших support-спеціалістів створив в системі тестовий обліковий запис мерчанта. Після певних процедур він вирішив прибрати за собою та натиснув в адмінці кнопку «ВИДАЛИТИ» мерчанта. «Під капотом» в сервісі видалявся запис цього мерчанта та всі його залежності, після чого відразу зупинився весь процесинг. Досліджуючи цей інцидент ми зрозуміли, що проблема була у запиті на видалення ключів доступів мерчанта. В нас не було написано інтеграційного тесту на цей запит, і помилка коштувала нам $50k.
UPDATE credentials SET active_till = now() FROM credentials as cr INNER JOIN channels as ch ON cr.channel_id = ch.id WHERE ch.account_id = $1 AND (cr.active_till >= now() or cr.active_till is null)
Ми керуємося правилом піраміди тестування:
- юнітами покриваємо все, що можливо (тут орієнтуємося на coverage);
- функціональними тестами (яких значно менше) — основні флоу сервісу;
- окрема команда AQA відповідає за e2e-тести, які перевіряють роботу всієї системи в цілому.
Визначити, чи у вас достатньо тестів, дуже просто: якщо ви виконуєте бізнес-задачу, змінюєте кілька місць у коді — і при цьому жоден тест не впав, це означає, що тестів у вас недостатньо :)
Тут варто зважати не лише на якість продукту, а й на якість коду. Наш код перевіряють численні лінтери — на швидкодію, оптимальність, конкурентність, дотримання стилю, вразливості та інші потенційні проблеми. І авжеж важливо дотримуватися практики Code Review — це етап, на якому можна виявити і попередити помилки до того, як вони потраплять до користувача. Для покращення процесу Code Review ми використовуємо CodeRabbit (ШІ).
Stage as Prod
Зупинимось детальніше на автоматизованих тестах. Тут йдеться не просто про окремо підняті шматочки системи з замоканими викликами — це тестування всієї системи з усіма її компонентами.
Щоб змоделювати поведінку системи на проді, потрібно мати оточення, ідентичне до прод — не лише з технічної точки зору (наприклад, за кількістю виділених ресурсів), а й з продуктової. На stage мають бути всі ті самі версії сервісів, що й на проді.
Якось один з наших інженерів робив задачу, в якій помилково зберігав до бази дані, використовуючи RO репозиторій. Локально та на staging усі тести пройшли, бо в конфігурації сервісу використовувалася одна і та сама БД. А от на прод це були мастер БД та її репліка, тому save(autoSettle)на прод на з’єднанні з реплікою викликав помилку. Такі помилки знаходяться легко, якщо stage оточення аналогічне до прод.
Як цього досягти? У пайплайнах є обов’язковий етап — деплой збірки на stage. Вийти на прод, оминувши автотести на stage, неможливо. Крім того, задеплоїтись на stage не можна з будь-якої гілки — лише з main. Це гарантує, що на stage потрапляють лише ті зміни, які вже пройшли Code Review і були влиті в main. А отже, це або те, що вже є на проді, або найближчим часом там з’явиться.
Чому важливо тримати stage «чистим» від експериментів? Якщо викатити на stage свої зміни з feature-гілки, ви ризикуєте зламати якусь бізнес-логіку. Десь в іншої команди впадуть автотести, і вони витратять свій час на те, щоб зрозуміти, що причина помилки — не в їхніх змінах. Наприклад, вони не отримають якийсь колбек, імейл або іншу нотифікацію, за яку відповідає ваш сервіс.
Отже, оточення stage-as-prod додає впевненості в тому, що тести, які щойно успішно пройшли, так само успішно пройдуть і на прод.
Observability
Нарешті сервіс виїхав в прод. Всі тести пройшли успішно, але нова feature занадто важлива і змінює суттєві компоненти системи. Треба переконатися, що вона працює як слід. А якщо щось йде не так — як оцінити ситуацію і зрозуміти, що саме відбувається і чому?
Важливий компонент надійності системи — моніторинг її роботи. Ви маєте бачити якомога більше інформації, щоб швидко і точно визначити стан системи та зрозуміти причини її поломки. В цьому допоможуть логи та метрики. Їх достатньо для процесу CI/CD, але для дослідження причин помилок важливо мати ще і трейсинг. Вам потрібен dashboard, де на одному екрані можна побачити функціонування сервісу.
Ось так виглядає наш summary dashboard, де відображені технічні метрики сервісу. Тут можна побачити важливу загальну інформацію по будь-якому з них.
Всі сервіси пишуть метрики за єдиним стандартом, і це дає можливість побудувати загальні дошки для всіх сервісів.
На цьому зображенні можна побачити всі вхідні HTTP-запити за одним, кількома обраними або навіть одразу за усіма сервісами.
Видно, що кількість запитів є доволі сталою, але в якийсь момент посипались
Ось те саме, але з запитами до баз даних.
Кожна команда має набір персоналізованих бордів під кожний сервіс. Ці дошки ексклюзивні, тому що сервіси мають свої особливості, за якими зручніше визначати їхній стан. У когось це HTTP-трафік, у когось — RPC, а у когось — черги. В деяких сервісах на центральне місце виводяться бізнес-метрики.
Rollback
Я задеплоївся, переглядаю метрики і раптом бачу, що щось пішло не так — якісь метрики почали перевищувати трешхолди. Наприклад, різко збільшилося споживання CPU або летенсі-запитів в БД, а через це і кількість з’єднань та затримка запитів.
Сервіс досі працює, але почуває себе погано, або, можливо, не працює взагалі. Що робити в такому випадку? Нагадаю, 1 хвилина даунтайму нашої системи — $10k. Немає часу розбиратися, що сталося, шукати причину, а тим паче — випускати фікс.
Найпростіше рішення — rollback. Система має бути побудована таким чином, щоб можна було одним натисканням повернути сервіс у попередній стан. Тут треба зважати на дві компоненти цієї задачі:
- DevOps. У CI/CD має бути налаштована окрема мануальна job для rollback — проста, без затримок, додаткових перевірок чи поступових перемикань. Її єдине завдання — якомога швидше підняти попередню стабільну збірку.
- Розробка. Інженери мають писати код зі зворотною сумісністю. Це означає, що якщо нам потрібно видалити таблицю чи колонку в базі або перейменувати зовнішні ресурси тощо, ми розбиваємо такі зміни на кілька етапів, щоб на кожному з них була можливість безпечно повернутися до попереднього стану.
У наведеному прикладі, щойно виникає найменший сумнів, я натискаю кнопку «rollback» — а вже потім з’ясовую причину збою. Можливо, проблема була спричинена зовсім іншим сервісом, який вийшов у прод одночасно з моїм. І я зможу натиснути «deploy» ще раз і знову вийти в продакшен.
Деплоймент стратегії
Обзервебіліті і ролбек, про який йшлося вище — це ручні інструменти для виправлення проблеми, яка вже сталася. Та чи можливо запобігти катастрофі на більш ранньому етапі?
Так — для цього можна використовувати щось розумніше, ніж просто rolling update. Rolling update — це безшовне оновлення вашого сервісу зі старої версії на нову; тобто версія сервісу міняється без зупинки його роботи.
У процесі оновлення поруч з працюючою версією підіймається інстанс з новою, оркестратор постійно питає у нової healthcheck, і щойно він отримує ОК — направляє весь трафік на новий под, а старий тушить. І так з усіма інстансами по черзі.
У результаті юзери майже не помічають переходу — спершу працюють зі старою версією, а потім автоматично переходять на нову. Розгляньмо, як оптимізувати цей процес.
Ми використали Blue/Green стратегію та Canary. Спочатку в системі підняті інстанси старої (blue) версії. Оркестратор підіймає поруч таку ж кількість інстансів нової (green) версії, чекає на їхню готовність і запускає smoke-тести — виключно на green-подах, на які ще не йде користувацький трафік.
Якщо тести не проходять, оркестратор тушить green-поди, а система продовжує працювати на старій blue-версії. Користувачі при цьому не помічають жодних помилок.
Якщо ж тести успішні, починається поступове перенаправлення трафіку на green-поди — по 10% щохвилини (це етап Canary). У цей час оркестратор уважно стежить за спеціальним deploy monitor: якщо різко зростає кількість помилок у сервісі, або споживання CPU/memory/latency, оркестратор повертає весь трафік на blue-поди і гасить green. Таким чином негативний вплив помилкового оновлення обмежується мінімальною кількістю користувачів.
Ну і нарешті, якщо всі 100% трафіку переключилися на green-версію, старі blue-поди вимикаються, і сервіс продовжує працювати на новій версії.
Feature flags (toggles)
Важливо чітко розділяти поняття деплой та делівері. Не завжди, але досить часто буває безпечніше випускати фічу не радикально змінюючи код, а мати обидві реалізації в коді одночасно — і стару, і нову.
Наприклад, нещодавно ми винесли частину функціональності з моноліту в окремий сервіс (детальніше про це я писав у статті кілька місяців тому). У моноліті я додав виклик нового сервісу та отримання даних з нього, однак старий розрахунок не видаляв. Обидва розрахунки працювали паралельно декілька тижнів, що дозволяло мені постійно порівнювати їхні результати.
А feature-флаг визначав, яку версію розрахунку я буду використовуватись у подальшій роботі. Коли я повністю переконався, що новий сервіс працює без помилок, я видалив з моноліту стару функціональність. Feature-флаг давав впевненість: якщо щось піде не так, я можу швидко переключити джерело ресурсу.
Так само feature-флаги можуть допомогти, якщо ви викатуєте нову функціональність, до якої ще не готові інші сервіси системи. Або якщо хочете мати на проді функціональність, яку можуть побачити лише тестувальники, пілотні мерчанти чи якийсь відсоток користувачів.
Часто feature-флаги допомагають рухатися по TBD. Може здатися, що в нас немає великих задач, але «кілька деплоїв у прод на день» зовсім не означає роботу лише над завданнями на
Фічу видаємо порціями, і доки не готова, вона залишається прихованою за feature флагом. Цей флаг прибирається лише в останньому MR.
On-call duty
Наостанок поговоримо про чергування — квінтесенцію надійності системи. Метрики та логи будуть давати мало, якщо за ними ніхто не стежить. Так само і можливість швидко зробити rollback нічого не варта, якщо її застосовують із запізненням.
На базі логів і метрик можна побудувати алертинг, але важливо, щоб за ними спостерігала компетентна людина, яка знає, що з ними робити. Тут йдеться не лише про чергування в межах команд у робочий час — хоча таке в нас теж є. Маю на увазі чергування за критичними сервісами всієї компанії, від яких залежить процесинг платежів. За їхньою роботою цілодобово спостерігає черговий інженер, який реагує на спеціально налаштовані алерти, а через 24 години його змінює наступний.
Чергування — це необхідна, але недостатня умова. Лише її недостатньо, але і без неї також не можна.
Ми дійшли до моделі, де чергуванням займається невелика кількість досвідчених інженерів — здебільшого це техліди, адже найкраще розуміють систему ті, хто її створює. Процес чергування побудували на базі Grafana OnCall. У спеціальний Slack-канал приходять алерти з повним контекстом: що саме спрацювало, в якому сервісі, посилання на метрики та логи, які призвели до цього алерту, інформацію про останні деплої, а також повідомлення від зовнішніх систем про можливі проблеми.
Якщо трапляється alert, саме черговий:
- Реагує на нього та вирішує, наскільки він важливий і якщо це інцидент, починає процедуру вирішення інциденту.
- Координує всіх учасників процесу.
- Долучає потрібних спеціалістів.
- Комунікує зі стейкхолдерами та мерчантами, оновлює status page компанії.
- Шукає шляхи вирішення інциденту.
- Нотує перебіг всього процесу у спеціальному документі.
- Збирає ретроспективну зустріч для аналізу події та визначення action items, щоб уникнути такого в майбутньому.
Висновок
Кожен із перелічених пунктів додає впевненості під час деплою, а всі разом гарантують, що задачу можна безпечно деплоїти в прод хоч в п’ятницю, хоч кілька разів на день.
А де ж мінуси, спитаєте ви?
Вони є. Безпечний CI/CD-процес існує, але досягти його непросто — щоб побудувати таку систему, потрібні місяці або роки. А особливою перепоною є стан, коли все і так наче працює, хай без тестів або без метрик, але працює — нащо щось міняти?
Та якщо ви будуєте живу й якісну систему, яка активно розвивається, варто інвестувати у це час та ресурси. Інвестиції згодом відіб’ються шляхом зменшення втрат від інцидентів і через оптимізацію процесу розробки.
Підсумуємо:
- Лінтери, юніт-тести й функціональні тести дають впевненість, що нічого не зламалось ще до мержу в main-гілку.
- Автоматизовані тести на stage (який as prod) гарантують, що зміни не зачепили інші сервіси.
- Blue/green + Canary дають впевненість: навіть якщо тести щось пропустили, у проді проблема з’явиться лише ненадовго й зачепить мінімальну кількість користувачів.
- А якщо й тут щось не спрацює — черговий on-call інженер моментально помітить відхилення за метриками, зможе швидко відкатити сервіс до попередньої версії або вимкнути feature-флаг з новою функціональністю.
Отже, можна натиснути «merge» і йти пити каву. За пів години робота буде в проді.
І це — хороша система:)
Корисні матеріали
$10.000 за хвилину даунтайму: архітектура, черги та стрімінг у фінтех
Як ми розпилювали моноліт. Наш досвід переходу до мікросервісів
DORA research
The Art of Software Testing
The Field Guide to Understanding ’Human Error’
Observability Engineering: Achieving Production Excellence
Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation (Addison-Wesley Signature Series (Fowler))
Site Reliability Engineering: How Google Runs Production Systems
The Site Reliability Workbook: Practical Ways to Implement SRE
Стартувало літнє зарплатне опитування DOU. Збираємо відповіді 15 тисяч ІТ-фахівців в анкеті. Долучайтеся!
68 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів