Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

На РІТ 2019 наш колега Олександр Коротков зробив доповідь про автоматизацію розробки в ЦІАН: щоб спростити життя та роботу, ми використовуємо власну платформу Integro. Вона відстежує життєвий цикл завдань, знімає з розробників рутинні операції та помітно скорочує кількість багів у production. У цьому пості ми доповнимо доповідь Олександра та розповімо, як пройшли шлях від простих скриптів до об'єднання open source продуктів через власну платформу і чим у нас займається окрема команда автоматизації.
 

Нульовий рівень

"Нульового рівня не буває, я такого не знаю"
Майстер Shifu з м/ф «Кунг-фу Панда»

Автоматизація в ЦІАН розпочалася через 14 років після заснування компанії. Тоді у команді розробки було 35 осіб. Насилу віриться, так? Звичайно, в якомусь вигляді автоматизація таки існувала, але окремий напрямок з безперервної інтеграції та доставки коду почав формуватися саме у 2015 році. 

На той момент ми мали величезний моноліт з Python, C# і PHP, розгорнутий на Linux/Windows серверах. Для деплою цього монстра ми мали набір скриптів, який ми запускали вручну. Було ще складання моноліту, що приносить біль і страждання через конфлікти при злитті гілок, правки дефектів та перескладання «з іншим набором завдань у білді». Спрощено виглядав так:

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Нас це не влаштовувало, і ми хотіли побудувати повторюваний, автоматизований і керований процес збирання та деплою. Для цього нам була потрібна CI/CD система, і ми вибирали між безкоштовною версією Teamcity і безкоштовною Jenkins, тому що ми з ними працювали та обидві влаштовували нас за набором функцій. Вибрали Teamcity як свіжіший продукт. Тоді ми ще не використовували мікросервісну архітектуру та не розраховували на велику кількість завдань та проектів.

Приходимо до ідеї про власну систему

Використання Teamcity прибрало лише частину ручної роботи: залишилося ще створення Pull Request-ів, просування завдань за статусами в Jira, вибір завдань для релізу. Із цим система Teamcity вже не справлялася. Потрібно було обирати шлях подальшої автоматизації. Ми розглядали варіанти роботи зі скриптами у Teamcity або перехід на сторонні системи автоматизації. Але врешті-решт вирішили, що потрібна максимальна гнучкість, яку дає тільки власне рішення. Так виникла перша версія внутрішньої системи автоматизації під назвою Integro.

Teamcity займається автоматизацією на рівні запуску процесів складання та деплою, а Integro сфокусувалася на верхньорівневій автоматизації процесів розробки. Потрібно було об'єднати роботу із завданнями в Jira з обробкою зв'язаного вихідного коду у Bitbucket. На цьому етапі всередині Integro почали з'являтися свої workflow для роботи із завданнями різних типів. 

Через збільшення автоматизації у бізнес-процесах зросла кількість проектів і run-ів у Teamcity. Так прийшла нова проблема: одного безкоштовного інстансу Teamcity стало не вистачати (3 агенти та 100 проектів), ми додали ще один інстанс (ще 3 агенти та 100 проектів), потім ще. У результаті ми отримали систему з кількох кластерів, якою було складно керувати:

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Коли постало питання про 4 інстансі, ми зрозуміли, що далі так жити не можна, тому що сукупні витрати на підтримку 4 інстансів вже не лізли в жодні рамки. Виникло питання купівлі платної Teamcity або вибору на користь безкоштовної Jenkins. Ми провели розрахунки з інстансів і планів автоматизації і вирішили, що будемо жити на Jenkins. Через пару тижнів ми перейшли на Jenkins і позбулися частини головного болю, пов'язаного з підтримкою кількох інстансів Teamcity. Тому ми змогли зосередитися на розробці Integro та допилюванні Jenkins під себе.

