Автоматизуємо процеси, щоб масштабуватися. Кейс Shopmonkey

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привіт! Мене звати Ігор Соломаха, я Lead Core Back-end Team в продукті Shopmonkey, що займається розробкою софту для управління та впорядкування роботи автомайстерень та тюнінг ательє США та Канади.

Всього в компанії понад 180 фахівців, 50 з них — у київському офісі. Проте ще рік тому моїх колег у столиці було близько 10, а до кінця 2021 їх має бути вже 60. І як часто буває, процеси не ростуть так швидко, як ми. Тому треба змінювати підхід у роботі, щоб підтримувати ефективність. Ми почали автоматизовувати процеси у CI/CD Pipeline паралельно зі збільшенням кількості фахівців у командах. Як це було, навіщо впроваджували, що використовували та скільки етапів пройшли, поділюся у цій статті.

Матеріал буде корисним тімлідам, розробникам та девопсам, які також шукають можливості для автоматизації та гайди, як це зробити. При цьому такий підхід можна застосовувати як для компаній до 100 людей, так і для великих команд у межах корпорацій.

Потенціал для автоматизації: позбуваємося рутинних процесів

Процеси розробки часто вимагають багато часу і дуже рутинні. Вони охоплюють делівері різних фіч чи додатку від самої ідеї до запуску на пристрої кінцевого користувача. Проте якщо ідеї беруть на себе продакт-менеджери та сейлз-менеджери, то ми їх оцінюємо та розробляємо. І вже на цьому етапі з’являється великий потенціал для автоматизації, наприклад, процесів заливки коду тощо. Це допомагає зберегти час та зусилля.

Основна ідея автоматизації — неперервна інтеграція (Continuous Integration), яка об’єднує процеси тестування, збірки та додаткових перевірок, а також неперервна доставка (Continuous Delivery). Це те, що відомо всім як CI/CD Pipeline.

Що під капотом автоматизації

У CI/CD Pipeline розробка більшості сучасного ПЗ передбачає ітеративність:

> Ідея -> Написання коду -> Тестування -> Реліз

Цей процес включає значну кількість етапів, які потрібно повторювати при проходженні кожного циклу. Автоматизувати цей процес — одне з ключових завдань, коли проєкт розширюється. Це може бути пов’язано як зі збільшенням команди розробників, або ж з такими технічними особливостями, як зростання кількості сервісів у проєкті чи необхідність у паралельній розробці.

Тригер процесів. Розбираємося в етапах автоматизації

Будь-які зміни до нашої інфраструктури супроводжуються змінами коду. Тобто потенційний розробник бере останню версію програми, source code, змінює щось, комiтить, заливає свій код на GitHub. Для нас це основний тригер для всіх автоматизаційних процесів.

Етап І. Пишемо код

По суті, це етап генерації source code, який тісно пов’язаний з контролем версій, і тут на допомогу приходить Git. Ми в Shopmonkey використовуємо GitHub для менеджменту наших репозиторіїв, у нас їх налічується близько 50. У багатьох з них розробка ведеться паралельно і незалежно, тобто різними командами.

Налаштовуючи свій CI/CD Pipeline, ми встановили контракт для розробників при написанні коду та заливанні його в GitHub:

1. Кожна створена гілка (Git) повинна мати в назві номер JIRA тікету. Так легко можна зрозуміти, до якого завдання відносяться зміни в цій гілці. Ця інформація також використовується в наступних етапах нашого пайплайну (версіонування, деплой), оскільки кожна наша гілка (тікет) розгортається як окрема копія програми, в окремому оточенні. Це дозволяє вести дійсно паралельну розробку безлічі фіч водночас і дати розробникам свободу та незалежність одне від одного.

2. Кожен коміт повинен відповідати стандарту Conventional Commits. Це полегшує створення Changelog, а також дозволяє автоматично визначати нову версію програми (про версіонування детальніше трохи згодом)

Етап ІІ. З source code в артефакт

