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

Отвечая на этот вызов, мы создали внутреннюю платформу разработки. Это набор стандартов, библиотек, шаблонов и решений по автоматизации. Он позволит командам не тратить время на одни и те же технические вопросы, а взять всё «из коробки» и сосредоточиться на задачах. 

Меня зовут Олег Сабрян, я руководитель отдела разработки PaaS ВКонтакте. В этой статье я расскажу, как мы строили внутреннюю платформу разработки, какие грабли собрали, какие принципы выработали и почему это не только про код, но и про людей. 

Как всё начиналось: от хаоса к осознанности

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

Новый сервис можно сделать за один-два дня, но чтобы дотащить его до прода, уходили недели. Необходимо было согласовать Sentry, дождаться, пока создадут CI/CD-пайплайны, подготовят helm-чарты для k8s и т.д. Проходя этот длинный путь, мы решили создать портал по принципу единого окна, где всё необходимое для запуска и поддержки сервиса будет доступно за пару кликов. 

Как устроена внутренняя платформа разработки?

  1. Платформа строится вокруг собственного SDK: это библиотеки и шаблоны, которые интегрированы в инфраструктуру. Разработчик, подключая библиотеку, получает телеметрию на дашбордах и рабочую конфигурацию «из коробки», а взаимодействие с инфраструктурой сведено к минимуму.

  2. Команда PaaS сопровождает коллег на всех этапах их задач: от идеи до продакшена, помогая с архитектурой и выбором инструментов. Все шаблоны интегрированы с CI/CD: сервисы собираются и выкатываются буквально по кнопке, а стабильность процессов обеспечивается в сотрудничестве с DevOps.

  3. Автоматизация инфраструктуры избавила от рутинных операций: например, получение DSN Sentry теперь происходит автоматически, без бюрократии. Observability & Monitoring реализованы через нашу систему Argos, которая отслеживает аномалии метрик и сигнализирует о проблемах.

За три года с момента появления PaaS мы запустили более 150 сервисов на разных языках: Go, Python, TS.

Что входит в хорошую платформу разработки:

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

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

  • Автоматизация. Ручные операции — зло. Если для доступа к Sentry или базе данных нужно идти к человеку, писать таску и ждать неделю — это тормозит бизнес. Всё, что можно автоматизировать, должно быть автоматизировано.

  • Унификация и поддержка. Стандарты и шаблоны позволяют DevOps-инженерам и администраторам эксплуатировать сервисы без постоянного участия разработчиков. Всё прозрачно: где логи, где метрики, как менять параметры.

Стандарты: как перестать спорить и начать работать

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

  1. Выбор базовой операционной системы. Вдаваться в детали выбора здесь не буду — всё-таки основной акцент сейчас на разработке, а не на сравнении дистрибутивов. 

  2. Инфраструктурные компоненты. Всё фиксируется заранее: например, Kafka — для одних задач, RabbitMQ — для других, Redis и Memcached — для своих сценариев. В итоге появляется простая и понятная таблица: слева название движка, справа область его применения. То же касается языков и фреймворков. Это снимает массу вопросов и экономит время всей команды. Условно, это можно назвать техрадаром.

  3. Телеметрия и мониторинг. Соберите по максимуму всю самую важную информацию по рантайму приложения: потребление памяти, работа GC, CPU и т. д. Чем больше вы соберёте на старте, тем проще будет работать с приложениями в будущем. Главное — помнить, что нет ненужных метрик по рантайму. 

    У вас годами может не возникать проблем, к примеру, с GC. Но одна допущенная ошибка — и перформанс приложения упадёт, а потребление ресурсов вырастет. Отловить это поможет готовая метрика на дашборде.

  4. SDK и шаблоны. SDK обеспечивает быстрый старт, интеграцию с инфраструктурой и предсказуемое поведение сервисов вне зависимости от среды — будь то Bare Metal, Kubernetes или своё кастомное облако. Хороший SDK снимает головную боль с логами, метриками и администрированием: всё уже реализовано, не нужно каждый раз изобретать велосипед или писать свою библиотеку для сбора метрик. Всё это должно быть заложено в базовый шаблон приложения.

