В любой компании рано или поздно встаёт вопрос: как писать код так, чтобы его было удобно поддерживать и масштабировать? Одни решают эту задачу через процессы и правила, другие — через инструменты и платформенные команды. Но если присмотреться, платформенный подход возникает почти везде, даже там, где формально нет платформы. 

Меня зовут Дима Салахутдинов, я тимлид платформенной команды в Купере, автор телеграм-канала «Стафф-инженер», и в этой статье я расскажу, какие принципы лежат в основе платформенной разработки, как они помогают снижать издержки и почему единые инструменты важнее бесконечных споров о том, какой фреймворк лучше.

Зачем нужна платформа

Представьте, вы работаете в большой компании, и вам нужно запустить новый сервис. Скорее всего вам придётся сделать кучу «приседаний»: оформить проект, выбрать технологии, поднять локальную разработку, настроить деплой на стейджинг и в прод. А как только сервис окажется в продакшен-среде, появятся новые заботы, связанные с эксплуатацией: мониторинг, логи, метрики, дашборды. В итоге, из-за необходимости решения технических нюансов, времени на разработку продукта остается меньше.

А если в компании более 300 сервисов и написаны они на разных языках, ситуация еще более усугубляется. В таком случае подобный объем работ проделает каждая команда. А используемые технологии будут очень сильно зависеть от личных предпочтений, опыта и бэкграунда разработчиков в конкретной команде.

Было бы отлично, вынести повторяющиеся технические и оформительские задачи за скобки, делегировать их отдельной команде инженеров. А при разработке продукта фокусироваться на исключительно реализации продуктовых фич.

Такой подход называется PaaS (Platform as a Service), или попросту платформенный. Он позволяет переиспользовать однотипные решения для разработки различных сервисов и помогает повышать эффективность разработки. PaaS подход призван ускорить разработку и обслуживание сервисов через стандартизацию технических и оформительских решений.

Из чего состоит платформа

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

  1. Инфраструктурная часть — общий процесс деплоя (канарейка), адаптация Kubernetes-кластеров под платформу, инфраструктура сбора и визуализации логов и метрик.

  2. Общие сервисы — авторизация, аутентификация, сервисы экспериментов и другие инструменты, которыми пользуются почти все продуктовые команды. А также сервисы платформы, к примеру, реестр всех сервисов, запущенных на платформе.

  3. Языковая часть — стандартные библиотеки на разных стеках, отвечающие требованием инфраструктуры. Именно на ней мы остановимся подробнее. Всё, что здесь разрабатывает и поддерживает платформенная команда — мы называем платформенным тулингом.

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

sbm-cli service create newServiceName https://your-gitlab.url –engine=go|python|rails

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

Для деплоя на продакшен останутся небольшие формальности: согласовать и выделить ресурсы для баз данных и с помощью стандартизованного деплоя выкатить сервис в продакшен. 

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

Вот несколько вариантов шаблонов, который поддерживает платформенная команда:

  • Для Golang у нас есть шаблон простого сервиса, терминах DDD (Domain-Driven Design) — транзакционный сервис, куда можно быстро добавить бизнес-логику.

  • Для более сложных сценариев есть вариант с «чистой» (гексагональной) архитектурой, где удобно хранить домен с богатой бизнес-логикой.

  • Для Python доступны шаблоны простого сервиса или сервиса на базе ML.

  • Шаблон сервиса на Ruby (фреймворке Rails).

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

Платформенная библиотека:

  • пробрасывает в приложение зависимости, которые в платформе считаются стандартизованными; 

  • осуществляет их базовую преднастройку; 

  • включает вспомогательный тулинг, связанный с технологическим стеком компании: например, поддержку HTTP, gRPC, работу с Kafka.

Если говорить о функциональных составляющих, то платформенная библиотека для любого стека поддерживает типовой набор возможностей для запуска сервиса в соответствие с технологическим стеком компании.

Для пользователя это означает, что не нужно делать выбор какого-то решения. Если нужно запустить HTTP-сервер, или Kafka консюмер, это возможно сделать с помощью готовых, поставляемых платформой, библиотек. Где-то подходы могут отличаться, в каком-то стеке может не быть базовых OSS-библиотек для решения, тогда платформа разрабатывает свою.

Цель платформы — реализовать функционал, необходимый для быстрого запуска сервисов и максимально его унифицировать.

В качестве специфического примера, приведу платформенную реализацию адаптера для работы с БД. Платформа предоставляет тулинг для удобной работы с БД из приложения.