Етап збірки програми включає всі маніпуляції для перетворення source code в артефакт:

  • Лінтинг та статичний аналіз коду — перевіряємо код на відповідність нормам та внутрішнім правилам команди.
  • Збірка (компіляція/контейнеризація) — тут з вихідного коду формується єдиний файл, якому буде призначена версія (процес версіонування). Після цього він потрапить до реєстру артефактів, тобто зарелізиться.
  • Тестування — залежно від розміру програми, тут можуть запускатися як юніт, так і в деяких випадках інтеграційні тести.

Тут у справу вступає GitHub Actions як наш основний CI інструмент для автоматизації.

Наведу приклад коду для автоматизації цього етапу засобами GH Actions:

name: Release

on:
  - push

jobs:
  build_test_lint:
    name: Build & Test & Lint
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/[email protected]

      - name: NPM Install
        run: npm ci

      - name: Lint files
        run: npm run lint

      - name: Build files
        run: npm run build

      - name: Run tests
        run: npm run test

      - name: Run Helm Template
        run: helm template ./helm --debug

      - name: Build Docker Image
        run: |
          docker build -t image

Етап III. SemVer для всіх артефактів: отримуємо валідну версію

На цьому етапі артефакту призначається версія. Ми у Shopmonkey використовуємо SemVer для всіх типів наших артефактів. У цій справі нам допомагає Semantic Release та Conventional Commits. Вони дозволяють у кілька рядків коду вирішити задачу версіонування та отримати на виході актуальну та валідну версію.

Як ви пам’ятаєте, кожна наша git-гілка розгортається як окрема копія програми і має у своєму імені номер JIRA тікету. Цей номер використовується як пререліз токен при формуванні версії, а також як сабдомен при розгортанні програми.

Наприклад:

Jira Ticket — WEB-1288
Git Branch — WEB-1288/fix-something
Version — 1.1.12-WEB-1288.1 ... 1.1.12-WEB-1288.n
Domain — WEB-1288.domain.com

Приклад реалізації такого кроку в GH Actions:

- name: Semantic Release
  id: release
  uses: sujimoshi/[email protected]

Етап IV. Випускаємо реліз у світ

Тут ми завантажуємо наш артефакт у відповідний реєстр. У нас це:

Docker Image → Amazon ECR
Helm Chart → ChartMuseum
NPM Package → GitHub Packages

Приклад пайплайну:

- name: Publish Docker Image
  run: |
    TAG=ecr.aws.com/$(basename ${{ github.repository }}):${{ steps.version.outputs.version }}
    docker push image -t $TAG
- name: Publish Helm Chart
  run: |
    helm plugin install <a href="https://github.com/chartmuseum/helm-push.git">https://github.com/chartmuseum/helm-push.git</a>
    helm repo add shopmonkey chartmuseum.shopmonkey.io --username ${{ secrets.HELM_USER }} --password ${{ secrets.HELM_PASSWORD }}
    helm package ./helm --version "${{ staps.version.outputs.version }}" --app-version "${{ steps.version.outputs.version }}"
    helm push "$(basename ${{ github.repository }})-${{ needs.version.outputs.version }}.tgz" shopmonkey

Етап V. Деплой: стежимо за правильністю роботи

Відбувається розгортання програми та в справу вступає ArgoCD. Працює це так:

ArgoCD слідкує за змінами в одному з наших git-репозиторіїв (argo) і чекає, коли там з’явиться або зміниться Helm Chart (конфігурація) з описом всіх залежностей та їхніх версій (те, що було сформовано на етапі релізу), що необхідні у цьому оточенні. У цьому репозиторії також живе базовий шаблон такого чарту з дефолтними налаштуваннями та версіями, необхідними для запуску базової версії нашої програми.

Тут також зберігається Triggerable GH Action, який можна запустити з використанням звичайного curl. Йому можна передати назви та версії залежностей, які ви хочете перевизначити у шаблоні, після чого невеликий скрипт змержить базовий шаблон (chart.yml) з переданими вами версіями, створить новий Helm Chart саме під ваші потреби в папці з ім’ям того самого JIRA тікету. Його підхопить ArgoCD і самостійно задеплоїть у Kubernetes.