Зі зростанням базової автоматизації (у вигляді автоматичного створення Pull Request-ів, збору та публікації Code coverage та інших перевірок) з'явилося стійке бажання максимально відмовитися від ручних релізів та віддати цю роботу роботам. Крім цього, всередині компанії почався переїзд на мікросервіси, які вимагали частих релізів, причому окремо один від одного. Так ми поступово дійшли автоматичних релізів наших мікросервісів (моноліт поки що випускаємо вручну через складність процесу). Але, як це буває, виникла нова складність. 

Автоматизуємо тестування

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Через автоматизацію релізів прискорилися процеси розробки, причому частково за рахунок пропуску деяких етапів тестування. А це призвело до тимчасової втрати якості. Звучить банально, але разом із прискоренням релізів потрібно було змінювати методологію розробки продукту. Потрібно було замислитися про автоматизацію тестування, прищеплення персональної відповідальності (тут мова про «прийняття ідеї в голові», а не грошові штрафи) розробника за код і баги в ньому, а також про рішення з випуску/не випуску завдання через автоматичний деплой. 

Усуваючи проблеми з якістю, ми дійшли двох важливих рішень: стали проводити канаркове тестування та впровадили автоматичний моніторинг фону помилок з автоматичним реагуванням на його перевищення. Перше рішення дозволило знаходити очевидні помилки до повноцінного влучення коду в production, друге зменшило час реагування на проблеми в production. Помилки, звичайно, бувають, але ми витрачаємо більшу частину часу і сил не на виправлення, а на мінімізацію. 

Команда автоматизації

Зараз у нас штат із 130 розробників, і ми продовжуємо рости. Команда з безперервної інтеграції та доставки коду (далі - команда Deploy and Integration або DI) складається з 7 осіб і працює у 2 напрямках: розробка платформи автоматизації Integro та DevOps. 

DevOps відповідає за Dev/Beta оточення сайту CIAN, оточення Integro, допомагає розробникам у вирішенні проблем та виробляє нові підходи до масштабування оточень. Напрямок розробки Integro займається як самої Integro, так і суміжними сервісами, наприклад, плагінами для Jenkins, Jira, Confluence, а також розробляє допоміжні утиліти та програми для команд розробників. 

Команда DI працює спільно з командою Платформи, яка займається розробкою архітектури, бібліотек та підходів до розробки усередині компанії. Разом з цим будь-який розробник усередині ЦІАН може зробити внесок в автоматизацію, наприклад, зробити мікроавтоматизацію під потреби команди або поділитися класною ідеєю, як зробити автоматизацію ще кращою.

Листковий пиріг автоматизації в ЦІАН

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Всі системи, задіяні в автоматизації, можна поділити на кілька шарів:

  1. Зовнішні системи (Jira, Bitbucket та ін.). З ними працюють команди розробки.
  2. Платформа Integro. Найчастіше розробники не працюють із нею безпосередньо, але саме вона підтримує роботу всієї автоматизації.
  3. Сервіси доставки, оркестрування та виявлення (наприклад, Jeknins, Consul, Nomad). З їх допомогою ми розгортаємо код на серверах та забезпечуємо роботу сервісів один з одним.
  4. Фізичний рівень (сервера, ОС, суміжне ПЗ). На цьому рівні працює наш код. Це може бути як фізичний сервер, і віртуальний (LXC, KVM, Docker).

Виходячи з цієї концепції, ми ділимо зони відповідальності всередині команди DI. Два перші рівні лежать у зоні відповідальності напряму розробки Integro, а два останні рівні вже в зоні відповідальності DevOps. Такий поділ дозволяє сфокусуватися на завданнях і не заважає взаємодії, тому що ми знаходимося поряд один з одним і постійно обмінюємося знаннями та досвідом.

Цілий

