golang

Как PaaS решил проблемы стандартизации разработки сервиса одной утилитой

  • суббота, 14 октября 2023 г. в 00:00:17
https://habr.com/ru/companies/sbermarket/articles/766928/

Привет 👋 На связи команда PaaS СберМаркета. Меня зовут Роман, и уже больше 2-х лет моя группа занимается разработкой инструментов для разработчиков, в том числе утилитой sbm-cli, о которой хочу рассказать сегодня.

Вероятно вы каждый день пользуетесь консольными утилитами, такими как git, homebrew, ssh, grep, find, etc. Мы сделали command line interface приложение, запуск которого на локальной машине разработчика может превышать количество запусков команды git (спойлер: статистика в конце статьи).

О том как так получилось, почему этот инструмент так важен для разработчика и что он умеет, а что нет, расскажем в этой статье.

Обо всем по порядку:

Предпосылки

Это третья статья из цикла о разработке IT-платформы. В предыдущих сериях:

Классическая проблема, с которой вы можете столкнуться во время разработки — это использование разных версий инструментов для обслуживания ваших сервисов.

В примере ниже это cli-приложение oapi-codegen — утилита для генерации Go-кода на основе openapi спецификации. Каждый разработчик ставит себе @latest и скоро версии oapi-codegen расходятся даже у членов одной команды.

git diff
git diff

