Привет! Меня зовут Андрей, я Head of Platform в финансовом маркетплейсе Банки.ру. Со мной Борис @qbudhaЗырянов, go/php разработчик. Сегодня расскажем о том, как мы разработали и внедрили процесс создания сервисов одной кнопкой.
В Банки.ру много команд, которые регулярно создают новые сервисы. До недавнего времени этот процесс регламентирован не был. Часто случалось, что о новом сервисе узнавали, когда уже случился инцидент на продакшене. Было непонятно, как с ним работать, поддерживать, мониторить, какие смежные сервисы от него зависят и зачем вообще его создали. Мы решили этот процесс максимально оптимизировать и в результате пришли к созданию сервиса одной кнопкой.
В материале расскажем:
как работал старый подход и с чем были сложности;
какая схема пришла на замену и как все работает;
что планируем делать дальше.
Конечно, сначала о проблемах, которые хотели решить.
Проблемы старого подхода
Раньше сервисы создавались хаотично, единого процесса не было. Вот несколько самых распространенных вариантов:
Команда самостоятельно принимала решение разрабатывать новый сервис, создавала репозиторий, наполняла его кодом. Часто просто клонировали старый сервис, который давно требовал обновлений. В зависимости от экспертизы, некоторые команды сами готовили плейбуки и манифесты для деплоя, а девопсы лишь ревьюили, мержили и иногда ругались. Другие же не хотели тратить время и всю работу поручали девопсам. Случалось, что до первого падения о таком сервисе никто, кроме команды, не знал.
PM ставил задачу на devops установить неведомый никому инструмент (админка, сервис пушей и так далее). Девопсы исполняли. Сервис оказывался в продакшене, и уже после этого команды эксплуатации или платформы узнавали о его существовании. При этом сами девопсы поддерживать и мониторить такой сервис не умели, не понимали, что там происходит, и не могли починить среди ночи, если срабатывали алерты.
В обоих случая часто команда, а иногда и стейкхолдер не понимали, для чего создают тот или иной сервис и каковы перспективы его дальнейшего развития.
Уверен, всем очевидно, что такой процесс имел массу минусов:
Сервис создавался долго. Часто ставили задачу-«тост», которую исполнитель уточнял, согласовывал и переделывал неделями с учетом коммуникаций и передачи контекста новым людям.
Не всегда использовались актуальные шаблоны манифестов и бойлерплейтов: команды делали, как умели.
Отсутствовали архитектурное ревью и дискуссия по адекватности используемых технологий и решений.
Без подробного контекста использования девопсы часто просто копировали старые манифесты, при этом был большой риск где-то ошибиться — банальная опечатка могла на некоторое время сломать все пайплайны.
Некоторые задачи дублировались. Например, логику сокращения ссылок мы написали четыре раза.
Сервисы-сироты находились с завидной регулярностью.
Понятного приоритета в задачах не было, все зависело от авторитета стейкхолдера.
Иногда приходилось блокировать задачи, в которые уже были инвестированы недели, а то и месяцы работы команд.
В результате мы теряли в скорости изменений, не могли их толком контролировать, тратили лишние деньги, плодили зоопарк технологий и регулярно изобретали велосипеды.
Стало ясно, что так больше жить нельзя, поэтому мы придумали и запустили новый процесс.
Брейншторм
Идеи для процесса генерировали на стратегической сессии, накидав все хоть сколько-нибудь подходящие решения. Потом отобрали самые разумные и прикинули варианты реализации. Сначала думали сделать отдельный сервис чисто для этой функции, обсуждали gitops подход, имплементацию в Big picture и вынос в специализированные ci/cd пайплайны.
Конечно, посмотрели и на индустриальный опыт: многие ИТ-гиганты в рамках концепции «платформа как сервис» предоставляют интерфейс для создания новых ресурсов.
Если кратко, то бывает так:
Интеграция процесса в общую панель управления частным облаком.
Создание утилит командной строки.
Пайплайны в оркестраторах.
Микросервисы, управляющие другими сервисами.
Мы определили критически важное качество нового подхода — «единая точка контроля создания новых сервисов». Главное, чтобы нельзя было создать новый сервис без того, чтобы о нем не знали определенные сотрудники. Список сотрудников (ролей), которые должны давать аппрувы, составили на той же сессии.
Взвесив все варианты, в итоге остановились на процессе, близком к выдаче доступов: флоу согласования разными людьми. Такой процесс не требовал смены привычного бизнес-процесса и позволял внедрению пройти мягче.
Новый подход
Наша цель была в том, чтобы минимизировать взаимодействие между подразделениями и максимально упростить пайплайн. На наш взгляд, получилось неплохо.
Сейчас создание сервиса состоит из трех шагов.
1. Подготовка заявки в Jira
Команда, которой нужен новый сервис, создает заявку в Jira. В ней указывает тип, размещение и назначение сервиса. Линкует ссылку на архитектурное решение или стратегию продукта. Там должна быть информация о роли будущего сервиса в технологическом ландшафте.
2. Согласование
В зависимости от типа сервиса, заявка уходит по списку ревьюеров, которые отвечают за свою долю технологического пирога и проверяют архитектуру решения, оценивают сервис с точки зрения системы в целом. Если все хорошо — задача идет дальше, в цепкие руки роботов. Этот этап мы посчитали ключевым с точки зрения интерфейса, поэтому и решили «приземлить» его именно в Jira — основной менеджерский инструмент.
3. Создание сервиса
Для этой части мы написали сервис-обработчик. Если немного углубиться, его работа выглядит следующим образом:
Создается гит-репозиторий.
Init-commit на основе шаблонного кода.
Коммиты в репозитории с манифестами, чтобы появились build-планы и deploy-планы.
Правки в секреты, конфиги и всё остальное для того, чтобы задеплоить сервис.
После первого билда можно деплоить сервис в любое окружение.
Общая схема
Общая схема сейчас выглядит так:
В центре находится робот api-service-creator, который делает всю рутинную работу (детальнее опишем чуть ниже).
Бот интегрирован с Jira и Bitbucket. Мы сделали его под различные клиенты: веб-клиенты, консоль, хуки.
Интерфейс бота представляет собой rest-api: простой для понимания, легко расширяется.
Честно сказать, сначала команды восприняли инициативу с большим недоверием. Но буквально после пилота осознали, насколько стало проще и быстрее.
Первый сервис, который мы создали, используя новый подход — сервис для команды SEO. На согласование ушло около трех часов, а на создание — пять минут, вместо недели, которая бы ждала команду при работе по старой схеме. И, как видим по обратной связи, процесс прошел успешно.
Внутреннее устройство service-creator
Абстракции, которые используются в service-creator, повторяют любую CI-систему. Есть стейджи, внутри них команды (джобы), а внутри команд конкретные операции (таски).
При этом уже на этой схеме мы решили не ставить триггеров на запуск билдов и деплоев, чтобы с ходу не портить процессы команд. Ну, и мы всё равно генерируем пайплайны, доступные командам для запуска, так что они сами могут запустить деплой тогда, когда посчитают необходимым. А первый билд происходит автоматически после того, как Bamboo перечитывает Java-спеки.
В итоге пришли к тому, что вся эта история помещается в еще одну сущность, которая у нас называется план. Появляется некая матрешка из абстракций.
Стейджи выполняются последовательно, команды внутри них — параллельно, а операции внутри команд снова выполняются последовательно, потому что состоят из моментов, которые никак не распараллелишь.
Здесь мы решили пойти дальше и попробовать сделать так, чтобы планы запускались параллельно. За счет этого хотели уменьшить вероятность коллизий, если одновременно согласовались сразу два сервиса. Так родилась штука, которая называется Piper. Это программный компонент, который отыгрывает запуск планов. Планы запускают стейджи, а стейджи, в свою очередь, запускают команды.
В итоге получается вот такая матрешка.
На что это похоже? На Fun In, Fun Out.
С тем лишь исключением, что здесь мы запускаем какие-то рутины, но не агрегируем результат вычислений — у нас его нет. То есть это просто распараллеливание, Fun out.
Далее надо подробнее рассказать о том, что же такое в нашем случае «команда» (она же – «джоба»).
Команда
Решает конкретную задачу, является шагом, без которого не получится создать новый сервис. Команда получает на вход набор параметров и обрабатывает их.
Сейчас команда выполняется в процессе контейнера кода. Но потенциально это закладывалось так, чтобы команда могла стать самостоятельным докер-имиджем: запускаться и выполняться везде, где есть Docker и docker run.
Как у нас выполняются планы
В Jira создается новая задача. После того как она собирает все аппрувы от лидов (иногда после сессии вопросов-ответов в комментариях), Jira переводит её в другой статус и посылает webhook в api. Сам service-creator состоит из двух компонентов: Api & Piper.
Api, получив web-hook от Jira, формирует из него план для Piper и пишет его в базу данных.
Piper периодически запускается, вычитывает из базы данных невыполненные планы и начинает их выполнять.
После того как план выполнен, api об этом узнает с помощью крон-джобы.
Заявка, которая пришла к нам из Jira в виде webhook, получает в базе статус «Выполнено», а сервис через api двигает задачу в Jira в следующий статус — «Исполнено» и пишет комментарии.
Планы на будущее
В работе с service-creator планируем достичь следующих целей:
Избегать Vendor lock. Например, из слака мы уже переехали меньше чем за день.
Участвовать в каждом этапе жизненного цикла сервисов. Сейчас мы участвуем только в создании сервиса, а хотелось бы автоматизировать и передачу в сопровождение (эксплуатацию), и удаление.
Использовать разные типы сервисов, в том числе и nocode/lowcode.
Управлять параметрами сервиса в рантайме (лимиты, реквесты, количество реплик).
POV: создание нового сервиса
Шаг 0. Определяем с командой необходимость создания сервиса и готовим архитектурное решение по шаблону.
Шаг 1. Захожу в джиру, жму Create.
Шаг 2. Выбираю проект RFC.
Шаг 3. Краткий комментарий в summary, выбираю язык программирования, тип сервиса, подходящее имя, вставляю ссылку на архитектурное решение.
Шаг 4. Жму Create Task. Заявка создана со статусом «новая задача». Всем, кто ставит аппрувы, уже ушло сообщение в мессенджер.
Шаг 5. Когда задача соберет все аппрувы, Jira переведет ее в статус «утверждено», в бота улетит webhook, и в действие вступит service-creator, который и исполнит эту заявку.
Шаг 6. Когда все пуши в репозитории прошли, сервис переведёт задачу в финальный статус и напишет комментарий со ссылкой на репозиторий и билд-план.
Заметьте, не нужно самому писать ни строчки кода!
Заключение
Прошло уже почти сто задач, так что новый подход создания сервиса под кодовым названием «Одна кнопка» продемонстрировал свою эффективность.
Теперь для того, чтобы создать новый сервис, команды тратят не больше одного дня, им не приходится делать ручные изменения в коде. Мы значительно сократили время на создание сервиса и упростили процесс коммуникаций. Сбор апрувов обеспечил прозрачный процесс и предсказуемый результат как для команд, так и для менеджеров. А также мы сильно снизили вероятность ошибок и забыли про «сирот».
Сегодня мы можем успешно развернуть полноценные сервисы на PHP, Java, Golang и Node.js. Впереди еще много планов. Продолжаем совершенствовать наши методы и инструменты. И, конечно, планируем делиться ими здесь, на Хабре, следите за обновлениями!
Будем рады комментариям от тех, у кого был похожий опыт, и любым вопросам.