Щоденні релізи: досвід продуктових ІТ-компаній
Редакція DOU звернулася до українських продуктових ІТ-компаній, щоб з’ясувати, як успішно релізити на продакшен по кілька разів на день та уникнути факапів. Як зорганізувати процес? Які проблеми виникають і як з ними впоратися?
Дмитро Волошин, CTO і co-founder в Preply
Ми в Preply пройшли довгий шлях від тижневих деплоїв, виконаних кваліфікованими інженерами, до щогодинних деплоїв, зреалізованих усією командою. Про історію еволюції наших процесів я розповідав на Highload FW days.
Тижневі релізи зумовлювало те, що в нас був громіздкий процес ручного тестування. Коли ж ми змогли його частково завтоматизувати, почали релізитися щоденно. Але ця функція належала реліз-інженерам і була дуже монотонною. Щоб якось її гейміфікувати, ми ввели в штатний розпис свиню (на фото).
Реліз-інженер, який був відповідальний за релізи цього дня, брав її собі на стіл, і всі знали, до кого звертатися, якщо щось не працює або не релізиться. Згодом свиню десь загубили, і ми вирішили: це знак, що потрібно завтоматизувати процес. Основна мотивація полягала в тому, що наші інженери пишуть код постійно, і немає сенсу чекати наступного дня, щоб зарелізити код, якщо він потрібен користувачам уже сьогодні. Тобто ми розуміли, що нам треба зоптимізовувати метрику time to market.
Основні речі, які ми зімплементували, і які вможливили релізитися по десять або більше разів на день (enablers):
- Стабільний Continuous Integration- / Continuous Deployment-процес, з Дженкінсом і тестами. Бажано також, щоб end-2-end-тести відбувалися в середовищі, якнайбільше наближеному до продакшену. У нашій ситуації завдяки Kubernetes кожен інженер може розвернути тестовий стенд зі своєї гілки коду.
- Trunk-Based Development — процес розробки, коли є якнайменші feature branches, які вливаються безпосередньо в master без проміжних гілок.
- Feature flags — це був еволюційний процес, але загалом ми змогли прийти до того, щоб майже кожну нову фічу можна було вимкнути за допомогою feature flag. Цьому також сприяла сильна культура A/B-тестування.
- Access democratization — кожен має можливість вливатися в мастер (після quality gates) і релізитися на продакшен. Відповідальність за свій код з часу написання до моменту доставки його кінцевому користувачу сприяє якості й швидкості.
- Service ownership — попри те, що в нас монорепозиторій, ми намагаємося поділяти наш монолітний продукт на сервіси зі слабшою зв’язністю. Це дає змогу мати набагато менш зв’язний продукт, коли кожна його частина має власника, якого навіть формалізовано через Codeowners-файл. Оскільки сервіси більш-менш незалежні, їх також можна релізити незалежно.
Основним побоюванням було те, що фічі різних команд конфліктуватимуть після вливання в мастер. Тобто через те, що команди не знають, хто що релізить, виникатимуть ситуації, коли команди ламають фічі інших команд. Це побоювання не справдилося: основні помилки визначаємо на етапі unit/integration/e2e-тестування. Якщо щось падає після деплоя, завдяки feature flags можна миттю виправити це для кінцевого користувача або відкотити зміни. Зазвичай баги виявляємо на етапі тестування розробником (ми нещодавно відмовилися від функції manual QA), інколи баги повідомляють кінцеві користувачі відділу служби підтримки користувачів. У середньому пропускаємо
Вищеописане було б неможливе, якби не сильна DevOps-команда, яка в нас сформувалася, адже будь-яку автоматизацію все одно виконують люди. Бажання DevOps-команди дати цінність кінцевим користувачам продукту було основним драйвером поліпшень, що відбулися.
Владимир Никонов, руководитель департамента разработки платформы в Terrasoft
Организацию процесса частых поставок на production в наших командах мы проходили не один раз и в составе многих команд. На протяжении всех изменений ключевым моментом в данном вопросе стоят не технологии, не формат и размер продукта, и даже не технологический уровень команд. На мой взгляд, ключевым фактором является культура, которая царит в команде.
Начиная менять свой подход к поставкам, вы должны быть готовы к ошибкам, причем иногда довольно болезненным. В нашей практике в начале этого пути было все: от простой недоступности сервиса до временной потери данных, которые пришлось восстанавливать в ручном режиме. Оглядываясь на все эти события, можно задать вопрос: зачем же тогда идти на такие риски, меняя привычные и отлаженные процессы?
Я могу с уверенностью сказать, что культура частых поставок функционала:
- в корне меняет отношение команды к разрабатываемому функционалу;
- меняет восприятие приоритетов задач;
- позволяет команде почувствовать продукт с пользовательской стороны и рождает необходимость технических инноваций, которые делают ваш продукт/сервис/код на порядок лучше.
Все наши команды, которые работают в таком режиме, знают актуальные потребности клиентов и генерируют набор улучшений работы пользователей, стараясь включить в очередную поставку небольшую порцию улучшений.
Для организации такого механизма мы сделали несколько вещей:
- Предоставили командам большую свободу действий при принятии решений (если этого не сделать, вы столкнетесь с проблемой согласований, которая будет затягивать поставки).
- Предоставили командам необходимые права и инструменты для быстрого решения инцидентов на production (с одной стороны, это повышение операционных рисков, с другой — мы получили сокращение времени решения критичных инцидентов).
- Обучили команды разработки в областях, где их компетенции были недостаточны, но необходимы для обслуживания своих сервисов.
Помимо обучений, мы эволюционно дошли до автоматизации всех возможных процессов поставок и операций на production-среде (установка обновлений, переключение трафика и бекапирование и восстановление данных). Человеческий фактор — основная причина сбоев, и мы минимизируем его при любой возможности. Именно поэтому нам пришлось серьезно поработать в области автоматизации тестирования и получения возможности тратить минимум ручных усилий при признании изменений готовыми к релизу на production.
Учитывая наличие большого количества поставок, мы постоянно работаем над их скоростью, уменьшая простои сервиса или моменты переходных периодов. Технически подросли не только команды, освоившие основные принципы, которые надо учитывать при разработке и проектировании функционала для высокодоступных систем, ни и сам продукт приобрел новые возможности, позволяющие проводить канареечное тестирование и включать или отключать часть функционала.
Отдельные операции, требующие ранее рестарта сервиса, теперь работают на «горячую». Появились возможности интеграции систем автоматизации и мониторинг бизнес-функций для превентивной реакции на сбои без ожидания фидбека от пользователей.
На текущий момент у нас восемь команд, которые самостоятельно работают в режиме частых поставок. В итоге, помимо роста уровня команд и возможности реализовывать сложные фичи в продукте, мы получили еще и увеличение скорости развития самого продукта, потому что теперь имеем меньше накладных расходов на внесение изменений в код и его поставку клиенту. Далее планируем продолжать внедрять этот подход в остальных направлениях.
Никита Артемчук , руководитель департамента разработки в Prom.ua
В соавторстве с Виталием Харитонским , СТО в Prom.ua
Частота релизов на продакшен зависит от инфраструктуры. Так, Prom.ua — крупнейший маркетплейс страны. На его платформе создали интернет-магазины больше 236 тыс. предпринимателей, которые продают 111 млн товаров. Проекту 11 лет, и в нем параллельно существуют монолитная и микросервисная инфраструктуры.
В монолитной все элементы связаны между собой. Чтобы обновить отдельную часть, нужно обновлять весь огромный Prom. Поэтому большие изменения выходят два раза в неделю, а небольшие могут чаще. В целом,
Чтобы быть более гибкими, мы постепенно переходим к микросервисной инфраструктуре. Она состоит из автономных элементов, обновлять которые можно отдельно и хоть каждый час. Это снижает время исправления ошибки за счет того, что не нужно обновлять всю структуру.
Налаживая этот довольно сложный процесс, мы пытаемся найти баланс между безопасностью проекта и гибкостью разработки. Как правило, выкатка происходит в несколько этапов:
- Поскольку разработчиков много (над Prom.ua работает 107 разработчиков и еще 500+ IТ-специалистов), то их изменения могут противоречить друг другу. При помощи специального ПО мы их проверяем и ищем несоответствия между новыми изменениями со стороны разработчика и уже существующей логикой проекта.
- Затем передаем код в тестовую среду. У нас таких несколько. Это разные копии Prom, которые доступны только нам. В этих тестовых средах код проверяют другие разработчики и тестировщики, а затем его прогоняют через автоматические тесты.
- В момент выкатки на Prom мы еще раз прогоняем новую версию проекта через автотесты.
Также мы стараемся не выливать большие изменения сразу на всех пользователей. Поэтому со старта функционал доступен небольшой группе клиентов, но постепенно расширяем изменения на всех.
Говоря о проблемах, часто сложно предугадать, что твое текущее изменение может сломать в другом куске продукта. Ведь Prom — большой и взрослый проект. Его элементы очень сильно зависят друг от друга из-за монолитной инфраструктуры.
Поэтому важно на человеческом уровне синхрониться друг с другом, чтобы не нарушить либо техническую целостность продукта, либо бизнес-логику в связанных между собой частях функционала.
Для этого на техническом уровне у нас есть культура взаимного ревю. Проверять могут как программисты из одной команды, так и разработчики из смежных. Особенно это важно, когда изменения затрагивают части функционала, относящиеся к нескольким кускам продукта.
Быстро отлавливать проблемы помогают автотесты и автоматическая проверка доступности ключевых кусков функционала продукта. Также мы практикуем регулярные встречи product-менеджеров и тех. лидов, где делимся планами разработки на ближайшее время. Наконец, есть демовстречи, на которых команды показывают всем — суппорту, продажам, маркетингу — будущие изменения.
Весь процесс максимально автоматизирован: достаточно нажать одну кнопку в нашем CI/CD-интерфейсе, и все произойдет само собой. Релиз-команда и разработчики пользуются этим, чтобы релизить с телефона, по дороге домой, ночью, в походе и в других нестандартных ситуациях. Тут нужно понимать, что только мажорный релиз происходит в рабочее время, когда все на местах. Минорные релизы проходят часто незаметно для окружающих, в любое время суток.
Для того чтобы контролировать релиз постфактум, все приложение и связанная инфраструктура покрыты метриками и алертами. Если что-то не работает, то с вероятностью 95% мы знаем об этом и работаем, чтобы устранить причины.
Много сложностей происходит на стыке проекта и его внешних зависимостей: базы данных, поисковый движок и конфигурация сетевых компонентов. Такие вещи достаточно сложно и рискованно автоматизировать, поэтому что-то приходится делать вручную. К счастью достаточно редко.
Для того чтобы автоматизировать процесс деплоя, мы внедрили GitLab CI/CD, Docker и Kubernetes. Это значительно упростило жизнь отделу инфраструктуры, а также позволило разным командам быстрее запускать свои микросервисы и автономно использовать железо на продакшене.
Леонід Литвиненко, СТО в YouScan
Почнімо з того, що я трохи розповім про структуру команди розробки. Увесь колектив, майже 20 людей, поділили на чотири сквади — мінікоманди, що відповідають за різні напрями розвитку.
Команда, що відповідає за збір даних, — Data Squad; команда, що відповідає за смарт-фічі, як-от аналіз зображень чи визначення трендів, — Data Science Squad; Platform Squad відповідає за автоматизацію нашої платформи й виробляє найкращі практики, які потім приймають інші команди; і Product Squad — команда, що безпосередньо розробляє основний продукт.
Нам подобається, що ми досягаємо одночасного руху в чотирьох напрямах з порівняно невеликою кількістю людей. Проте, щоб цього досягти, потрібно завтоматизовувати якомога більше речей на своєму шляху: CI/CD, prod-like staging environments, safe rollback, автоскейлінг, дашборди, сповіщення, реакцію на інциденти, run books і post-mortems.
Частий деплоймент — це, з одного боку, теж спосіб безпечної розробки невеликими командами, а з іншого боку, часті деплойменти не вийде робити без тотальної автоматизації.
За ці пів року ми вже зробили понад 2500 деплойментів у продакшен, але в нас майже сотня сервісів на більш як двох сотнях віртуальних машин.
Іноді ми жартуємо, що в нас log driven development, тому що частину релізів можна реально перевірити лише на повному навантаженні. Для цього активно використовуємо в нашому продукті feature toggles. Та й узагалі, ми любимо feature toggles: якщо можна щось не цілком завершене задеплоїти, заховавши за feature toggle, — виберемо цей варіант.
Крім інструментів, потрібна ще й певна культура для того, щоб деплоїти часто. «You built it — you run it» — це про нас: розробник і деплоїть, і тестує, і стежить за роботою в production. Розробник уміє робити roll back або roll forward (коли замість деплою попередньої версії роблять виправлення й ще один новий деплой).
Ми практично завжди працюємо в master-гілці, гілки створюємо лише для великих завдань або принципових архітектурних змін.
Трошки про міграцію даних: у нас трапляються періоди, коли ми змінюємо місце й формат для величезної кількости даних, а також міграції, що тривають тижнями. У цей час ми підтримуємо код, який уміє працювати з двома версіями даних. На цю тему ми робили хорошу доповідь.
Дам ще кілька цікавих посилань на цю тему: Friday deploy freezes are exactly like murdering puppies і
Stack Overflow: How We Do Deployment — 2016 Edition.
Віктор Прокопенко, керівник функціонального розвитку в IT-Enterprise
Не буває релізів «часто» — є «навіщо» і «коли» потрібен реліз. На практиці — це майже завжди відомо. Для виходу нового функціоналу є терміни, вони, як правило, узгоджені, озвучені, часто опубліковані. Тут немає інших варіантів, окрім як встигнути у строк і видати реліз не «часто», а «вчасно».
Важливо також, навіщо виходить реліз: нова функціональність чи усунення багів? Адже, якщо функціонал новий, то можливі баги. Частіше «постановочного» характеру, ніж технічного. Для технічних — існують технічні засоби виявлення. Але трапляється, що якась функціональність не розглядалась, а сьогодні вже потрібна. Вона не документована, але, з точки зору користувача, її відсутність або реалізація не так, як очікував користувач, — це вже «бага».
Отже, потрібно вміти їх швидко виявляти і швидко виправляти. Тому неважливо — часто чи не часто виходить реліз — важливо, щоб своєчасно і без багів, навіть імовірних. Можна, звичайно, довше тестувати, але ринок втомиться чекати і почне користуватися чимось іншим. Зрештою, функціонал буде без багів, але не затребуваний. Щоб все передбачити, потрібен весь арсенал знань, засобів розробки і тестування, організації, обчислювальних потужностей і ще багато чого.
Абстрагуємося від багів. Розглянемо приклад нової функціональності. Вона буває двох типів — індивідуальна (замовна) і для масового ринку.
Індивідуальна (замовна) передбачає тісну роботу з клієнтом. Необхідно зрозуміти, що йому потрібно і у відведені терміни вкластись із реалізацією. Часто ці терміни знаходяться усередині інших проектних термінів, тобто дуже стислі. Аналітична робота обов’язково проводиться, але клієнт вимагає термінів реалізації більш коротких, ніж потрібно для глибокої аналітики. Буває також, що за аналітичну роботу клієнт не готовий платити. У цьому випадку необхідна тісна співпраця із зацікавленими людьми від клієнта. Розробка, демонстрація прототипів, зміна вимог виконуються паралельно. Як правило, такі процеси не надто чутливі до багів на етапі розробки і дослідної експлуатації, тому що клієнт з розробником «пливуть в одному човні». Але баги все одно потрібно «лагодити» швидко. Ця технологія застосовувалася нами дуже часто на початку
Для масового ринку — ситуація інша. Баги в цьому софті не можна допускати, тому що це може призвести до втрати функціональності на деякий час і зупинці якихось процесів у значної кількості клієнтів. Це завжди загрожує репутаційними втратами (як мінімум). А якщо клієнт може скористатись у «проблемний» час іншим софтом іншого виробника, то це може призвести до втрати клієнта.
Такий софт на момент внесення змін, як правило, непогано накритий функціональними тестами-сценаріями. Якщо йде розробка нового сценарію, то потрібно розробити нову порцію тестів. У разі необхідності — в старі варто внести зміни ще до розробки, або у процесі. Такий софт обов’язково повинен бути покритий тестами, тому що ефективність автоматизованого тестування трохи вище мануального. Але без мануального теж не обійтися. І бажано, щоб після мануального тестування у базі даних залишався тест. Тому, на мій погляд, до таких змін необхідно підходити дуже зважено. Іноді можна затягнути часом не тестування і це виправдовується. Але якщо з часом тягнути не можна — наприклад, законодавчі зміни — то потрібно розподілити час так, щоб до кінця завжди залишався запас. Це завдання менеджера. Не обов’язково про це «говорити вголос» усій команді, але ризики потрібно мінімізувати.
Якщо одночасно проявився баг і з’явилася фіча — чи варто випускати два релізи? Якщо бага проявилась — потрібно швидко її полагодити. Усі тести займають час — а його немає. Тут на допомогу приходить логіка, формалізація, здоровий глузд, знання системи і т.д. Не варто шкодувати часу фахівців високої кваліфікації.
Завдання цього етапу — за найкоротші терміни прийняти рішення, внести зміни в софт, звузити область тестування до мінімуму, але при цьому необхідно бути впевненим, що помилку виправлено. Після усунення проблеми в продуктиві можна приступати до «розбору польотів», але не раніше. Спочатку потрібно ліквідувати проблему.
При виконанні такого роду завдань треба бути принциповим у частині складання релізу. Не треба змішувати «мух з котлетами»: якщо ми швидко лагодимо багу, то нові фічі, які хотілося б також включити разом у це оновлення, сюди допускати не можна. Для фічей необхідна своя процедура тестування та верифікації. Вона займає час і розтягує тривалість некоректної роботи функціоналу загалом. Найгірше, що при змішуванні усунення баги з новим функціоналом — бага більш тривалий час знаходиться в продуктиві.
Існує організаційно-технічна процедура підготовки релізу. Процедура підготовки релізу програмістами автоматизована. За рахунок цього мінімізуються кваліфікаційні відмінності розробників і людський фактор «забув», «пропустив», «це не потрібно, не впливає», і т.д. Реліз неможливо закрити технічно, не кажучи про публікації, якщо він не пройшов повну покрокову процедуру підготовки.
У нас є відділ якості та свої тестувальники. Процес тестування релізу теж автоматизований. Застосовуються як стандартні технології — Unit testing, Integration testing, UI, Service testing, так і власна система функціонального тестування, що має ряд переваг для задач ERP-системи, зокрема — для багатьох паралельних сценаріїв, продуктивності, працездатності служб.
На допомогу розробникам так само приходять результати щоденних нічних прогонів тестів. Є спеціальні люди і процедури, які забезпечують виконання завдань «полагодити тест» у найкоротший термін, особливо, коли випускається реліз.
Ми випускаємо релізи щомісяця. Система тиражна. Постійно виконується багато проектів і необхідний новий функціонал. Нам потрібно встигати в проектні терміни. Згодом співробітники звикають до щомісячного релізу і це входить в культуру. Процеси розробки підлаштовуються самі під ці тимчасові рамки знизу. Якщо щось не встигають, то або прикладають більше зусиль, або домовляються про зміщення термінів. Кожен з підходів застосуємо, але залежить від ситуації. Єдиної таблетки немає. Потрібно думати і домовлятися.
Унікальність, перш за все, диктує наш основний продукт — ERP-система. Особливістю ERP-систем є велика кількість функцій, модулів і підсистем. Для мінімізації ризиків у цій частині у нас розділені технологічно клієнтські «відтворення» і «серверні обчислення». Це, в свою чергу, вимагає підтримки власного інструментарію високорівневої розробки. Цей підхід непогано себе зарекомендував практично, тому що суттєво мінімізує як кількість багів, так і швидкість розробки. Розробникам не потрібно займатися написанням коду типу «відпрацювання кнопки Esc». Він за замовчуванням працює правильно. Якщо, раптом, необхідно внести зміни у стандартну поведінку, то вони вносяться, але вкрай рідко. Крім цього, єдиний підхід до роботи в інтерфейсах для користувачів мінімізує тимчасовий поріг для входу, тому що все схоже.
Крім цього, клієнтську і серверну логіку пишуть різні команди, що сприяє спеціалізації кваліфікацій і кращій якості.
Для нас як продуктової компанії, знання продукту, потреб своїх клієнтів та їх очікувань дуже важливе. І наявність цих знань у розробників лише сприяє створенню якісного продукту. Тому ми періодично підвищуємо прикладні знання розробників, ставимо в плани розвитку отримання нових скілів, знань і компетенцій, відвідування семінарів, запрошення тренерів, участь в івентах та інше.
Наступний момент — робота на випередження. Якщо клієнт пише — значить йому вже «набридло терпіти». Ми попереджаємо такі ситуації: для цього з низки проектів отримуємо щоденні зведення «здоров’я», міряємо і контролюємо «здоров’я системи». У зведеннях здоров’я ми спостерігаємо щоденну динаміку роботи в розрізі різних показників і маємо інтегральний показник «здоров’я», який контролюється особливо ретельно.
Сьогодні значної популяності набули месенджери. Ми застосовуємо їх для сигналізування у режимі on-line ситуацій виходу за встановлені межі якихось лічильників. Наприклад, стрибок завантаження процесора або пам’яті. Якщо він настав, то, не чекаючи щоденного звіту, відповідальні за напрямок отримують повідомлення і приступають до роботи негайно. Це стосується як лічильників на продуктиві, так і на робочих потужностях в офісі.
Крім цього, миттєва реакція дозволяє швидше розслідувати інцидент за гарячими слідами та ідентифікувати причину проблеми. Якщо причина відома — це вже пів-справи.
У разі, коли клієнт «закритий» від зовнішнього світу з різних причин (такі є), ми поставляємо свою систему контролю і накопичення статистики. Як правило, це великий бізнес зі своїм IT-відділом, де є відповідальні за такий напрямок люди.
Чи потрібно «дублювання функціональності»? Питання філософське, вимагає додаткових витрат і суперечить твердженням у колах розробників, що «дві програми, які реалізують один алгоритм, на одних і тих же даних дають різні результати».
Але, тим не менш, у деяких рідкісних випадках — особливо, коли виконується перехід на новий функціонал і є непевність, а надійність дуже важлива — ми змушені це застосовувати.
При такому підході необхідно пам’ятати, що це тимчасове рішення. Тому в програмному коді обов’язково повинна бути закладена система збору статистики і обов’язкова періодична процедура обробки цієї статистики.
Дуже короткі висновки:
- Релізи потрібно робити.
- Релізи потрібно робити, коли вони потрібні.
- Релізи потрібно впроваджувати в культуру всього процесу розробки та експлуатації.
- Треба застосовувати автоматизацію для контролю якості — тоді ніхто нічого не забуває.
- Контроль якості має бути на всіх етапах: від постановки — до кінця життєвого циклу продукту.
- Процеси якості треба контролювати: потрібно міряти показники і утримувати їх в установлених межах.
- Баги бувають. Не помиляється той, хто нічого не робить. Потрібно вчитись усувати їх максимально швидко, але — що ще важливіше — необхідні процеси, які не допускають появу цих багів.
- Потрібно постійно моніторити систему. Баги треба виправляти до того, як клієнт про це скаже чи помітить.