Вероятно, вы сталкивались с тем, что в соседних проектах для <чего-либо> (http client'а, авторизации, генерации кода, ORM и т.д.) используются разные библиотеки. На первый взгляд это не является проблемой, но со временем точно ей становится — когда приходится тратить много время на получение экспертизы N технических компонентов, реализующих смежные технологии.

Еще одна типичная боль для разработчика — выбор инструментов для локального запуска сервиса и его зависимостей:

  • кто-то запускает приложение и зависимости нативно (go run, python *.py runserver, systemctl start postgresql, etc);

  • кому-то ближе история с docker-compose;

  • есть любители minikube. 

Список подобных разногласий может быть достаточно объемным. Чтобы минимизировать количество таких болей мы и сделали sbm-cli.

В погоне за скоростью поставки ценности

Давайте представим крутую команду инженеров (скорее всего вы в такой работаете), которая разрабатывает и обслуживает N микросервисов.

Перед вами встаёт задача написать новый сервис, например, промокодов.

  1. Он будет ходить в сервис заказов.

  2. Забирать некоторую информацию.

  3. Агрегировать ее и отправлять в кафку.

Спецификации API в сервисе заказов не оказалось или она просто устарела. Поэтому разработчик курит изучает код, чтобы получить возможные варианты входных и выходных параметров API. В итоге, для решения задачи он потратил на 2 дня больше, чем планировал.

Может показаться, что это не так много. Но представьте, что подобная проблема встречается в каждом сервисе, и каждая новая интеграция требует еще больше времени. Ситуация усугубляется, когда с подобной проблемой сталкивается не только один инженер, но и вся команда.

При создании sbm-cli у нас была одна основная цель: ускорить процесс доставки ценности. И мы руководствовались простым принципом: если что-то можно автоматизировать, то это нужно сделать.

Что умеет sbm-cli?

На сегодняшний день sbm-cli содержит команды из шести доменных областей. Далее подробнее поговорим о каждой. 

А вот и все 27 команд:
  • Управление шаблоном сервиса

    • sbm-cli service create

    • sbm-cli service upgrade

  • Локальная среда разработки (Playground)

    • sbm-cli service up

    • sbm-cli service down

    • sbm-cli service debug

    • sbm-cli service reset

    • sbm-cli service purge

    • sbm-cli service status

    • sbm-cli service env

  • Работа с API

    • sbm-cli dependency add

    • sbm-cli dependency upgrade

    • sbm-cli contract list

    • sbm-cli codegen

  • Работа с БД

    • sbm-cli database create

    • sbm-cli migration create

    • sbm-cli migration apply

    • sbm-cli migration rollback

  • Валидация

    • sbm-cli code lint

    • sbm-cli contract verify

    • sbm-cli dependency check

    • sbm-cli manifest check

    • sbm-cli inframanifest check

    • sbm-cli setup system-check

  • Системные команды

    • sbm-cli setup self-upgrade

    • sbm-cli setup autoupgrade

    • sbm-cli setup token-set

    • sbm-cli version

sbm-cli help
sbm-cli help

Для каждой команды соблюдаются следующие правила интерфейса:

  1. Help: Понятная помощь на старте с примером запуска команды.

  2. Next steps: Рекомендации по выполнению дальнейших действий после успешного завершения команды.

  3. What to do: Ответ на вопрос «Что надо сделать, что бы команда успешно прошла?»

  4. Exit codes: 0 — успех, 1 — ошибка, 3 — предупреждение.

Управление шаблоном сервиса

Экосистема PaaS построена вокруг главной сущности — сервиса. Для обеспечения корректной работы платформы мы приняли ряд конвенций. Поэтому наше типичное приложение имеет следующую структуру каталогов в сервисе, вне зависимости от выбранного engine (языка или фреймворка):

Минимальный набор директорий в PaaS сервисе
Минимальный набор директорий в PaaS сервисе

Engine — основная технология на которой построен сервис. В СберМаркете это Golang, Rails, Python и NodeJS.

Для обеспечения такого соглашения мы сделали свой механизм создания сервиса. Именно с него началась разработка sbm-cli. Мы вдохновились лучшими практиками шаблонизации сервисов:

И объединили их с конвенциями принятыми в платформе.

Пример создания сервиса:

sbm-cli service create https://git.sbermarket.tech/paas/test-python-service --engine=python

В статье Как подступиться к созданию PaaS мой коллега подчеркивал важность стандартного набора библиотек для разработки сервисов. Поэтому для каждого engine была разработана своя God-библиотека <engine>-libs (go-libs, ruby-libs, etc), которая предоставляет API для использования библиотеки следующего уровня. 

Например, вы хотите использовать ruby http client faraday. ruby-libs предоставляет инстанс faraday с базовой конфигурацией, миддлварами, единым форматом логов и метрик, которые общеприняты в платформе.

С течением времени наши базовые шаблоны менялись, и мы решили добавить функцию обновления в sbm-cli с командой sbm-cli service upgrade, чтобы упростить процесс перехода на новые версии библиотек.

Локальная среда разработки (Playground)

Docker-compose является достаточно эффективным инструментом для локального запуска приложения и его зависимостей. Но писать новый docker-compose.yaml каждый раз для нового сервиса — весьма утомительно. Наша команда поставила себе цель, сделать так, чтобы для локального старта любого сервиса разработчик выполнил как можно меньше действий. Так появилась команда sbm-cli service up.

Пример вызова команды sbm-cli service up
Пример вызова команды sbm-cli service up

Playground — это docker-compose-based компонент sbm-cli, который управляет жизненным циклом сервиса на локальной машине разработчика.

sbm-cli service up разворачивает приложение и все его инфраструктурные зависимости. Список зависимостей определяется в configs/values.yaml и используется как при деплое приложения, так и при локальном развертывании.

Пример values.yaml
Пример values.yaml

сonfigs/values.yaml — манифест инфраструктуры, в котором декларируется поведение сервиса и его зависимостей в staging, production и playground среде.

При необходимости, разработчик может указать точную версию образа, который необходим для развертывания зависимости. В противном случае будет выбрана версия по умолчанию.

Playground предоставляет возможность запустить docker-контейнеры только с инфраструктурными зависимостями. При этом запуск самого приложения может оставаться на локальной машине. 

Остальные команды playground:

  • sbm-cli service debug — запускает приложение в режиме debug (например с использованием delve для golang).

  • sbm-cli service down — останавливает приложение.

  • sbm-cli service status — отображает состояние приложения.

  • sbm-cli service reset — down & up.

  • sbm-cli service purge — полный сброс (останавливает приложение и удаляет все образы, контейнеры, вольюмы, сети).

  • sbm-cli service env — вызов команды в контексте playground (например sbm-cli service env -- go test -v ./… запустит тесты в окружении env переменных для подключения к ресурсам, развернутым в playground).

Работа с API

Наша tech-команда выбирает API-first подход при разработке. Подробнее можно прочитать в статье: Трудности перевода. Как научить микросервисы общаться и не ссориться.

Этот метод предоставляет ряд ключевых преимуществ:

  • улучшение качества проектирования приложения;

  • всегда актуальные контракты для разработки;

  • разработчики могут одновременно работать над клиентской и серверной частями приложения.

Как это работает на практике? Разработчик сервиса-продьюсера формирует контракт в директории api. Затем он может применить инструмент кодогенерации sbm-cli codegen –servers для автоматической генерации серверного кода на основе этого контракта.

Пример вызова кодогенерации серверной части
Пример вызова кодогенерации серверной части

Мы используем openapi и proto спецификации. Кодогенерация доступна для каждого вида контракта.

Разработчик сервиса-консьюмера использует команду sbm-cli dependency add перед реализацией интеграции. Она выполняет несколько функций:

  1. Декларирует интеграцию в манифесте приложения app.toml.

  2. Скачивает контракты сервиса в директорию deps/services/<service-name>/<contract-name>.

  3. Генерирует клиентский код (как команда sbm-cli codegen –clients).

  4. В манифесте инфраструктуры values.yaml прописывает сетевой доступ для межсервисного взаимодействия в kubernetes.

Одна команда закрывает большую часть вопросов и разработчик может сфокусироваться на написании кода.

Пример вызова команды sbm-cli dependency add
Пример вызова команды sbm-cli dependency add

Что делать если в контракте добавилось новое поле и необходимо его поддержать со стороны консьюмера? Для этого есть команда sbm-cli dependency upgrade, которая актуализирует контракт и клиентский код.

Для просмотра всех контрактов сервиса используется команда sbm-cli contract list.

Работа с БД

Что необходимо сделать, чтобы создать БД postgres локально? Базовый сценарий такой:

  1. Подготовить, если еще нет, dsn для будущего подключения приложения к БД.

  2. (Опционально) Подключиться к хосту, на котором располагается сервер БД (например docker-контейнер).

  3. Подключиться к psql.

  4. CREATE DATABASE …

Для разработчика, который использует платформу это одна команда sbm-cli database create.

При проектировании PaaS нам необходимо было выбрать один инструмент управления миграциями, чтобы он использовался как при локальной разработке так и в среде kubernetes. Команды sbm-cli migration up и sbm-cli migration rollback применяют или откатывают миграцию с использованием goose, а также сохраняют дамп structure.sql.

Валидация

sbm-cli позволяет снизить человеческий фактор при разработке вашего приложения в части изменения контрактов. Вероятно вы встречали случаи, когда в response body удалили одно поле, после чего пострадали сервисы-консумеры. sbm-cli contract verify проверяет контракты на обратную совместимость с master веткой, а также на соответствие REST API стандарту (только для openapi).

Пример вызова sbm-cli contract verify
Пример вызова sbm-cli contract verify

В платформе мы выработали ряд базовых линтов golang кода, которые используются в большинстве проектов и общеприняты go-сообществом. Такие линты проверяются командой sbm-cli code lint. Линты, которые не являются базовыми, могут быть настроены в каждом проекте по решению команды. 

Пример вызова sbm-cli code lint
Пример вызова sbm-cli code lint

Системные проверки в sbm-cli:

  • sbm-cli dependency check — проверяет корректность декларированных зависимостей.

  • sbm-cli manifest check — проверяет корректность заполнения  configs/app.toml.

  • sbm-cli inframanifest check — проверяет корректность заполнения  configs/values.yaml.

  • sbm-cli setup system-check — проверяет наличие дистрибутива engine, docker и других зависимостей на хосте.

Большая часть команд из этой категории используется в CI/CD пайплайнах, тем самым обеспечивается гарантия вызова этих валидаций.

Планы на будущее

Наша команда планирует дальнейшее развитие sbm-cli. Вот основные фичи, которые запланированы на будущий год:

  • Поддержка ruby проектов в Playground (пока доступно только для go и python).

  • Локальный запуск backend и frontend приложений одновременно в Playground среде.

  • Использование локального docker-контейнера в удаленной среде kubernetes.

Уровень зрелости инструмента достигает своего максимума, поэтому еще одно из направлений развития sbm-cli, которые рассматривает наша команда, — open source.

Итоги

Вероятно, не все проблемы необходимо решать здесь и сейчас. Но с ростом инженерной команды компании исправление их задним числом может оказаться достаточно дорогими.

После двух с половиной лет разработки sbm-cli стал повседневным инструментом инженера в СберМаркете. Количество ежедневных вызовов sbm-cli может превышать 2400, а количество уникальных пользователей ежедневно более 210 — это около 30% нашей tech команды.

Количество вызовов sbm-cli с 05.10 по 11.10 включительно
Количество вызовов sbm-cli с 05.10 по 11.10 включительно
Количество уникальных пользователей sbm-cli с 05.10 по 11.10 включительно
Количество уникальных пользователей sbm-cli с 05.10 по 11.10 включительно

Я надеюсь эта статья помогла вам взглянуть на стандартизацию и автоматизацию процессов разработки с новой стороны.

Пишите в комментариях какая часть sbm-cli вам наиболее интересна и мы постараемся раскрыть технические детали этого компонента в одной из следующих статей цикла.

Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на  YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.