Зосередимося на Integro і почнемо з технологічного стеку:

  • CentOs 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (старий моноліт Integro залишиться на Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (Embedded)
  • Grafana + Graphite + Prometheus + Jaeger + ELK
  • Web UI: React (CSR) + MobX
  • SSO: Keycloak

Ми дотримуємося принципу мікросервісної розробки, хоча у нас і є legacy у вигляді моноліту ранньої версії Integro. Кожен мікросервіс крутиться у своєму docker-контейнері, сервіси спілкуються між собою за допомогою HTTP-запитів та RabbitMQ-повідомлень. Мікросервіси знаходять один одного через Consul і виконують запит, проходячи авторизацію через SSO (Keycloak, OAuth 2/OpenID Connect).

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Як реальний приклад розглянемо взаємодію з Jenkins, яка складається з наступних кроків:

  1. Мікросервіс управління workflow (далі Flow-мікросервіс) хоче запустити складання в Jenkins. Для цього він через Consul знаходить IP:PORT мікросервіс інтеграції з Jenkins (далі Jenkins-мікросервіс) і відправляє в нього асинхронний запит на запуск збірки в Jenkins.
  2. Jenkins-мікросервіс після отримання запиту формує та віддає у відповідь Job ID, за яким потім можна буде ідентифікувати результат роботи. Разом з цим він запускає складання в Jenkins через виклик REST API.
  3. Jenkins виконує складання і після її закінчення відправляє webhook з результатами виконання в Jenkins-мікросервіс.
  4. Jenkins-мікросервіс, отримавши webhook, формує повідомлення про закінчення обробки запиту та прикріплює до нього результати виконання. Сформоване повідомлення надсилається в чергу RabbitMQ.
  5. Через RabbitMQ опубліковане повідомлення потрапляє до Flow-мікросервісу, який дізнається про результат обробки свого завдання, зіставивши Job ID із запиту та отриманого повідомлення.

Зараз у нас близько 30 мікросервісів, які можна розбити на кілька груп:

  1. Управління конфігураціями.
  2. Інформування та взаємодія з користувачами (месенджери, пошта).
  3. Робота із вихідним кодом.
  4. Інтеграція з інструментами деплою (jenkins, nomad, consul тощо).
  5. Моніторинг (релізів, помилок тощо).
  6. Web-утиліти (UI для управління тестовими середовищами, збору статистики тощо).
  7. Інтеграція з таск-трекерами та подібними системами.
  8. Управління workflow для різних завдань.

Workflow завдання

Integro автоматизує дії, пов'язані з життєвим циклом завдання. Спрощено під життєвим циклом завдання будемо розуміти workflow завдання в Jira. У наших процесах розробки є кілька варіацій workflow залежно від проекту, типу завдання та опцій, вибраних у конкретній задачі. 

Розглянемо workflow, який використовуємо найчастіше:

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

На схемі шестерня вказує, що transition викликається Integro автоматично, тоді як фігурка людини означає, що transition викликається вручну людиною. Розглянемо кілька шляхів, якими завдання може пройти у цьому workflow.

Повністю ручне тестування на DEV+BETA без канаркових тестів (зазвичай так випускаємо моноліт):

Від скриптів до власної платформи: як ми автоматизували розробку в ЦІАН

Можуть бути інші комбінації transition. Іноді шлях, яким піде завдання, можна вибрати через опції в Jira.

Рух завдання

Розглянемо основні кроки, які виконуються під час руху завдання з workflow «Тестування на DEV + канаркові тести»:

1. Розробник чи PM створює завдання.

2. Розробник бере завдання працювати. Після завершення переводить її у статус IN REVIEW.

3. Jira відправляє Webhook у бік Jira-мікросервісу (відповідає за інтеграцію з Jira).

4. Jira-мікросервіс відправляє запит до Flow-сервісу (відповідає за внутрішні workflow, в яких виконується робота) для запуску workflow.

5. Усередині Flow-сервісу:

  • Призначаються ревьюєри для завдання (Users-мікросервіс, який знає все про користувачів + Jira-мікросервіс).
  • Через Source-мікросервіс (знає про репозиторії та гілки, але не працює з самим кодом) здійснюється пошук репозиторіїв, в яких є гілка нашого завдання (для спрощення пошуку ім'я гілки збігається з номером завдання в Jira). Найчастіше завдання має лише одну гілку в одному репозиторії, це спрощує управління чергою на деплой та зменшує зв'язність між репозиторіями.
  • Для кожної знайденої гілки виконується така послідовність дій:

    i) Підлив master-гілки (Git-мікросервіс для роботи з кодом).
    ii) Гілка блокується від змін розробником (Bitbucket-мікросервіс).
    iii) Створюється Pull Request на цю гілку (Bitbucket-Мікросервіс).
    iv) Надсилається повідомлення про новий Pull Request у чати розробників (Notify-мікросервіс для роботи з оповіщеннями).
    v) Запускається складання, тестування та деплой завдання на DEV (Jenkins-мікросервіс для роботи з Jenkins).
    vi) Якщо всі попередні пункти завершилися успішно, Integro ставить свій Approve у Pull Request (Bitbucket-мікросервіс).

  • Integro очікує Approve у Pull Request від призначених ревьюєрів.
  • Як тільки отримано всі необхідні Approve (в т. ч. позитивно пройдено автоматизовані тести), Integro перекладає завдання статус Test on Dev (Jira-мікросервіс).