Пользователям платформы доступны:

  • ORM библиотека

  • инструмент запуска миграций

  • и клиент для БД.

К примеру, в Ruby весь функционал присутствует в фреймворке Rails, а для Go — его необходимо собирать из разных библиотек. Хотя внутренняя реализация для разных стеков отличается. Принципы работы примерно одинаковые. 

Еще одна составляющая платформенного тулинга — это опциональные, специфичные библиотеки, которые платформа разрабатывает для решения определенных задач. К примеру, в OpenSource не было подходящей библиотеки для реализации Transactional Outbox паттерна и команде платформы пришлось написать свою.

Стандартизация подходов

Все эти примеры показывают главный принцип разработки платформенного тулинга: унификация и стандартизация. Гораздо выгоднее иметь одно решение для конкретной задачи, чем десяток разных. Единое решение дешевле развивать, поддерживать, а продуктовым командам проще, когда всё работает примерно одинаково.

Например, рубисты могут бесконечно спорить, какой веб-фреймворк лучше, а Golang-инженеры — стоит ли полагаться на стандартную библиотеку. Но с точки зрения платформы важен не сам выбор, а то, что инструмент один.  Проще и более взаимозаменяемой становится разработка и обслуживание сервисов со стороны пользователей.

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

Но чтобы OSS решение подходило на роль платформенного его необходимо всесторонне изучить по ряду вопросов:

  1. Проверить лицензию. 

  2. Оценить, насколько оно наблюдаемо (есть логи, метрики, поддержка OpenTracing). 

  3. Как ведет себя под нагрузкой. 

  4. Как масштабируется. 

  5. Используется ли в больших продакшн-средах.

Если ответ хотя бы на один из вопросов отрицательный, это повод задуматься о собственной реализации. Когда за основу берется OpenSource библиотека, платформа проектирует API и способ ее использования в сервисах. 

Варианта обычно два:

  • либо реализовать свой высокоуровневый фасад

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

Каждый вариант имеет свои плюсы и минусы. Фасад дольше делать и сложнее проектировать, зато он может уберечь пользователей в случае изменения реализации. Второй вариант — предоставить полный доступ разработчикам к публичному API фреймворка — подходит в случае, если фреймворк зрелый и не подвержен изменениям. Такой вариант быстрей, но рискованней.

Хороший пример — Ruby on Rails. Это самый популярный и зрелый фреймворк в экосистеме Ruby, с минимальной вероятностью изменения публичного. Здесь нет смысла городить обертку, проще дать пользователям прямой доступ к API.

Каждый разработчик сталкивался с болью обновления фреймворка до мажорной версии. Если в компании нет платформы, обычно разработчик остается один на один с этой проблемой.

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

Таким образом платформа снижает издержки разработки и обслуживания сервисов за счет стандартизации технических и организационных решений.

Снижение эксплуатационных издержек

В долгосрочной перспективе основная статья расходов — это поддержка сервисов в продакшене. 

Возьмём инциденты. Для владельца сервиса это всегда дорого: бизнес теряет деньги, а команда времени и нервов. Поэтому после инцидента принято проводить разбор (постмортем): найти первопричину, задокументировать ее и выработать план, как избежать повторения. А дальше пойти и поправить первопричину.

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

Еще один принцип платформенного подхода — тиражируемость решений. Максимальная ценность достигается тогда, когда унифицированные инструменты установлены в как можно большем числе сервисов.

У продуктовых команд не всегда есть время обновлять версии библиотек. Поэтому платформа использует разные практики:

  • Мягкие: автоматизация (скрипты, которые обновляют библиотеки без участия команд), а также скоринг сервисов. Последний измеряет «здоровье» по множеству критериев — от времени транзакций в БД до нагрузки на чтение/запись. Один из критериев — версия платформенной библиотеки. Чем она старее, тем хуже общий балл.

  • Жёсткие: блокировка релизных пайплайнов до обновления библиотеки. Это непопулярный метод, но необходимый, например, при критических уязвимостях или серьёзных багах. Пайплайны — тоже часть платформы, и у команды есть возможность управлять ими напрямую.

Тиражирование и единообразие библиотек упрощают поддержку, повышают надежность сервисов и в конечном счете снижают эксплуатационные издержки.

Интроспекция больших систем

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

Распределенный трейсинг

Справиться с этим помогает распределённый трейсинг: он позволяет отслеживать цепочки взаимодействий сервисов и понимать, как работает система. Но чтобы трейсинг работал, нужна не только инфраструктура (сбор телеметрии, пайплайны, визуализацию), но и программная поддержка со стороны библиотек.

