Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go. api gateway.. api gateway. caddy.. api gateway. caddy. fiber.. api gateway. caddy. fiber. gin.. api gateway. caddy. fiber. gin. Go.. api gateway. caddy. fiber. gin. Go. golang.. api gateway. caddy. fiber. gin. Go. golang. grpc.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура. mailion.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура. mailion. Блог компании МойОфис.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура. mailion. Блог компании МойОфис. высоконагруженные системы.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура. mailion. Блог компании МойОфис. высоконагруженные системы. мойофис.. api gateway. caddy. fiber. gin. Go. golang. grpc. grpc-gateway. IT-инфраструктура. mailion. Блог компании МойОфис. высоконагруженные системы. мойофис. Программирование.
Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 1

Всем привет! Меня зовут Герман Кравец, я больше десяти лет в IT. В МойОфис работаю руководителем группы Календаря в отделе разработки Mailion — это наша отказоустойчивая корпоративная почта для крупного бизнеса.

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

Будет немного боли, немного архитектуры и чуть-чуть магии. Если вам интересно, как решать нетривиальные задачи в продуктовой разработке, где стоит использовать готовые решения, а где всё писать вручную, или просто хочется узнать, как мы сократили простои на регрессе с 4–6 часов до пары минут, — добро пожаловать под кат!


Ключевые особенности API Gateway

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

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

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

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

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

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

API Gateway и мы

Так у нас выглядела архитектура до того, как мы занялись поиском нового решения.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 2

С внешним миром API Gateway взаимодействует через HTTP и WebSockets, а внутри это набор плагинов, сгенерированных на основе прото-файлов, написанных вручную, и всей структуры системы. Когда мы начали искать новое решение, стало очевидно: плагинов накопилось прилично: раздача клиентской статики, работа с картинками, файлами, их было около восьми, не считая десятков вспомогательных. Всё общение между сервисами шло по gRPC, и таких сервисов в системе насчитывалось больше семидесяти. Их все нужно было как-то безопасно и стабильно опубликовать наружу.

Как мы пришли к жизни такой? Проект Gateway мы начали разрабатывать примерно в 2017 году. Основным веб-сервисом выбрали Caddy — тогда это был довольно мощный инструмент с гибкой системой плагинов и возможностью писать свои. Мы вручную написали шестнадцать активных плагинов, по сути, шестнадцать отдельных репозиториев с разным уровнем вложенности и зависимостей. А вдобавок создали мощный инструмент, который генерировал эти плагины из proto. Можно сказать, что это был отдельный продукт, заточенный именно под генерацию плагинов для Caddy.

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

Во-первых, поддержка HTTP/2: мы активно используем gRPC и gRPC-стримы, в том числе на клиентской стороне, и наличие полноценной поддержки протокола тогда было критично.

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

Третий аргумент — Go-ориентированность. Наши бэкендеры все пишут на Go, поэтому порог входа был минимальным. Нужно добавить кастомный функционал — просто написал плагин или форкнул нужный модуль.

Ну и, наконец, раздача статики из коробки. Caddy справлялся с этим не хуже NginX, поэтому выбор выглядел вполне оправданным.

Звучит классно… Так в чём же проблема?

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

Конфигурация тоже доставляла боль. Caddy первой версии использует Caddyfile, нечто вроде псевдо-YAML, и работать с ним было сложно даже для опытных инженеров.
Ситуацию усугубляли C++-зависимости, которые повышали порог вхождения в проект, замедляли скорость сборки как локально,так и в CI/CD.

Когда мы собрали всё это воедино, стало ясно, что система достигла точки, где поддерживать её дальше уже дороже, чем переписать. Мы были слегка в шоке от масштабов накопившихся проблем и поняли, что пора что-то менять.

Начало работ

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

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

Делим статику

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 3

По классике всё разворачивалось в Docker: внутри и Caddy, и наши статики.
Вроде все логично и красиво, но смотришь глубже и взрыв мозга: статики запускаются постепенно, а потом вольюмами запихиваются в веб-сервис.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 4

Этот пайплайн ещё и в Jenkins, в общем, очень больно.