6. Тестувальники проводять тестування завдання. Якщо проблем немає, то переводять завдання статус Ready For Build.

7. Integro «бачить», що завдання готове до релізу, і запускає її деплою в режимі канарки (Jenkins-мікросервіс). Готовність до релізу визначається набором правил. Наприклад, завдання у потрібному статусі, немає блокувань інші завдання, зараз немає активних викладок цього мікросервісу тощо.

8. Завдання переводиться у статус Canary (Jira-мікросервіс).

9. Jenkins запускає через Nomad деплой завдання в режимі канарки (зазвичай 1-3 інстанси) і повідомляє про викладення сервіс моніторингу релізів (DeployWatch-мікросервіс).

10. DeployWatch-мікросервіс збирає фон помилок та реагує на нього, якщо потрібно. При перевищенні фону помилок (норма фону розраховується автоматично) здійснюється повідомлення розробників через Notify-Мікросервіс. Якщо через 5 хвилин розробник не відреагував (натиснув Revert або Stay), запускається автоматичний відкат канаркових інстансів. Якщо фон не перевищений, то розробник повинен вручну запустити деплою завдання на Production (натисканням кнопки UI). Якщо протягом 60 хвилин розробник не запустив деплою в Production, то канаркові інстанси також з метою безпеки буде відкачено.

11. Після запуску деплою на Production:

  • Завдання переводиться у статус Production (Jira-мікросервіс).
  • Jenkins-мікросервіс запускає процес деплою та повідомляє про викладку DeployWatch-мікросервіс.
  • DeployWatch-мікросервіс перевіряє, що на Production оновилися всі контейнери (були випадки, коли не все оновлювалося).
  • Через Notify-мікросервіс надсилається сповіщення про результати деплою в Production.

12. У розробників буде 30 хвилин на запуск відкату завдання з Production у разі виявлення некоректної поведінки мікросервісу. Після цього часу завдання буде автоматично влито в master (Git-мікросервіс).

13. Після успішного merge-а в master статус завдання буде змінено на Closed (Jira-мікросервіс).

Схема не претендує на повну деталізацію (насправді кроків ще більше), але дозволяє оцінити ступінь інтеграції в процеси. Ми не вважаємо цю схему ідеальною та покращуємо процеси автоматичного супроводу релізів та деплою.

Що далі

У нас великі плани розвитку автоматизації, наприклад, відмова від ручних операцій при релізах моноліту, поліпшення моніторингу при автоматичному деплої, поліпшення взаємодії з розробниками.

Але на цьому місці поки що зупинимося. Багато тем в огляді автоматизації ми охопили поверхнево, деякі не торкнулися взагалі, тому ми з радістю відповімо на запитання. Чекаємо на пропозиції, що висвітлити детально, пишіть у коментарях.

Джерело: habr.com

Додати коментар або відгук