Инструментирование — это добавление в библиотеку кода, который генерирует телеметрию: спаны, метки, события. Авторы книги об OpenTelemetry пришли к выводу, что большинство open source библиотек этим пренебрегают. 

Наблюдаемость почти никогда не встроена в OSS библиотеки «из коробки». Очень часто инструментированием занимаются не авторы библиотек, а другие люди. В Купере они работают в платформенной команде :).

Вот несколько принципов инструментации, или чек-лист, которому следует платформа:

  1. Принцип минимального сопротивления. Подключил библиотеку — и все сразу завелось: телеметрия, спаны, трейсы. Минимум ручных настроек. Для этого библиотеку нужно буквально «обмазать» кодом, который собирает и отправляет данные. Иногда это делают через промежуточные слои (middleware), иногда прямо вшивают в исходный код.

  2. Проброс контекста. Если один сервис отправил сообщение в Kafka, то на стороне получателя должен восстановиться контекст вызова. Только так получится собрать связанный трейс и проследить всю цепочку событий.

  3. Соблюдение семантических конвенций OpenTelemetry. Они описывают, какие именно данные и в каком формате нужно отправлять. Если все придерживаются этих правил, то вне зависимости от стека разработки данные телеметрии выглядят одинаково. А это сильно упрощает анализ и даже открывает дорогу к автоматическому анализу трейсинга.

Мы наверняка замечали, что зрелость OSS библиотеки можно оценить не только по числу звездочек на GitHub. Если в ней реализована поддержка OpenTracking, значит авторы вложились в инструментацию, проект используют в серьёзных продакшенах и его реально можно мониторить. Почти всегда наличие поддержки OT — это гарантия качества!

Метрики

Другой важный аспект наблюдаемости - метрики. В книге Google SRE выделяют четыре ключевых показателя:

  • Latency — сколько времени уходит на обработку запроса.

  • Traffic — объём запросов к сервису, по которому можно прогнозировать нагрузку (например, к «черной пятнице»).

  • Errors — частота ошибок, которая сразу говорит о стабильности.

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

Многие из этих метрик вычисляются автоматически платформенными библиотеками. Благодаря этому мониторинг всех сервисов можно сводить в единые дашборды (например, в Grafana) и анализировать их одинаковым образом.

Единый дэшборд метрик Rails-приложений, с фильтром по namespace/сервису
Единый дэшборд метрик Rails-приложений, с фильтром по namespace/сервису

Логи

Задача платформы:

  • ввести стандарты логирования, соответствующие инфраструктуре сбора логов, и валидировать правильность лого. Например, формат JSON для логов всех сервисов и библиотек, чтобы инфраструктура могла обрабатывать данные единообразно. 

  • обеспечить валидную семантику логов: логи gRPC должны содержать одни и те же поля (название сервиса, метод, клиент, код ответа) независимо от языка. Это упрощает анализ и ускоряет расследование проблем. 

Когда телеметрия, метрики и логи «из коробки» встроены в платформенный тулинг, система становится наблюдаемой из коробки: проще поддерживать и эксплуатировать.

Исследование производительности

Важный аспект качества платформенного тулинга — производительность. Системы растут, нагрузка увеличивается, и платформа должна держать этот ритм. Даже мелочь вроде JSON-сериализации не должна становиться бутылочным горлышком. Поэтому все платформенные инструменты обкатываются на стендах под нагрузкой, чтобы убедиться: при росте числа инстансов приложения их пропускная способность растет пропорционально.

k-6 тесты на стендах платформы
k-6 тесты на стендах платформы

Что такое платформенный стенд

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

Все обновления тулинга в первую очередь накатываются именно сюда. Потом стенд нагружается с помощью инструментов вроде k6 — и можно посмотреть, как система ведет себя под нагрузкой. Это что-то вроде «нагрузочного препрода», но заточенного под платформу.

Конечно, никакая синтетическая нагрузка не заменит реальный продакшн. Поэтому команда платформы почти всегда участвует в пилотных обновлениях платформенных библиотек в сервисах. Чтобы снизить риски, используют несколько подходов:

  • Учёт критичности сервиса. Начинаем с Tier-4 (внутренние сервисы с небольшой нагрузкой, где простой не критичен). Tier-1 — это уже клиентские сервисы, где даже минуты простоя оборачиваются большими потерями. Балансируя между тирами, можно выкатывать обновления аккуратнее.

  • Фича-флаги. Включаем новый функционал точечно, контролируем влияние.

  • Канареечный релиз. Постепенное раскатывание (например, в течение нескольких часов) с мониторингом.

  • Strangler pattern. Рядом строится новая система, которая оборачивает старую, и трафик переводится постепенно (см подробности в докладе)

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