Кроме того, любое изменение или push в одну из статик заставлял пересобираться их все и передеплоиваться полностью вместе с Gateway. То есть на регрессе, когда мы активно стабилизировали продукт, выкатывали фичи, догоняли код фриз и фича фриз, порядка 120 разработчиков фиксили баги, пайплайн на всё это триггерился и API Gateway мог лежать 4-6 часов. От этого люто страдали команды FE, BE и QA. 

Мы решили отделить статику от Gateway и пошли по самому очевидному пути — взяли nginx в качестве базового образа для статики и заодно использовали его как балансировщик. Решение оказалось не только простым, но и прагматичным: nginx уже был согласован с ИБ и юристами, использовался в других командах, а значит — не требовал бюрократии.
Инструмент популярный, сообщество большое, документация понятная, и самое главное, у нас уже были все нужные компетенции. Любой разработчик мог что-то поправить, а команда поддержки кастомизировать конфигурацию прямо на площадке заказчика.

В итоге получилась архитектура, в которой впереди стоит nginx-балансировщик, за ним — API Gateway, а статики живут отдельно и разворачиваются независимо. Никаких вольюмов, никаких общих сборок — каждый компонент выкатывается сам по себе.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 5

Результат почувствовали сразу: мы начали работать по новым пайплайнам, по новой архитектуре, и снизили время deploy с 30 до 2 минут. И боли на регрессе прекратились, потому что пайплайны выкатки стали незаметными, а время простоя API Gateway снизилось до считанных минут.

Обновляем Golang

Мы — продуктовая компания, и у нас есть собственный отдел ИБ, который проверяет все продукты на уязвимости. У коллег есть свои инструменты для анализа, но они не всегда успевают за обновлениями языков и библиотек. Поэтому разрешение на использование новой версии Go мы получаем только тогда, когда их стек готов это переварить.

В этот раз, наконец, дали добро на Go 1.21. Отлично — побежали обновлять сервисы. Всё шло по плану: обновили зависимости, подтянули библиотеки, ничего критичного не меняли, код не трогали. Локальная сборка прошла, запускаем… и сразу ловим панику.

Окей, так быть точно не должно, надо искать, где собака зарыта.

Gateway у нас построен на Caddy v1, а тот, в свою очередь, зависит от ряда библиотек.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 6

Проблема в том, что актуальная open-source версия qTLS поддерживает максимум Go 1.15, и именно на этом уровне начинает рушиться ядро Caddy. Самое неприятное, что паника срабатывает не при компиляции, а только при запуске.

Мы пошли в отладку и довольно быстро докопались до корня: знакомьтесь, функция init() в одной из библиотек.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 7

Внутри — проверки вроде structsEqual для нескольких структур. Если сравнить TLS из стандартной библиотеки с тем, что лежит внутри qTLS, то они совпадают буквально один в один. И сразу возникает закономерный вопрос: зачем вообще делать такое сравнение на этапе инициализации?

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

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 8

На этом моменте стало ясно: заплатками это не вытащить. Нужно всё выносить, перепроверять зависимости и фактически перекапывать Gateway заново, чтобы перейти на новую версию Go и при этом не сломать прод.

Упрощаем архитектуру

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

Пайплайн для одного репозитория занимал около пяти минут: сборка, тесты, сканеры безопасности — полный набор. А из-за вложенности одно изменение на самом нижнем уровне тянуло за собой цепочку пересборок. В итоге простая правка одной строчки кода могла превращаться в полчаса ожидания, пока вся цепочка отрабатывает. Cкорость вывода изменений в прод страдала, а разработчики страдали вместе с ней. Даже не говоря уже о случаях, когда на инфраструктуре что-то падало и весь CI/CD превращался в домино.

В какой-то момент стало очевидно, что нужно выбираться из этого болота. Мы объединили всё в один репозиторий, убрав ненужную вложенность. Теперь каждый пайплайн стабильно выполняется за те же пять минут, но без накопительного эффекта. Всё стало проще, прозрачнее и быстрее.

Радуемся, архитектуру поправили, идём дальше. Пора было искать альтернативы Caddy v1. 

Caddy v2

Первым делом решили проверить очевидное — может, сам Caddy уже эволюционировал. Вбиваем в Google, открываем официальную документацию и сразу видим: есть вторая версия! Да ещё и с поддержкой Go 1.21. Отлично, наконец-то шанс обновиться без костылей.