Практика и наш опыт: как строить SDK и шаблоны

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

  1. Конфигурация приложения. Важно продумать, как будет устроена конфигурация приложения — именно с этим вы столкнётесь при первом деплое в продакшен. Часто в компании уже есть для этого сервисы, и проще всего — предоставить единую библиотеку для работы с конфигами. Определитесь с форматом: это может быть YAML-файл, отдельный .env-файл, параметры запуска или что-то ещё. Также продумайте, как именно эти конфиги будут доставляться до приложения и кто за это станет отвечать. 

    Например, ВКонтакте мы реализовали «слоёный пирог». Сначала считывается YAML-файл — он используется для локальной разработки. Всё остальное — уже для продакшена. Разработчик запускает приложение, и оно везде работает одинаково: 

    1. сначала читается YAML;

    2. подтягиваются переменные окружения;

    3. обрабатываются аргументы запуска;

    4. всё это маппится на структуры данных, которые и представляют собой конфиги. 

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

  2. Секреты и безопасность. Очень важно заранее продумать, как вы будете доставлять секреты в сервисы. Просто прописывать секреты в конфигурационных файлах — небезопасно. ВКонтакте мы используем vault: 

    1. разработчики передают свои секреты администраторам через систему одноразового просмотра ссылки и данных внутри, те размещают их в vault; 

    2. дальше агентом автоматически подтягиваются секреты в контейнеры приложений. 

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

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

    1. узнать, как сейчас устроены проекты;

    2. какие структуры уже используются и насколько они распространены. 

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

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

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

  4. Телеметрия. Для сбора метрик определите подход: push- или pull-модель, формат данных и хранилище. Решите, какие метрики будут писаться по умолчанию, — это позволит подготовить типовые дашборды и быстро реагировать на сбои первых сервисов. Мы рекомендуем фиксировать базовые показатели:

    1. CPU;

    2. GC;

    3. память (heap, stack);

    4. числа потоков и goroutines — чтобы сразу отслеживать типичные ошибки, например избыточное создание goroutines.

    Стоит предусмотреть и продуктовые метрики. Лучше, если у разработчика будет возможность использовать уже существующую общую метрику, а не изобретать свою. Так проще строить сквозные графики и автоматические алерты. Не забывайте и о метриках железа: 

    1. CPU; 

    2. RAM;

    3. LAN. 

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

    У нас для агрегации метрик используется собственная разработка VK — Statshouse по push-модели, которая собирает более миллиарда событий со всей инфраструктуры. Мы заранее готовим общие дашборды: метрики можно фильтровать по сервисам, окружениям и даже по отдельным подам. Дополнительно используем инструменты фильтрации и очистки лишних данных, чтобы дашборды оставались информативными и не перегруженными.

  5. Инфраструктурные библиотеки. Когда вы реализовали первые библиотеки для записи метрик и определились с инфраструктурой, следующий шаг — создать общие библиотеки для работы с инфраструктурными компонентами: 

    1. базами данных;

    2. ingress/egress;

    3. интерфейсами взаимодействия (например, HTTP). 

    Важно, чтобы все эти стандартные библиотеки автоматически писали метрики в выбранном формате.

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

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

  6. Кодогенерация. Мы используем Cookiecutter: у нас есть несколько базовых шаблонов, каждый из которых можно параметризировать. Сначала мы думали сделать один универсальный шаблон с множеством опций, где разработчик отвечал бы на вопросы: нужен ли Swagger, требуется ли RPC-сервер, нужны ли фоновые «демоны» и так далее. Но поняли, что количество параметров может стать неконтролируемым. Поэтому мы разбили всё на отдельные базовые шаблоны — например, backend, админка, бот или ML-сервис. И уже внутри каждого реализуем нужную параметризацию, учитывая специфику.

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

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

Мультиязычность: как поддерживать разные стеки

Мультиязычность — одна из самых интересных и сложных тем при построении платформы. ВКонтакте используетс сразу несколько языков: у нас есть Go, Python (чаще для ML-задач), есть Node.js и другие. Изначально для каждого языка формировалась отдельная платформа — со своей спецификой и правилами. Со временем стало понятно, что поддерживать несколько разрозненных платформ неудобно. Они начинают расходиться:

  • по конфигурации базовых компонентов: http-сервер, логгер, клиенты к базам и т. д.;

  • по логированию: может быть разный формат записи данных;

  • по метрикам: приложения могли писать одну и ту же метрику по-разному.

В итоге даже такие базовые вещи, как мониторинг или система логов, перестают быть универсальными — и для каждого языка нужны свои отдельные решения.

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