Сформулируем еще один принцип разработки платформенных библиотек - готовность к нагрузкам, который дает командам уверенность, что платформенные инструменты не подведут под ростом трафика.

Принципы стандартизации платформенного тулинга

На этом этапе у нас получается своего рода пирамида:

  1. Унификация. Единые стандарты и инструменты.

  2. Тиражируемость. Массовое распространение этих инструментов по всей компании.

  3. Наблюдаемость. Общие подходы к телеметрии, метрикам и логам.

  4. Готовность к нагрузкам. Масштабируемость и стабильность решений.

Эта база упрощает внедрение более сложных практик вроде SRE или хаос-инжиниринга: когда всё унифицировано, строить более высокоуровневые подходы становится проще и дешевле.

Откуда берутся задачи для платформы

Может показаться, что нужно специальное исследование, чтобы выявить объекты стандартизации и сформировать бэклог задач для платформенной команды. На  практике же есть ряд источников стабильного пополнения бэклога, которые мы можем обсудить с перспективы сформулированных принципов стандартизации:

  • Инициативы высокого порядка. Например, компания хочет внедрить SLA. Для этого нужны унифицированные метрики с веб- и gRPC-серверов — значит, задача идёт в платформу.

  • Инфраструктурные ограничения. Система логирования работает только с JSON? Придётся адаптировать все сервисы через платформенные библиотеки. Платформа здесь выступает посредником между инфраструктурой и командами.

  • Разборы инцидентов. Решения, найденные на постмортемах, можно масштабировать через платформенные библиотеки.

  • Выравнивание стэков. В реальности один язык (часто Go) получает на старте полный набор инструментов, а остальные подтягиваются позже. Задача платформы — нивелировать этот разрыв.

  • Запросы команд. Фидбэк от разработчиков помогает понять, что нужно стандартизировать и унести в платформу. Особенно это касается инструментов, которые полезны сразу многим сервисам.

Через выведенную базу мы можем легко классифицировать задачи, которые продуктовая разработка может (или даже должна) делегировать в платформу:

  • В первую очередь это инструменты, требующие стандартизации и унификации подходов и максимального распространения в компании. 

  • настройка наблюдаемости. При наличие платформы — продуктовая команда не должна ковыряться в кишках библиотек и настраивать инструментацию по отправке трейсов и метрик. Только если это не специфические продуктовые метрики.

  • Оптимизация и работа библиотек под нагрузкой тоже делегируем в платформу. 

Но это совсем не означает, что использовать можно только платформизованные решения. К примеру в Купере, такого запрета нет. Команды могут использовать другие библиотеки, но важно понимать, что платформа в силу ограниченного ресурса мейнтейнит только ограниченный (и широко используемый) набор инструментов, которые в компании считаются стандартизованными.

Нужна ли вам платформа

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

Платформизация (стандартизация) инструментов — это долгий и дорогой процесс, требующий больших вложений времени и ресурсов. Если фича нужна лишь 1–2 сервисам, нет смысла тащить её в платформу. Это локальное решение. Но если потребность становится массовой — это сигнал к платформизации.

Платформа снижает издержки разработки и эксплуатации. Но эта экономия сама по себе стоит усилий. Получается парадокс: платформа — это про удешевление, но ее развитие требует немалых вложений. В конечном счёте всё упирается в соотношение затрат и выгод. Экономика должна быть экономной  :)

Платформенный подход в вашей компании

В реальности, даже в небольших компаниях, платформенный подход часто возникает сам по себе. Всегда найдется инженер, которому не нравятся «местечковые решения». Он выносит код в библиотеки, убеждает коллег ими пользоваться, настраивает инсорсинг, а иногда даже выносит результат в open source. В каком-то смысле open source — это платформа «на минималках»: есть унификация, а дальше каждый использует её по-своему.

Кроме того, у любой команды есть ограничения инфраструктуры и свои стандарты. Кто-то пишет ADR, кто-то документирует правила в Confluence — и все начинают работать примерно одинаково.

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

Даже если у вас нет формальной платформенной команды, платформенный подход все равно возникает. Его появления инициируют инженеры, интуитивно действуя в соответствии с принципами, которым мы с вами обсудили.

Можем считать, что в этом случае состав платформенной команды очень маленький: один-два активиста. И это уже отлично.

Спасибо, что дочитали, расскажите в комментариях, что думаете про платформенный подход?

Комментарии (0)