Начали разбираться. Архитектурно Caddy v2 похож на своего предшественника: тот же модульный подход, тот же Go под капотом, активная разработка. Появилась и приятная новинка — JSON-конфигурация. После их псевдо-YAML в первой версии это просто глоток свежего воздуха.

Главный плюс JSON-конфига — возможность hot-reload: можно обновлять настройки плагинов на лету, без полного перезапуска сервиса. Захотел — подхватил новый конфиг, перезагрузил нужный модуль и продолжаешь работать. Красота.

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

Во-первых, bus-фактор никуда не делся. Один человек из всей команды (а нас больше сотни) изучит новый стек, разберётся в конфигурации, соберёт систему и станет единственной точкой знаний. Дальше классика: отпуск? нельзя. больничный? не вовремя. Горячая пора релиза, и этот человек буквально живёт в деплое. Такой сценарий недопустим, если мы хотим держать стабильный продукт.

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

Дальше, неприятное открытие. В Caddy v1 можно было сделать небольшой костыль: считать исходный config-файл и построчно проверить каждый параметр, вытащить сквозные ссылки между плагинами. В v2, с переходом на JSON, эту возможность убрали, вероятно, из соображений безопасности. В результате стало невозможно реализовать привычный контекст между модулями.

И наконец — порядок инициализации. Если итоговый JSON-файл формируется генератором не в той последовательности, в какой планировалась загрузка модулей, сервис может повести себя непредсказуемо. Иногда просто меняешь два блока местами и всё, поведение при запуске другое.

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

Пишем с нуля

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

Но тут важно не впасть в другую крайность — не делать «всё своё» руками. В начале статьи я уже упоминал, что раньше мы писали собственные генераторы, которые создавали плагины для Caddy прямо из прото-файлов на лету. Эти генераторы со временем разрослись до состояния отдельных монстров — по объёму кода они превосходили большинство наших микросервисов.

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

Ключевые критерии были простые: стабильность, высокая скорость обработки запросов, нормальные бенчмарки и адекватное поведение под нагрузкой. Пусть сейчас Gateway не был узким местом по RPS — мы хотели предусмотреть запас на будущее.

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 9

Выбор в итоге пал на Fiber. 

Fiber

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

Fiber, наоборот, подошёл идеально по балансу «готовое / контролируемое». Это, по сути, аналог Express.js в мире Go: простой, понятный, с нормальной документацией и логичной архитектурой. Порог вхождения низкий — любой разработчик может быстро разобраться, а коллеги с фронтенда даже получили возможность при необходимости зайти, поправить заголовки или плагин вручную, не залезая в дебри бэкенда.

Fiber оказался лаконичным и предсказуемым, а документация — человеческой: всё описано, читается легко, без копания в исходниках. Построив архитектуру на нём, мы сразу выиграли в читаемости и поддерживаемости кода.

Плюс, JSON-конфигурация у Fiber оказалась очень близка к тому, как устроены наши остальные gRPC-сервисы. В Caddy-файлах синтаксис был уникальный и никак не стыковался с инфраструктурой продукта, а тут всё единообразно: те же структуры, те же подходы. Это важно не только для разработчиков, но и для DevOps-ов и сопровождения — всё знакомо, всё на автомате. Мы не изобретаем велосипед, а просто берём то, что уже хорошо работает у соседей.

Начали тестировать и сразу влетели в проблему. Мы активно используем стримы, и примерно половина клиентских запросов работает именно через них. Первые тесты шли нормально: запросы отрабатывали, ответы возвращались, всё красиво.

Но потом — бах. На десятом, пятидесятом или сотом запросе (зависело от случая) страница переставала отвечать. Проверяем — сервис упал в панику. Проблема воспроизводилась хаотично: иногда с первого запроса, иногда только после сотого.

Разбор показал: Fiber не поддерживает HTTP/2, а у нас стримы как раз шли поверх него, через обвязку gRPC Gateway. В результате — несовпадение дескрипшенов запросов на уровне ядра net/http, и сервер просто рушился.

На этом этапе стало ясно: починить такое в лоб не получится. Мы снова оказались у развилки и пошли искать другой фреймворк, который умеет работать с HTTP/2 из коробки.

