Введение: В Nitro нам нужен профессиональный балансировщик нагрузки. После некоторых исследований мы с Михаем Тодором создали решение для маршрутизатора на основе Nginx, протокола Redis с использованием Go, где nginx выполняет всю тяжелую работу, а сам маршрутизатор не передает трафик. Это решение безотказно работало в производстве в течение прошлого года. Вот что мы делаем и почему мы это делаем.
Почему
Новый сервис, который мы создаем, будет располагаться за пулом балансировки нагрузки и выполнять дорогостоящие вычислительные задачи, поэтому нам необходимо локальное кэширование. Для оптимизации кеша мы хотим попытаться отправить запросы на один и тот же ресурс на один и тот же хост (если этот хост доступен).
Существует много существующих решений этой проблемы, ниже приводится неполный список:
-
Используйте файлы cookie для сохранения закрепленных сессий
-
Использовать заголовок
-
Прилипчивость на основе исходного IP
-
HTTP-перенаправление на правильный экземпляр
Эта служба будет запускаться несколько раз при каждой загрузке страницы, поэтому переадресация HTTP невозможна из соображений производительности. Несколько оставшихся решений работают нормально, если все входящие запросы проходят через один и тот же балансировщик нагрузки. С другой стороны, если ваш интерфейс представляет собой пул балансировщиков нагрузки, вам необходимо иметь возможность обмениваться состоянием между ними или реализовывать сложную логику маршрутизации. Нас не интересуют текущие проекты, которые требуют, чтобы изменения состояния распределялись между балансировщиками нагрузки, поэтому мы выбрали более сложную логику маршрутизации для этой службы.
наша архитектура
Взглянув на нашу архитектуру дизайна, вы сможете лучше понять наши намерения.
У нас есть набор интерфейсных балансировщиков нагрузки, и экземпляры этих сервисов развернуты в Mesos для входящего и исходящего контроля в зависимости от масштаба сервиса и доступности ресурсов. Поместить список хостов и номеров портов в балансировщик нагрузки не проблема, это стало ядром нашей платформы.
Поскольку все работает на Mesos, и у нас есть простой способ определения и развертывания сервисов, добавлять любые новые сервисы очень просто.
Помимо Mesos, мы повсюду запускаем Sidecars на основе сплетен, чтобы управлять обнаружением сервисов. Наш внешний балансировщик нагрузки состоит из Lyft Envoy, который поддерживается интеграцией Sidecar Envoy. Это удовлетворяет потребности большинства служб. Хосты Envoy работают на выделенных экземплярах, но все сервисы переносятся между хостами по мере необходимости и выполняются планировщиками Mesos и Sigualarity.
Сервисные узлы Mesos, которые все еще находятся на рассмотрении, будут иметь дисковые локальные кэши.
дизайн
Глядя на эту проблему, мы решили, что нам действительно нужно последовательное хеш-кольцо. Мы можем позволить узлам управлять входом и выходом по мере необходимости, и только запросы, обслуживаемые этими узлами, будут перенаправляться. Все оставшиеся узлы продолжат обслуживать любые открытые сеансы. Мы можем легко поддерживать согласованные хеш-кольца с данными Sidecar (вместо этого вы можете использовать Mesos или k8s). Узлы проверки работоспособности Sidecar, мы можем положиться на эти узлы проверки работоспособности, чтобы определить, правильно ли они работают в Sidecar.
Затем нам нужен какой-то согласованный метод хеширования, чтобы направлять трафик на правильные узлы. Он должен получать каждый запрос, идентифицировать проблемный ресурс, а затем передавать запрос другим экземплярам службы, которые готовы обработать этот ресурс.
Конечно, идентификация ресурсов может осуществляться просто по URL-адресу, и любой балансировщик нагрузки может разделить их для простой маршрутизации. Так что нам просто нужно связать их с согласованными хэшами, для которых у нас уже есть решение.
Вы можете сделать это с помощью lua в nginx или lua в HAproxy. В Nitro никто из нас не является экспертом в Lua, и, очевидно, нет библиотеки, которая делает то, что нам нужно. В идеале логика маршрутизации должна быть реализована на Go, который является ключевым языком в нашем стеке технологий и хорошо поддерживается.
Nginx имеет богатую экологическую среду, и идея выйти за рамки нормы также привела к появлению некоторых очень интересных подключаемых модулей nginx. Предпочтительным среди этих плагинов является nginx-eval-module Валерия Холодько. Этот плагин позволяет вам сделать вызов из nginx в конечную точку и оценить возвращенный результат как переменную nginx. Среди других возможных ролей значение этого плагина заключается в том, что он позволяет вам динамически решать, какая конечная точка должна получать доставку через прокси. Это то, что мы хотим сделать. Вы делаете вызов из Ngnix где-то, и после получения результата вы можете принимать решения о маршрутизации на основе возвращаемого значения результата. Вы можете реализовать приемник этого запроса с помощью службы HTTP. Служба просто возвращает строку имени хоста и номера порта конечной точки целевого сервера. Этот сервис всегда поддерживает согласованный хэш и сообщает Nginx, куда направлять трафик каждого запроса, но выполнение одного HTTP-запроса по-прежнему немного громоздко. Весь ожидаемый ответ будет строкой 10.10.10.5:23453. С HTTP мы передаем заголовки в обоих направлениях, что значительно превышает размер тела ответа.
Поэтому я начал изучать другие протоколы, поддерживаемые Nginx, и обнаружил, что он поддерживает как протокол memcache, так и протокол redis. Среди них наиболее дружественной поддержкой сервисов Go является протокол Redis. Вот где мы собираемся улучшить. В Nginx есть два модуля Redis, один подходит для использования через nginx-eval-module. Лучшей библиотекой для реализации языка Redis Go является Redeo. Rodeo реализует чрезвычайно простой механизм обработки, очень похожий на пакет http в стандартной библиотеке go. Любая команда протокола redis будет содержать функцию-обработчик, и ее написание очень простое. По сравнению с плагином Nginx он может работать с более новыми версиями протокола Redis. Итак, я отказался от своих навыков C и дополнил плагин Nginx, чтобы использовать последнюю кодировку протокола Redis.
Итак, наше последнее решение:
Этот вызов поступает из общедоступной сети, запускает узел Envoy, а затем переходит к узлу Nginx.Узел Nginx (1) спрашивает маршрутизатор, куда отправить запрос. Затем узел Nginx (2) отправляет запрос в указанную конечную точку службы.
выполнить
Мы создали библиотеку на Go для управления последовательным хэшированием, поддерживаемую Sidecar или библиотекой Memberlist от Hashicorp. Мы называем это библиотекой Рингмана. Затем мы принудительно подключаем библиотеку к службе, запрашиваемой протоколом Redis, поддерживаемым библиотекой Redeo.
Для этой схемы требуются только две команды Redis: GET и SELECT. Мы решили реализовать некоторые команды для отладки, в том числе INFO, которые могут отвечать любым состоянием сервера, которое вы хотите. Из двух обязательных команд мы можем смело игнорировать SELECT, поскольку для любых последующих вызовов выбирается Redis DB. Мы просто принимаем это и ничего не делаем. GET упрощает всю работу. Ниже представлена полная функциональность для обслуживания конечных точек Ringman через Redis и Redeo. Nginx передаст полученный URL-адрес и вернет конечную точку из хеш-кольца.
Это вызов Nginx со следующей конфигурацией:
Мы вызываем маршруты в Nginx и контейнере и позволяем им работать на одном хосте, чтобы мы могли реализовывать там более дешевые вызовы.
Вот Nginx, который мы построили:
представление
Мы провели детальное тестирование производительности в нашей собственной среде и увидели, что среднее время отклика от Nginx до маршрутизатора Go по протоколу Redis составляет около 0,2–0,3 мс. Поскольку среднее время отклика от восходящих служб составляет около 70 мс, это незначительная задержка.
Более сложная конфигурация Nginx может выполнять более сложную обработку ошибок. Сервис работает уже больше года, надежность очень хорошая, а производительность очень стабильная.
заключительные замечания
Если у вас схожие потребности, вы можете повторно использовать большинство компонентов. Просто перейдите по ссылке выше, чтобы перейти к фактическому исходному коду. Если вы заинтересованы в добавлении поддержки K8s или Mesos непосредственно в Ringman, мы будем более чем рады.
Это решение звучит немного хакерски, но в итоге оно стало отличным дополнением к нашей инфраструктуре. Надеюсь, это поможет кому-то еще с аналогичной проблемой.
Автор этой статьи Карл Матиас, перевод Ван Хэ. Для перепечатанных переводов, пожалуйста, укажите источник, техническую оригинальность и статьи об архитектурной практике, и вы можете представить материалы через меню официального аккаунта «Связаться с нами».
Связанное чтение:
Оружие Service Mesh: NGINX будет поддерживать gRPC
Express | Доля Nginx на рынке веб-серверов составляет 33,3%, а доля Apache упала ниже 50%.
Предстоящие События:
С 1 по 2 июня в Шэньчжэне пройдет конференция GIAC Global Internet Architecture. GIAC — это конференция по технической архитектуре для архитекторов, технических лидеров и высококлассных специалистов-практиков, организованная сообществом технологий архитектуры высокой доступности. В этом году в GIAC уже вошли Tencent, Alibaba, Baidu, Toutiao, iFLYTEK, Sina Weibo, Xiaomi, Meitu, Oracle, Lianjia, Vipshop, JD.com, Ele.me, Meituan Dianping, Luojithinking, ofo, презрение, LinkedIn,Pivotal Дождитесь прибытия специалистов компании.
Вот некоторые из интересных тем на этой конференции GIAC:
Примите участие в GIAC, чтобы ознакомиться с новейшими технологиями 2018 года. Нажмите "читать оригинал"Подробнее о конференции