За всім ви зможете спостерігати з веб-консолі ArgoCD у приємному графічному інтерфейсі, переглядати логи при необхідності та стежити за правильністю роботи вашої версії програми.

Етап VI. 7 днів на редеплой, або помри. Переходимо до тестування

Після повноцінного розгортання програми або її копії у нас відбувається запуск E2E тестів. Ми використовуємо Cypress, тому просто запускаємо наші тести в GH Actions через Triggerable Pipeline (виклик curl з k8s джоби). А також автоматично прикріплюємо посилання на розгорнутий додаток до JIRA тікету, щоб мануальні тестувальники швидко могли приступити до тестування.

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

Загалом весь пайплайн займає від 3 до 7 хвилин у середньому. А кількість розгорнутих копій програми безпосередньо залежить від кількості розробників та завдань на борді. Загалом у нас одночасно запущено близько 80 копій. Додатки, які не переживають редеплой протягом семи днів, автоматично вмирають, однак у будь-який момент можуть бути перезапущені заново.

Автоматизація — це не так легко, але вона того варта. Що змінилося для клієнтів та розробників

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

У результаті зараз у нас в компанії 13 команд. Кожна може вільно розгортати додаток на окремому домені, а будь-який тестувальник чи продакт може зайти і подивитися, що змінилося і перевірити, чи все працює правильно. Це можна робити також для презентації окремому сегменту користувачів.

Масштабування та мікросервісна архітектура: підсумовуємо результати

При цьому підхід безперервної доставки важливий для будь-якої команди. Якщо пишеш під Android, будуть свої нюанси, наприклад, відправляєш все в Play Market, а ми — на сервер. Проте суть одна. Ми як команда Back-end Core намагалися задати загальний вектор саме нашої мікросервісної архітектури. Адже зараз ми в процесі переходу від монолітної архітектури, яка у нас була в минулому, до незалежної інтеграції та доставки мікросервісної архітектури.

Наразі у нас понад 80 feature environments, це досить непогана цифра. Проте до порівняння, рік тому ми мали всього 1. Загалом за цей час ми суттєво масштабувалися, покращили процеси та пришвидшили вихід у продакшн. Крім цього, помітили, що з’явився контроль та змінилася швидкість процесів. Так що ми дуже задоволені результатом, активно готуємося до подальшого масштабування та впроваджуємо нові архітектурні рішення.

👍ПодобаєтьсяСподобалось15
До обраногоВ обраному7
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Супер статья, спасибо!

Деплой у продакшн теж відбувається автоматично після коміту в master, чи потрібно підтвердження людини?

Деплой в продакшн потребує коммiту, коммiт потребує пул реквесту, а там потрiбен апрув

Цікаво, а скільки ресурсів використовує один feature env та скільки «заліза» загалом потрібно, щоб обслуговувати цей тестовий cloud?

Feature env сам по себе недорогой. В дев кластере используются aws spot instances, они значительно дешевле чем настоящие ноды. Основные расходы связаны с persistant нодами, это те ноды где хостятся базы данных. Однако здесь мы прибегаем к поднятию единой базы данных (mongo, elastic) для всех фича енвов и использованию логических неймспейсов. Для mongo это логические бд, а для еластика префиксы в названиях индексов. Очень надеемся что в скором будущем неймспейсы также завезут в кафку, потому как в данный момент каждый фича енв поднимает свой инстанс кафки

Плюс ко всему мы пришли к тому что все фича енвайроменты отключаются ежедневно, и внедрили простой способ в один клик из Jira тикета вернуть енв к жизни через Jira Automation

Рекомендую звернути увагу на AWS Fargate (AWS Fargate is a serverless, pay-as-you-go compute engine compatible with EKS) що може суттєво зменшити витрати на вашу інфраструктуру.

А в Kafka можна використовувати 1 сервер + префікси для топіків кожного енва.

Підписатись на коментарі