Gin

Когда начали искать фреймворк с нормальной поддержкой HTTP/2, вариантов оказалось не так уж много. После серии тестов и чтения исходников остановились на Gin.

Из всех кандидатов у него оказались лучшие документация и комьюнити, внятная архитектура и богатый набор middleware из коробки. Порог вхождения низкий даже для тех, кто раньше с ним не работал. Да, по RPS Gin немного проседает по сравнению с Fiber, но для нас это не критично: Gateway никогда не был узким местом по производительности, зато стабильность и поддерживаемость для нас приоритет.

Интегрировав Gin, запустили всё прекрасно. Но наши приключения на этом не закончились. Разработчику всегда попадётся на глаза что-то, что нужно подрефачить. 

Рефакторинг

У нас было большое дублирование соединений. Каждый плагин по идеологии Caddy — это инкапсулированная, изолированная единица. Поэтому, чтобы прокинуть наши gRPC-соединения, их приходилось дублировать в каждом плагине и каждой конфигурации, хотя по факту все они стучались в один и тот же сервис. Мы сделали единую точку — фабрику соединений, которая по запросу «дай мне соединение к такому-то сервису» проверяет: если соединения нет — создаёт его, если есть — просто переиспользует и отдаёт плагину. Так мы сократили количество соединений по ключевым сервисам с восьми до одного, что в будущем заметно снизит нагрузку.

C++-зависимость была нашей болью на протяжении всего существования Gateway. Это одна из причин, почему при виде задач по этому проекту разработчики думали: «О нет, только не он». Ни нормального readme, ни инструкций: запускаешь и получаешь сообщение «Отдай мне библиотеку». На Linux это ещё можно было пережить — где-то в Confluence можно найти, что именно нужно поставить. А вот на Mac библиотек просто нет, и приходилось проходить через эту боль вручную.

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

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 10

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

Так у вас статика вольюмами маунтится! И другие весёлые приключения в поисках нового Gateway на Go - 11

Мы посмотрели внимательнее — действительно ли всё это нужно, ведь мы работаем по gRPC? Открыли прото соседа и обнаружили метод, который полностью закрывал нашу потребность. Мы удалили зависимость, переписали обработчик так, чтобы он просто вызывал этот метод через gRPC-стрим, и всё заработало. 40–50 строк кода заменились несколькими строками, ушла боль с C++, сборка стала заметно быстрее, а настройка проще. Теперь проект собирается чистым Go, без лишних зависимостей, а разработчики наконец могут просто запустить, скомпилировать и работать без шаманства.

Приятные плюшки

Первое, что мы почувствовали, — ушёл bus-фактор. Теперь любой бэкенд-разработчик в команде может спокойно запустить наш Gateway локально и разобраться, как он работает.
Никаких «коробочных» ограничений, странных зависимостей и «магии». Код открыт, структурирован, модули логично разбиты по бизнес-областям. Если нужно что-то поправить — просто проваливаешься в нужный блок и сразу понимаешь, где внести изменения.

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

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

Теперь всё иначе. Мы собрали локальный конфиг из примерно 200 строк — только базовые блоки: HTTP, аутентификация и авторизация. Всё остальное стало управляемым: если плагин нужен, то включаешь его в конфиг, не нужен — просто не указываешь.

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

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

Мы также интегрировались с корпоративным PaaS-решением, которое коллеги из другой команды используют для централизованной работы с логами и трейсам. Раньше Gateway мешал полноценной интеграции: именно он был входной точкой для трейсов, а из-за ограничений старой версии Go мы не могли подключить нужные SDK. После обновления языка и переписывания Gateway мы наконец подтянули нужный пакет и успешно встали в общую систему мониторинга.

И, наконец, мы избавились от CVE. История получилась показательной. Пока мы активно рефакторили Gateway, не спешили его выкатывать в релизы — и вовремя: через один релиз коллеги из ИБ сообщили, что найденные уязвимости в Go 1.19 получили критичный статус. Это означало риск блокировки релиза. К счастью, к тому моменту мы уже были готовы с новой версией Gateway и в следующий релиз ушли в прод именно с ним, полностью закрыв проблему.


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

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

Автор: germankravec

Источник

Rambler's Top100