Как мы к этому пришли? Было много споров внутри команды. В какой-то момент приняли решение: при создании ключевых библиотек (например, логгера или клиента для работы с популярной базой данных) мы сначала описываем конфигурацию и интерфейс взаимодействия с библиотекой на псевдокоде, а потом реализуем это на всех языках, которые нам нужны.

Плюс подхода в том, что конфигурация библиотек становится одинаковой для всех языков. Админы, работая с сервисами на Go или Python, не сталкиваются с неожиданностями — всё работает и настраивается одинаково. Разработчикам тоже проще: при переходе с одного языка на другой всё выглядит знакомо, поведение и конфиги схожи.

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

Получается такой roadmap для SDK: 

  • Configs. Формируем конфигурацию, затем отдаём её разработчикам. 

  • Template. Делаем базовые шаблоны, прорабатываем структуру приложений. 

  • Observability. Внедряем стандарты для записи метрик и логов. 

  • Infra packages. Абстрагируем инфраструктуру в общих библиотеках. 

  • Product packages. Делаем продуктовые библиотеки. Иногда, если одна и та же модель пользователя нужна в разных сервисах, её выносят в отдельную. 

  • Codegen. Занимаемся кодогенерацией.

  • Multilanguage. Подгоняем под стандарты другие фреймворки в компании.

Процессы и автоматизация: как избавиться от бюрократии

Когда речь заходит о платформе, важно помнить: написать код — это только часть дела, ещё нужно уметь выкатывать и сопровождать. Раньше, чтобы зарелизить один новый сервис, нужно было ставить восемь задач на разных Jira-досках и взаимодействовать с командами. А любая ошибка — не туда поставил задачу или заполнил не так поле в шаблоне задачи — приводила к пинг-понгу таски, и это могло затянуться надолго. По итогу на заполнение тасок могла уйти неделя-полторы. 

Какие изменения мы внесли:

  1. Описали процессы и участников. Мы зафиксировали всех участников процесса раскатки новых сервисов и подробно описали, какие этапы проходит сервис на пути в продакшен. Особое внимание уделили SLA: если инфраструктурные команды не дают чётких сроков, сервис может застрять на этапе выката на месяц.

  2. Единая Jira-задача и базовая автоматизация. Вместо множества разрозненных досок сделали одну общую Jira-таску для PaaS-команды. Разработчики заполняли все необходимые данные в ней, а дальше информация автоматически подтягивалась на другие доски. Это исключило хождение по разным командам и ускорило движение задач. Теперь это делала автоматика, а разработчик уже мог видеть в закрепе связанные таски и при необходимости дополнять их.

  3. Перераспределение ответственности. Пересмотрели распределение задач между командами. Некоторые задачи, которые раньше выполняли админы или DevOps-инженеры, взяла на себя команда платформы. Например, мы стали помогать с настройкой CI/CD, объяснять разработчикам, как заводятся пайплайны, и брать на себя часть саппорта. При этом профильные команды продолжали контролировать процесс.

  4. Автоматизация рутины. Мы упростили рутинные задачи с помощью простых скриптов. Например, реализовали автоматическое создание алертов. После этого приступили к более серьёзной работе: развернули собственный сервис, который принимает хуки из Jira и при необходимости создаёт или перемещает нужные задачи. Типичная ситуация: сервис мог зависнуть на этапе деплоя на десять дней просто из-за того, что никто не перевёл задачу в нужный статус. Автоматизация этого процесса с помощью скрипта сразу улучшила Time to Market. Как только мы автоматизировали общий workflow в Jira и наладили процессы, стали постепенно избавляться от ручных операций.

Например, у нас изначально были подготовленные шаблоны для CI/CD в TeamCity. Но чтобы использовать их для конкретного сервиса, нужно было создать задачу для DevOps-команды и ждать. Чтобы улучшить процесс, мы действовали так:

  • настроили автоматическое создание MR в репозиторий с TeamCity DSL, оставив за человеком только проверку и принятие изменений;

  • реализовали автоматическое слияние созданного merge request;

  • исключили соответствующую задачу из общего workflow в Jira.

По итогу получили первое упрощение и ускорение процесса, когда создаётся новый сервис. 

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

Итоги: платформа — это не только про технологии, но и про людей

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

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

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


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

P. S. Пишите в комментариях, о чём хотели бы узнать подробнее, — обязательно расскажем в следующих статьях.

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