Привет! Снова на связи Петр Коробейников, техлид серисов Redis и RabbitMQ в #CloudMTS.
Сегодня мы поговорим о самой главной составляющей Developer Experience (DX) — локальном окружении разработчика. Вернее о том, как сделать так, чтобы оно разворачивалось быстро и автоматизированно, а еще было единообразным у всех участников команды.
Я поделюсь нашими подходами и укажу на некоторые особенности, которые могут быть специфичны для разработки облачных сервисов. В этой статье я не буду касаться вопросов реализации — у каждого она может быть своя. Но, думаю, сам подход будет полезен многим.
Зачем нужно типовое локальное окружение для разработчика — объяснять не нужно. Просто вспомните ваш технический онбординг. Чаще всего вместо него вам предлагается проштудировать некоторое количество недописанных статей в конфлюенсе, из которых мало что можно было понять.
А меж тем вам надо настроить свое новое рабочее место, узнать, какие инструменты вам понадобятся, где находятся нужные для работы репозитории, какой воркфлоу используется для решения задач.
Унифицированное локальное окружение, которое разворачивается автоматизированно, по одинаковым правилам, облегчит онбординг новичку. А еще принесет пару бонусов:
- Версии серверного программного обеспечения будут совпадать в локальном окружении и продакшене до патчей.
- Все необходимые инструменты имеют общий и единообразный интерфейс, скрывающий под собой сложности использования.
Локальное окружение в одной консольной утилите
Интерфейс к типовому локальному окружению разработчика мы упаковали в консольную утилиту и назвали её dxctl
— developer experience control
. Она написана на Go для облегчения распространения самого исполняемого файла. Но реализовать аналог вы можете на любом языке, который используется в вашей компании. Вот что она умеет:
dxctl sandbox up
— поднимает на машине разработчика всю серверную часть, необходимую для разработки сервисов локально;dxctl sandbox down
— тушит локальное окружение;dxctl sandbox status
— показывает, запущено ли локальное окружение в данный момент и что конкретно запущено;- вызов команды
dxctl
без аргументов выводит в консоль дашборд с доступными локально сервисами, вот пример:
+------------+---------------------------------------------------------------+
| СЕРВИС | АДРЕС |
+------------+---------------------------------------------------------------+
| Дашборд | http://localhost |
+------------+---------------------------------------------------------------+
| Postgres | N/A, подключение только через PgBouncer |
| | http://localhost/pgweb |
| PgBouncer | localhost:6432 |
+------------+---------------------------------------------------------------+
| Kafka | localhost:9092 |
| Kafdrop | http://localhost/kafdrop/ |
+------------+---------------------------------------------------------------+
| Grafana | http://localhost/grafana |
| Prometheus | http://localhost/prometheus |
| Jaeger | http://localhost/jaeger |
| | JAEGER_AGENT_ENDPOINT="localhost:6831" |
| | JAEGER_COLLECTOR_ENDPOINT="http://localhost:14268/api/traces" |
+------------+---------------------------------------------------------------+
Помимо консольной версии дашборда, есть еще и веб-версия, доступная в нашем случае по адресу http://localhost
. Я не буду прикладывать скриншот, но схематично покажу, как он выглядит:
Чуть детальнее расскажу, что является частью нашего локального окружения.
Базы данных
Мы используем в своей работе Postgres. Мы не ходим напрямую в Postgres даже локально, всё через PgBouncer
. А конфиг самого баунсера отличается от прода только уровнем логирования — больше ничем.
Для запуска миграций мы тоже используем dxctl
. Мы унифицировали мигратор под капотом и в принципе используем одни и те же инструменты в каждом нашем сервисе. Об этом подробнее я постараюсь рассказать в будущей заметке о реализации паттерна микросервисной архитектуры Service Template. Вернемся к миграциям:
dxctl postgres migrate
— применяет миграции к локальному окружению;dxctl postgres status
— выводит информацию о всех миграциях.
Брокеры сообщений
В прошлой своей статье я рассказывал про наши подходы к асинхронному общению между сервисами через шину событий. Пришло время описать, как же это видит со своей стороны разработчик. Начинается всё с .proto
-файла, в котором описывается сообщение и имя топика, куда это сообщение будет отправлено и откуда вычитано. А вот механизм кодогенерации скрыт под dxctl
:
dxctl generate eventbus
— эта команда генерирует продьюсеры и консьюмеры для всех сообщений, описанных в.proto
-файлах.
На этом возможности подкоманды generate
не заканчиваются:
dxctl generate mock
— вызывает кодогенерацию моков;dxctl generate httprpc
— вызывает кодогенерациюHTTP
-ручек. Мы не всегда имеем возможность выставитьgRPC
, поэтому пользуемся строго специфицированнымиHTTP
-эндпоинтами;dxctl generate workqueue
— генерация локальной очереди задач сервиса.
Мониторинг
Grafana, Prometheus и Jaeger являются частью нашего локального окружения.
Все дашборды сервисов облачной платформы #CloudMTS мы отлаживаем локально, перед тем как они поедут в боевую Grafana. Последняя закрыта для редактирования в проде напрямую. Обновление происходит только через CI.
Для описания дашбордов Grafana мы используем grafonnet
. У него есть и плюсы, и минусы, но сейчас речь только о подходе. Вместо того чтобы предложить разработчику поставить на свою машину jsonnet-bundler
и grizzly
нужных версий, мы запаковали всё в докер-образы, которые также собираются на CI и публикуются во внутреннем реджистри.
Процесс работы с дашбордами также автоматизирован с помощью dxctl
:
dxctl grafana apply
— позволяет накатить описание дашборда в форматеgrafonnet
на собственную локальную копию графаны. В самой Grafana уже подключены несколько источников данных, включая тот Prometheus, который развернут локально, и Prometheus с закрытого контура разработки.dxctl grafana init
создает необходимую структуру для чистого дашборда, если такой требуется создать с нуля.dxctl grafana export
— экспортирует дашборд в понятный для Grafana формат без применения его в локальном окружении.
Тестирование и линтеры
Выше я упомянул паттерн Service Template, поэтому не могу умолчать о том, что запуск тестов и линтеров происходит одинаково для всех наших сервисов:
dxctl service test
— запуск всех тестов;dxctl service lint
— запуск линтера.
Линтер имеет общий набор правил, обязательных для каждого сервиса. Его нельзя смягчить в каком-то определенном сервисе, но можно ужесточить. Так мы добиваемся соблюдения общих подходов к оформлению кода в наших сервисах.
Пока что о dxctl
на этом всё. Мы пока не рассмотрели автоматизацию процесса деплоя из командной строки. Но это мы оставим на следующий раз. Я постараюсь показать, как можно использовать API Gitlab (или любой другой системы, поддерживающей CI) для подобной автоматизации.
Что мы получили на данный момент?
- Быстрый и воспроизводимый онбординг новых сотрудников
- Унифицированное локальное окружение каждого разработчика
- Единый инструментарий и подходы в разработке микросервисов
- Одинаковые (совпадающие до версий патчей) инструменты у каждого разработчика, сложность использования которых скрыта под единым интерфейсом
На чем это можно сделать?
Почему мы не говорим в этой статье о виртуализации под капотом? Всё просто. Вы можете использовать ту виртуализацию или те инструменты, которые лучше всего в данный момент подходят вам:
- vagrant + vbox
- qemu
- docker + docker compose
- minikube
- в облаке
Что нужно, чтобы адаптировать подобный подход у себя в компании?
Можно начать со своей команды.
- Для начала выделите основные процессы: как происходит кодогенерация, что используется для описания API, какой инструмент миграции схем БД используется и как, какие сторонние инструменты используете. Какие задачи вы решаете, что часто повторяется и что из этого можно автоматизировать, скрыв под капотом сложность вызова с длинным и всегда повторяющимся списком аргументов.
- Выделите инструменты, которые используются в работе: база данных, брокер сообщений, тот же
protoc
, генератор OpenAPI. - Создайте под актуальные версии инструментов отдельный репозиторий/группу. Так вы добьетесь унификации версий и простоты распространения каждого инструмента. Об этом я писал здесь. Вам важно использовать не последние версии, а одинаковые для каждого члена команды.
- Уточните у команды эксплуатации, что у вас используется в продакшене и в каких версиях. Проконсультируйтесь, как лучше всего было бы построить локальную копию в уменьшенном масштабе. Здесь вам не нужно поднимать столько же инстансов той же Kafka — достаточно и одного, самое важное — чтобы у вас совпадали протоколы общения. Под протоколами я имею в виду не только формат TCP-пакетов, но и сами практики использования инструмента.
- И наконец, посадить на новый инструмент и подход вашу команду. Пожалуй, это самая сложная задача, но, уверен, вы с этим справитесь. Когда коллеги видят, что инструмент имеет простой и понятный интерфейс, решает их проблемы и снимает с них большую часть рутинных задач, они с удовольствием начинают пользоваться новым инструментом. А самые активные становятся контрибьюторами идей и новых фич.
Спасибо за внимание!
В комментариях я предлагаю вам поделиться вашими подходами и взглядами на проблему. Очень надеюсь на конструктивную и интересную дискуссию.