Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Прим. перев.: в этом материале, опубликованном прошлой весной в блоге Dropbox, инженеры компании рассказывают о том, какие проблемы были у их монолита, жившего аж с 2007 года, что они предприняли для их решения и какой результат это принесло.
Чтобы всегда радовать пользователей, сервис Dropbox должен быть надежным и отзывчивым. С самого начала нам, как компании, приходилось масштабироваться. Сегодня нашими услугами пользуются более 700 миллионов зарегистрированных пользователей со всех уголков планеты. В общей сложности они ежесекундно генерируют минимум 300 тыс. запросов. Увы, системы, отлично подходившие для стартапа, перестали справляться с возросшей нагрузкой. Возникла насущная потребность разработать новую модель для внутренних систем и, конечно, придумать способ перехода на нее без риска угробить основной продукт в процессе.
В этой статье мы расскажем, как и почему разработали и развернули Atlas — платформу, реализующую основные преимущества сервис-ориентированной архитектуры (SOA) и при этом минимизирующую типичные издержки, связанные с владением сервисом.
Монолит должен быть осознанным выбором
Основная часть разработчиков в Dropbox занимается кодом бэкенда на стороне сервера, и все это происходит в соответствующем монорепозитории. Для серверной разработки в Dropbox в основном используется Python: наш монолитный сервер на Python насчитывает более 3 млн строк кода.
Да, монолит работал, но также он мешал росту. Разработчикам приходилось ежедневно бороться с побочными эффектами от использования монолита. Каждая строка кода, написанная ими, попадала в общий «котел» независимо от желания — не было никакой возможности выбирать, чем действительно полезно поделиться, а что лучше изолировать в рамках отдельного endpoint'а. Аналогичным образом в production судьба endpoint'ов оказывалась неразрывно связанной со всеми остальными endpoint’ами независимо от стабильности, критичности или владельцев этих endpoint'ов.
В прошлом году мы запустили специальный проект, намереваясь разбить монолит и превратить его в бессерверную managed-платформу. Это позволило бы сократить запутанность кода и «освободить» сервисы и связанные с ними инженерные команды друг от друга. Для этого было необходимо обновить как архитектуру (например, переключиться на gRPC и использование транскодинга gRPC-HTTP в Envoy), так и эксплуатацию (например, реализовать автомасштабирование и анализ канареечных развертываний). Эта статья вобрала в себя ключевые идеи и уроки, усвоенные в процессе нашего путешествия.
1. Metaserver: монолит Dropbox
Топологию внутренних сервисов Dropbox на сегодняшний день можно представить как этакую модель «солнечной системы», в которой за значительную часть продуктовой функциональности отвечает монолит, а компоненты платформенного уровня, такие как аутентификация, хранилище метаданных, файловая система и синхронизация, выделены в самостоятельные сервисы.
Примерно половина всех коммитов в серверном репозитории приходится на Metaserver — приложение-монолит на Python'е.
Metaserver — один из старейших сервисов Dropbox. Он был написан в 2007 году одним из сооснователей компании. Metaserver хорошо послужил Dropbox, но естественный рост кодовой базы из-за появления новых функций привел к серьезным проблемам.
Запутанная кодовая база
Код Metaserver'а изначально был организован по простому принципу, типичному для небольшого Open Source-проекта: библиотека, модель, контроллеры — без централизованного курирования или защитных механизмов для обеспечения целостности кода. Со временем кодовая база Metaserver'а стала одной из самых неорганизованных и запутанных в компании.
Структура кода Metaserver'а:
//metaserver/controllers/ …
//metaserver/model/ …
//metaserver/lib/ …
Поскольку над кодом работало множество команд, ни одна не чувствовала серьезной ответственности за его качество. Например, при создании очередной продуктовой функции команда могла воспользоваться циклами импорта вместо того, чтобы провести рефакторинг. В краткосрочной перспективе такой подход ускорял поставку кода, однако поддерживать кодовую базу становилось сложнее, а проблемы усугублялись.
Нерегулярность push'ей
Metaserver ежедневно выкатывается в production для всех пользователей. К сожалению, в условиях, когда сотни разработчиков вносят изменения в одну и ту же кодовую базу, вероятность того, что каждый день в ней будет появляться хотя бы один критический баг, довольно высока. В результате приходится проводить откаты и cherry picks в коде всего монолита, что ведет к непоследовательной и непредсказуемой динамике push'ей для разработчиков. Общепринятые лучшие практики (например, изложенные в Accelerate) указывают на то, что быстрый и регулярный деплой — ключ к производительности разработчиков. В этом смысле мы были далеки от идеала.
Нерегулярность push'ей мешает разработчикам. Предположим, что запуск продукта запланирован на день Х. В этом случае разработчику приходится гадать, к какому дню код должен быть готов: за день до Х, за два дня или еще раньше, чтобы код успел выкатиться. Ведь в каком-нибудь совершенно не связанном компоненте, написанном другим разработчиком, может скрываться критическая ошибка, которая в день Х заставит делать откат всего кластера.
Инфраструктурный долг
Когда у вас монолит на несколько миллионов строк кода, инфраструктурные улучшения занимают гораздо больше времени или вообще не происходят. Например, оказывается невозможным организовать выкат новой версии HTTP-фреймворка или Python'а только на некритичных route’ах.
Плюс ко всему Metaserver задействует устаревший Python-фреймворк, который не используется в большинстве других сервисов Dropbox или где-либо еще. Пока внутренний инфраструктурный стек развивался и переходил на Open Source-системы, являющиеся отраслевым стандартом, вроде gRPC, Metaserver застрял на устаревшем фреймворке с (ожидаемо) низкой производительностью и эзотерическими багами. К примеру, старый фреймворк может работать только с HTTP/1.0, хотя все современные библиотеки уже перешли минимум на HTTP/1.1.
Более того, все полезные наработки, интегрированные во внутреннюю инфраструктуру, такие как встроенные метрики и трассировка, приходилось переделывать под Metaserver, построенный на различных внутренних фреймворках.
За последние несколько лет были развернуты несколько программ по решению накопившихся проблем. Не все из них оказались успешными, но даже провальные проложили путь к нынешнему решению.
2. SOA: цена использования независимых сервисов
Попытка разбить Metaserver проводилась в рамках более масштабной инициативы по переходу к сервис-ориентированной архитектуре (SOA). Ее целью была организация лучших абстракций и разделение обязанностей для функционала в Dropbox — в общем, разобраться с проблемами, которые мы хотели решить в Metaserver'е.
План реализации был прост: упростить командам работу с независимыми сервисами в production, после чего превратить части Metaserver'а в эти независимые сервисы.
Инициатива по переходу на SOA включала два главных этапа:
Сделать возможным создание сервисов за пределами Metaserver'а и упростить этот процесс:
а) Извлечь из монолита ключевые функции, такие как управление идентификацией, и обеспечить доступ к ним посредством RPC, тем самым проложив дорогу для реализации нового функционала за пределами Metaserver'а.
б) Разработать лучшие практики и процесс подготовки production для планомерного и масштабируемого запуска многочисленных новых сервисов, ориентированных на клиентский трафик (то есть live-сервисов на сайте).
Разбить Metaserver на множество небольших сервисов, которыми владеют и управляют разные команды.
Переход на SOA оказался долгим и непростым. За полтора года мы достигли определенных успехов на первом этапе. При этом полученный опыт выявил определенные недостатки второго этапа. По мере того, как новые команды и сервисы становились критической частью пути для клиентского трафика, становилось все труднее поддерживать высокие стандарты надежности. Проблема усугублялась по мере дальнейшего отклонения от основных функций и переключения продуктовых команд на использование сервисов.
Одно решение для всего — это миф
Осознав это, мы переосмыслили задачу. Оказалось, что продуктовый функционал в Dropbox можно разбить на две большие категории:
крупные, сложные системы вроде логики, связанной с sharing'ом файла;
небольшая, самодостаточная функциональность — например, домашняя страница.
Так, сервис Sharing включает stateful-логику по контролю доступа, квоты и другие ограничения. С другой стороны, домашняя страница — это довольно простая обертка вокруг сервиса хранилища метаданных/файловой системы. Она редко меняется, эксплуатационная нагрузка от нее невелика, а варианты отказов ограничены. В принципе, операционные проблемы большинства направлений, обслуживаемых Dropbox, похожи: например, неожиданные всплески внешнего трафика или сбои в работе нижележащих сервисов.
Это привело нас к важному выводу:
Небольшая, самодостаточная функциональность не требует независимо эксплуатируемых сервисов. Поэтому был создан Atlas.
В случае небольшого, простого функционала планирование мощностей, настройка толковых алертов и multihoming'а (использование нескольких ЦОД) лишь нагружает команду ненужной работой. Командам просто необходимо «место», где можно реализовать некоторую логику и чтобы она автоматически запускалась, когда пользователь попадает на определенную страницу. Плюс, им нужны базовые автоматические алерты на случай, если на их route’е окажется слишком много ошибок. Код, загружаемый в репозиторий, должен разворачиваться стабильно, быстро и непрерывно.
Большинство продуктового функционала в Dropbox относится к этой категории. Поэтому Atlas стоит оптимизировать именно под эту категорию.
Крупные компоненты должны оставаться самостоятельными сервисами, с которыми Atlas будет нормально сосуществовать.
Большие системы могут управляться более крупными командами, отвечающими за их здоровье. Команды должны сами управлять расписаниями push'ей и настраивать нужные алерты и верификаторы.
3. Атлас: гибридный подход
Учитывая фундаментальные проблемы с устойчивостью Metaserver'а и осознавая, что разбиение монолита на множество мелких сервисов не является универсальным решением, мы разработали Atlas — управляемую платформу для самодостаточного функционала.
Atlas — пример гибридного подхода. Разработчикам Dropbox он предоставляет пользовательский интерфейс serverless-подобной системы вроде AWS Fargate, при этом «за кулисами» остаются автоматически подключаемые сервисы.
Как уже отмечалось, цель Atlas'а — обеспечить большинство преимуществ SOA, при этом минимизируя эксплуатационные расходы, связанные с запуском сервиса.
Atlas — управляемая (managed) система. Это означает, что разработчикам достаточно разработать интерфейс и реализовать соответствующие endpoint'ы. После этого Atlas позаботится о создании production-кластера для обслуживания endpoint'ов. Команда Atlas'а отвечает за доставку кода (pushing) и мониторинг этих кластеров.
Схема ниже демонстрирует изменение рабочего процесса с появлением Atlas'а:
Цели
В идеале Atlas должен был дать нам следующее:
Улучшение структуры кода. В Metaserver'е не было настоящих абстракций для совместного использования кода, что приводило к coupling’у кода. Сильно связанный код сложен для понимания и плохо поддается рефакторингу. Кроме того, при его модификации могут возникать ошибки. Наша цель — привнести структуру и снизить coupling, чтобы новый код было легче читать и изменять.
Независимые, регулярные push'и. Поставлять новый код в Metaserver легко, когда все идет по плану. Разработчикам достаточно разместить код в репозитории, после чего он будет автоматически выкачен в production. Однако вышеупомянутое отсутствие изоляции push'ей приводило к непредсказуемым результатам. Наша цель — создать платформу, на которой команды бы не страдали от багов в несвязанном коде, и заложить фундамент функционала, который позволял бы командам push'ить свой собственный код в будущем.
Минимизация работы, связанной с эксплуатацией. Идея состояла в том, чтобы сохранить эксплуатационные преимущества Metaserver, при этом обеспечив некоторую гибкость сервисов. Было настроено автоматическое управление мощностями, автоматические алерты, автоматический анализ канареечных развертываний. Также был автоматизирован push-процесс. Все это облегчило миграцию от монолита к управляемой платформе.
Унификация инфраструктуры. Цель в том, чтобы унифицировать все обслуживающие элементы и свести их к стандартным Open Source-компонентам вроде gRPC. Другими словами, нет смысла заново изобретать велосипед.
Изоляция. Некоторые элементы (например, домашняя страница) важнее других. Необходимо изолировать их от остальных, чтобы перегрузка или баг в одной фиче не могли распространиться на остальные части Metaserver'а.
Мы оценили возможность применения готовых решений для запуска платформы. Чтобы снизить риск во время миграции и обеспечить низкие затраты на проектирование, имело смысл и дальше размещать сервисы на той же платформе оркестровки деплоя, которую использует остальная часть Dropbox.
Было решено удалить кастомные компоненты, такие как прокси-сервер запросов Bandaid, и заменить их на Open Source-системы вроде Envoy, отвечающие нашим потребностям.
4. Технический дизайн
Проект включал несколько ключевых стадий:
Компонентизация:
Разбить кодовую базу на компоненты по функциям, чтобы избежать запутывания в будущем.
Назначить конкретного владельца для каждого компонента, чтобы предотвратить внедрение нового функционала в компонент «посторонним» разработчиками.
Стимулировать сокращение числа общих библиотек и совместное использование кода с помощью RPC.
Оркестровка:
Реализовать механизм автоматического преобразования компонента в сервис на платформе оркестровки deployment'ов с помощью <50 строк шаблонного кода.
Настроить прокси-сервер (Envoy) на отправку запросов в рамках определенного сценария в нужный сервис вместо того, чтобы просто отправлять все запросы на узел Metaserver.
Настроить сервисы на общение друг с другом с помощью gRPC вместо HTTP.
Операционализация:
Обеспечить автоматическую настройку пайплайна для каждого компонента с ежедневным запуском и доставкой кода в production.
Настроить автоматические предупреждения и анализ регрессий для каждого push-пайплайна, обеспечив автоматическую приостановку и откат в случае возникновения проблем.
Реализовать автоматическое выделение дополнительных хостов для каждого компонента в зависимости от трафика (с помощью autoscaler'а).
Теперь давайте подробно рассмотрим каждую из этих стадий.
4.1. Компонентизация
Логическая группировка route'ов с помощью сервлетов
В рамках Atlas'а были реализованы так называемые Atlasservlets — логические, атомарные группировки route'ов. Например, Atlasservlet с именем home содержит все route'ы, необходимые для построения домашней страницы. Atlasservlet с именем nav — все route'ы, задействованные в навигационной панели на сайте Dropbox.
Консультации с продуктовыми командами позволили определить необходимое количество Atlasservlet'ов, чтобы охватить каждый route в Metaserver'е. В результате было создано более 200 Atlasservlet'ов для 5000+ route'ов. Atlasservlet'ы оказались незаменимым инструментом для разбиения Metaserver'а.
Структура кода Atlas по сервлетам:
//atlas/home/ …
//atlas/nav/ …
//atlas/<some other atlasservlet>/ …
Каждый Atlasservlet получает отдельную приватную директорию в кодовой базе. Владелец Atlasservlet'а имеет полные права на эту директорию; он может устроить ее по своему усмотрению, и никто другой не сможет импортировать из этой директории. Структура кода Atlasservlet самой своей природой разрушает монолит Metaserver, требуя, чтобы каждый endpoint находился в отдельной приватной директории. В результате совместное использование кода становится осознанным выбором, а не побочным эффектом от развития монолита.
То, что Atlasservlet является частью пути к директории, также позволяет автоматически генерировать production-конфиги, обычно связанные с production-сервисом. Dropbox использует сборщик Bazel для серверного кода. Запрет импорта был реализован с помощью так называемых «правил видимости» (visibility rules) в Bazel. Они позволяют владельцам библиотек контролировать, какой код может использовать эти библиотеки.
Разрыв циклов импорта
Чтобы разбить кодовую базу на части, потребовалось разорвать большинство циклов импорта в Python'e. На это ушло несколько лет; потребовалось написать кучу скриптов, проделать большую работу и провести серьезный рефакторинг кода. Предотвратить регрессии и возникновение новых циклов импорта помогли все те же правила видимости в Bazel.
4.2. Оркестровка
В Atlas каждый Atlasservlet является отдельным кластером. Это дает три важных преимущества:
Изоляция по умолчанию. Некорректно работающий route будет влиять только на другие route'ы в том же Atlasservlet'е (а он в любом случае принадлежит той же команде).
Независимые push'и. Доставка кода в рамках отдельного Atlasservlet'а может проводиться независимо, что позволяет разработчикам контролировать порядок и согласованность push'ей.
Согласованность. Каждый Atlasservlet выглядит и ведет себя как любой другой внутренний сервис в Dropbox. Поэтому все инструменты, предоставляемые нашими инфраструктурными командами — например, периодическое профилирование производительности — будут работать для всех Atlasservlets'ов.
gRPC-стек
Одной из целей при разработке Atlas'а была унификация серверной инфраструктуры. В итоге выбор пал на gRPC — широко используемый в Dropbox инструмент. Встроенная в Envoy (прокси-сервер и балансировщик нагрузки) функция транскодирования gRPC-HTTP позволила и дальше обслуживать HTTP-трафик. Подробности о внедрении gRPC и Envoy в Dropbox можно почерпнуть из соответствующих публикаций (1, 2).
Специально разработанный адаптер должен был облегчить переход на gRPC. Он берет существующий endpoint и преобразует ее в gRPC-совместимый интерфейс, попутно приводя состояние in-memory в вид, соответствующий ожиданиям endpoint’а. Это позволило нам автоматизировать большую часть изменений кода в процессе миграции. Дополнительным плюсом стало то, что в результате endpoint'ы оказались совместимы как с Metaserver'ом, так и с Atlas'ом, поэтому в процессе миграции мы могли безопасно переключать трафик между реализациями.
4.3. Операционализация
Преимущества managed-решения — это секретный ингредиент Atlas'а. Разработчики могут сосредоточиться на основной задаче, не отвлекаясь на различные эксплуатационные аспекты, связанные с работой сервиса в production. При этом сохраняется большинство преимуществ, присущих независимым сервисам (вроде изоляции).
Очевидным недостатком является то, что одна команда теперь отвечает за эксплуатацию более 200 кластеров. Поэтому в рамках проекта Atlas были разработаны различные инструменты для эффективного управления этими кластерами.
Автоматизированный анализ канареек
Metaserver (а следовательно и Atlas) является stateless-системой. Поэтому изменение в коде — одна из наиболее распространенных причин сбоя в работе. Внимательный подход к настройке соответствующих защитных механизмом позволит исключить большинство проблемных сценариев.
Простой сервис для анализа канареечных развертываний (по типу Netflix Kayenta) позволил автоматизировать проведение тестовых выкатов для проверок на наличие ошибок. Каждый сервис Atlas состоит из трех deployment'ов: canary, control и production. При этом canary и control получают лишь небольшой случайный процент трафика. Во время очередного push'а canary перезапускается с новейшей версией ПО. Одновременно control перезапускается со старой версией кода — тем самым обеспечиваются одинаковые начальные условия.
Далее автоматически сравниваются показатели, такие как загрузка процессора и доступность route'ов, для canary и control. Так можно выявить моменты, когда canary уступает по производительности control. Если все идет по плану, canary работает либо наравне с control, либо превосходит его по производительности, и новый код продолжает продвигаться к production. В ином случае push будет автоматически остановлен, а его владельцы получат соответствующее уведомление.
В дополнение к анализу канареечных развертываний оценить новый код нам помогают правильно настроенные уведомления. Они проверяются на протяжении всего процесса (в том числе, в перерывах между push'ами в canary, control и production в рамках одного кластера). Это позволяет автоматически приостановить и откатить push-пайплайн, если что-то пошло не так.
Увы, ошибки все равно случаются. «Плохой» код все же может проскользнуть мимо всех защитных механизмов. В таких случаях на выручку приходит встроенная в Atlas изоляция. «Плохой» код повлияет только на один кластер. Его можно будет откатить в индивидуальном порядке, не мешая работе остальных команд.
Автоматическое масштабирование и планирование мощностей
Стратегия кластеризации Atlas'а приводит к появлению большого числа небольших кластеров. Это хорошо для изоляции, однако значительно сокращает запас, которым располагает каждый кластер при увеличении трафика. Монолиты — это глобальные совместные кластеры, поэтому незначительное увеличение RPS для определенного route'а легко поглощается общим кластером. Но в условиях, когда каждый Atlasservlet является независимым сервисом, с 10-кратным увеличением нагрузки на route справиться гораздо сложнее.
Планирование мощностей для 200 с лишним кластеров стало бы непосильной задачей для команды. Поэтому была реализована система автоматического масштабирования. Автоскейлер в реальном времени следит за использованием кластеров и автоматически распределяет мощности, обеспечивая запас минимум в 40% для каждого кластера. Это позволяет справляться с наплывами трафика, а также устраняет необходимость в планировании мощностей.
Система автоматического масштабирования получает метрики от Load Reporting Service Envoy'я и использует длину очереди запросов для определения размера кластера (думаем, она заслуживает отдельной статьи).
5. Реализация
Ступени, а не вехи
Неоднократные предыдущие попытки улучшить Metaserver проваливались из-за размера и сложности кодовой базы. В этот раз мы были твердо настроены принести пользу разработчикам, даже если не получится полностью заменить Metaserver на Atlas.
План реализации предусматривал несколько ступеней [stepping stones], а не вех [milestones] — все это прекрасно описал в своей статье James Cowling, бывший инженер Dropbox. Таким образом, каждый шаг должен был принести определенную пользу, даже если следующий за ним провалился по какой-либо причине.
Несколько примеров:
В первую очередь была ускорена работа тестовых фреймворков в Metaserver'е, поскольку было понятно, что использование стека Atlas'а в тестах может замедлить их выполнение.
При переходе с Metaserver'а на Atlas возникла необходимость значительно повысить эффективность использования памяти и сократить количество OOM kills, разместив на хосте больше процессов и снизив потребление ресурсов во время миграции. В итоге мы сконцентрировались на повышении эффективности памяти только для Metaserver'а вместо того, чтобы связывать улучшения с выкатом Atlas'а.
Был разработан нагрузочный тест, чтобы доказать, что Atlas MVP сможет обрабатывать трафик Metaserver'а. Соответственно, в рамках другого проекта этот же тест был использован для оценки производительности Metaserver'а на новом оборудовании.
Упрощения для рабочего процесса по Atlas были по-максимуму бэкпортированы в Metaserver. Например, некоторые усовершенствования были перенесены на процессы по части веба в Metaserver.
Рабочие процессы по разработке Metaserver'а делятся на три категории в зависимости от протокола: веб, API и внутренний gRPC. Изначально Atlas был нацелен на внутренний gRPC. Это позволило снизить риски для нового стека без необходимости реализации потенциально более опасных частей, таких как транскодирование gRPC-HTTP. Это, в свою очередь, позволило улучшить рабочие процессы для внутреннего gRPC независимо от остальных рискованных частей Atlas'а.
Препятствия
Учитывая масштаб миграции, неудивительно, что возникло множество проблем. Они вполне заслуживают отдельной статьи. Вот несколько наиболее интересных из них:
В некоторых случаях старый HTTP-стек вел себя странным, удивительным и трудновоспроизводимым образом. Все это причудливое поведение пришлось переносить, чтобы избежать регрессий. Чтобы безопасно провести миграцию, пришлось внимательно изучать оригинальный исходный код, использовать функции из устаревшей библиотеки (там, где это нужно), полагаться на разнообразные существующие интеграционные тесты и разрабатывать главный набор тестов, которые побайтно сравнивают выходные данные старых и новых систем.
В то время как разделение Metaserver'а благоприятно сказалось на production, было совершенно нецелесообразно запускать 200+ Python-процессов в системе интеграционного тестирования. Поэтому для целей разработки и тестирования они были объединены обратно в монолит. Также была проведена тесная интеграция с правилами Bazel. В результате это объединение происходило за кулисами, а разработчики могли обращаться к Atlasservlets как к обычным сервисам.
Разбиение Metaserver'а в production обнажило целый пласт проблем, которые сложно обнаружить во время тестирования. Например, в некоторых инфраструктурных сервисах ID Metaserver'а был за'hardcode'н для контроля доступа. Чтобы минимизировать риск сбоев, был разработан подробный и поэтапный план миграции с четким пониманием рисков на каждом этапе. Все метрики тщательно отслеживались по мере развертывания новой системы.
Инженерные рабочие процессы в Metaserver'е органически росли вместе с монолитом, дойдя до состояния, когда для выполнения простейшей работы инженерам приходилось погружаться в огромное количество деталей. Чтобы убедиться, что Atlas правильно расставляет приоритеты и решает основные инженерные проблемы, к проектированию были привлечены ключевые продуктовые разработчики и проведено несколько циклов итераций для составления дорожной карты, реализация которой окончательно решила бы как продуктовые, так и инфраструктурные задачи.
6. Статус
В настоящее время (речь про март 2021 г. — прим. перев.) Atlas обслуживает более 25% трафика Metaserver'а. Оставшиеся миграции проверены с помощью тестов. В скором времени мы намерены полностью отказаться от Metaserver'а.
7. Заключение
Главным выводом из этой многолетней работы является то, что хорошо продуманный код на ранних этапах жизни проекта имеет решающее значение. В противном случае технический долг стремительно нарастает, равно как и сложность кода. Отказ от циклов импорта и разбиение Metaserver'а по фичам, оказались, пожалуй, самой стратегически эффективной частью проекта. Благодаря этому новый код перестал приносить новые проблемы и весь наш код стал более простым для понимания.
Продуманный подход помог разбить монолит и превратить его в managed-платформу. При этом стоит отметить, что монолиты имеют множество преимуществ (см. статью Shopify), и их бездумное дробление на сервисы приводит к увеличению эксплуатационной нагрузки.
Оказалось, что разработчиков не сильно волнует различие между монолитами и сервисами. Им просто нужен наименее хлопотный способ доставки конечной ценности потребителям. Поэтому мы почти не сомневаемся, что managed-платформа, которая устраняет эксплуатационную рутину (вроде планирования мощностей), и при этом обеспечивает максимальную гибкость (вроде быстрых релизов), — это путь в правильном направлении. Чудесно, что отрасль движется к таким платформам!
Благодарности
Atlas — результат совместных усилий большого числа нынешних и бывших сотрудников компании, включая, но не ограничиваясь: Agata Cieplik, Aleksey Kurkin, Andrew Deck, Andrew Lawson, David Zbarsky, Dmitry Kopytkov, Jared Hance, Jeremy Johnson, Jialin Xu, Jukka Lehtosalo, Karandeep Johar, Konstantin Belyalov, Ivan Levkivskyi, Lennart Jansson, Phillip Huang, Pranay Sowdaboina, Pranesh Pandurangan, Ruslan Nigmatullin, Taylor McIntyre и Yi-Shu Tai.
P. S. от переводчика
Читайте также в нашем блоге:
«Модули, монолиты и микросервисы»;
«Почему в InVision затаскивают микросервисы обратно в монолит»;
«Микросервисы: размер имеет значение, даже если у вас Kubernetes».