Все мы знаем, как трудно управлять современными приложениями, которые состоят из микросервисов со сложным взаимодействием между ними. Причём управлять одними только микросервисами будет недостаточно, нужен комплексный подход. В этой статье мы под разными углами рассмотрим управление сервисами и узнаем, как решать эти задачи с помощью service mesh, Istio, eBPF и RSocket Broker. Микросервисы должны слаженно работать вместе, чтобы приложение выполняло задуманные бизнес-задачи. Сервисы не должны выходить за определённые нами границы контекста. Мы управляем сервисами с разных сторон: Регистрация и обнаружение сервисов: Consul, ZooKeeper Конфигурация сервисов: Spring Cloud Config Взаимодействие между сервисами: Hystrix Шлюз: Zuul, Spring Cloud Gateway Балансировка нагрузки: Ribbon, Feign Отслеживание: Sleuth, Zipkin, Htrace Мониторинг: Grafana, Promethues Чуть позже мы обсудим некоторые из этих инструментов. Для эффективной работы в облаке монолитные приложения делят на небольшие фрагменты — микросервисы, которые можно развёртывать независимо друг от друга. Когда один сервис хочет пообщаться с другим, ему нужно узнать IP-адрес и номер порта своего будущего собеседника. Можно, конечно, просто создать файл конфигурации, где будут записаны IP-адреса и порты всех целевых сервисов, но у этого подхода много недостатков. Например он плохо масштабируется, что противоречит самой сути облака, где количество экземпляров серверов можно менять в зависимости от текущей нагрузки. Эту задачу можно решить с помощью обнаружения сервисов. Когда новый сервис готов обслуживать клиентские запросы, он регистрируется в реестре сервисов, сообщая свой IP и номер порта. Клиенты обращаются к реестру сервисов, чтобы получить эту информацию. Кроме того, мониторинг работоспособности позволяет направлять трафик только к здоровым экземплярам. Балансировка нагрузки — это стратегия планирования запросов, при которой мы координируем несколько серверов для эффективного распределения поступающих запросов. Ниже мы посмотрим, как интеллектуально распределять клиентские запросы по пулам серверов с помощью RSocket. Некоторые функции приложения, не связанные с бизнес-логикой, можно выделить в так называемые sidecar’ы, при этом сократив и упростив код. Sidecar-контейнер, развёрнутый рядом с сервисом, скажем, в одном поде в Kubernetes, может взять на себя наблюдаемость, мониторинг, логирование, конфигурация и размыкатели цепи (circuit breaker). В режиме sidecar прокси-контейнеры делят с контейнерами приложения в этом поде пространство имён сети. Пространство имён сети — это структура ядра Linux, благодаря которой у контейнеров и подов могут быть свои независимые сетевые стеки, изолирующие друг от друга контейнеризованные приложения. Поэтому сколько угодно подов могут выполнять веб-приложение и использовать порт 80. У прокси будет то же пространство имён сети, так что он сможет перехватывать и обрабатывать входящий и исходящий трафик контейнера приложения. В микросервисной архитектуре мы можем объединять несколько модулей с одной задачей и функцией, чтобы создавать очень большие и сложные приложения. Сначала микросервисы использовали внутренний SDK для обнаружения сервисов, повторных запросов и других функций (например, Spring Cloud). Это серьёзно усложняло жизнь разработчикам, которым приходилось включать SDK в программный стек и использовать ограниченный набор языков программирования, не говоря уже о сложностях обновления и развёртывания, нарушениях принципов agility (при обновлении SDK приходилось обновлять и приложение), фрагментации версий и необходимости получать новые навыки и знания. Новые версии приложений приходилось выпускать даже тогда, когда бизнес-логика совсем не менялась, потому что нефункциональный код был неотделим от бизнес-кода. Service mesh — это инфраструктурный уровень, где обрабатываются коммуникации между сервисами через sidecar, который служит как прозрачный прокси для безопасного, быстрого и надёжного обмена данными. При использовании service mesh мы можем абстрагировать от приложения большую часть возможностей SDK, упаковать их в независимые контейнеры и развернуть как sidecar’ы. Когда задачи по управлению сервисами лежат на инфраструктуре, сами микросервисы выполняют только бизнес-логику. Команда по инфраструктуре занимается разными общими функциями, а мы получаем возможности независимого развития, прозрачных апгрейдов и улучшения общей эффективности. Service mesh состоит из двух основных компонентов: control plane и data plane. Две самых популярных платформы service mesh — Istio и Linkerd. В этой статье мы будем рассматривать Istio. У service mesh относительно простая архитектура — пользовательские агенты рядом с сервисами плюс набор процессов по управлению задачами. Агенты составляют data plane, а управляющие процессы — control plane. Data plane перехватывает вызовы между разными сервисами и обрабатывает их, а control plane координирует поведение агентов и предоставляет API или инструменты командной строки, чтобы управлять версиями для конвейера непрерывной интеграции и развёртывания. Сервисы не вызывают друг друга по сети напрямую, а обращаются к своим sidecar’ам, которые берут на себя управление запросами и прочие тонкости коммуникаций. Istio — это  опенсорс-платформа для управления сервисами с помощью service mesh в облачном окружении с Kubernetes. Istio отвечает за балансировку нагрузки, аутентификацию между сервисами, мониторинг и другие задачи. Istio состоит из control plane и data plane. Data plane состоит из агентов, которые развёртываются как sidecar’ы при каждом сервисе. Sidecar перехватывает весь входящий и исходящий трафик сервиса и вместе с control plane применяет к нему настроенные правила и действия. Control plane управляет sidecar’ами на data plane и занимается распределением, обнаружением сервисов, авторизацией, аутентификацией и другими функциями. Благодаря control plane можно согласованно управлять data plane. Давайте рассмотрим главные функции основных компонентов архитектуры Istio. Envoy — это высокопроизводительный агент, написанный на C++. Istio внедряет Envoy в виде sidecar-контейнера к контейнеру приложений, чтобы он перехватывал весь входящий и исходящий трафик сервиса и выполнял с ним разные задачи, включая балансировку нагрузки, размыкание цепи, внесение ошибок, а также предоставлял метрики, скажем, для Prometheus и Jaeger. Все внедрённые агенты вместе образуют data plane. Istiod — это компонент control plane, который отвечает за обнаружение сервисов, конфигурацию и управление сертификатами. Istiod преобразует сложные правила в формате YAML в практическую конфигурацию для Envoy, а затем распространяет эту конфигурацию по всем sidecar’ам в service mesh. Pilot преобразует информацию о конфигурации, например правила маршрутизации, в формат, который будет понятен sidecar’ам, и отправляет её на data plane. Он распространяет конфигурацию и помогает sidecar’ам выполнять задачи по управлению трафиком. Citadel — это компонент Istio, который отвечает за безопасность. Он создаёт сертификаты, чтобы разрешить защищённое mTLS-взаимодействие между агентами на data plane. Gallery — это новый компонент в версии Istio 1.1, который отделяет Pilot от базовой платформы, вроде Kubernetes. Он берет на себя часть исходных функций Pilot и отвечает за верификацию, извлечение и обработку конфигурации. Для входящего и исходящего трафика существуют отдельные процессы маршрутизации. Обработчики входящего трафика пересылают трафик в контейнер своего приложения, а обработчики исходящего ждут исходящий трафик. Istio использует init-контейнер для работы с iptables в пространстве имён сети пода и обработки правил, чтобы входящие и исходящие пакеты передавались в sidecar-прокси пода. Init-контейнер отличается от контейнера приложения: Он запускается до главного контейнера и всегда выполняется до конца. Если init-контейнеров несколько, они выполняются друг за другом. Когда мы говорим о service mesh, мы обычно представляем себе sidecar, управляемый Istio + Envoy. На первый взгляд, это неплохой подход, но не без недостатков: Снижение производительности. Прокси — это отдельное приложение, которое тоже потребляет процессорные ресурсы и оперативную память. Для Envoy обычно требуется около 1 ГБ памяти. Сложная архитектура. Нам нужен control plane, data plane, правила для разных приложений, безопасность взаимодействия между прокси и т. д. Больше расходов на эксплуатацию и обслуживание. Невозможно развернуть sidecar без автоматизированных инструментов. Обычно service mesh работает в Kubernetes, что позволяет сократить нагрузку. Когда мы используем sidecar, нам приходится развёртывать в каждом поде контейнер с нужной конфигурацией. У каждой ноды только одно ядро, и все контейнеры на одной ноде используют это ядро. А нельзя ли в таком случае ограничиться одним sidecar-прокси на ноду? eBPF как раз об этом. Это технология, с помощью которой в ядре можно запускать пользовательские программы в ответ на события. События могут быть самыми разными, включая вход в функцию и выход из неё (в ядре или пользовательском пространстве) или поступление важных сетевых пакетов. Если добавить программу eBPF к событию, она будет срабатывать независимо от того, какой процесс вызвал это событие и где он находится — в контейнере приложения или на хосте. Решения на базе eBPF дают возможности мониторинга, безопасности и взаимодействий без sidecar. 2 декабря 2021 года проект Cilium объявил о программе бета-тестирования Cilium Service Mesh. Cilium использует eBPF и обходится без sidecar’ов для выполнения таких задач, как маршрутизация на L7, балансировка нагрузки, TLS, политики доступа, проверки работоспособности, логирование и трассировка. Что нам это даёт? Когда мы используем sidecar’ы и хотим добавить sidecar-контейнер, нам приходится вносить изменения в YAML с описанием каждого пода приложения. Обычно этот процесс автоматизирован. Например, мы используем модифицирующий вебхук, чтобы внедрить sidecar в момент развёртывания пода приложения. В Istio, например, мы создаём теги для пространства имён Kubernetes и подов, чтобы указать, нужно ли внедрять sidecar. А если что-то пойдёт не так? Если пространство имён или под помечены ошибочно, sidecar не будет внедрён и под не сможет подключиться к service mesh. Более того, если злоумышленник проникнет в кластер и запустит вредоносную рабочую нагрузку, мы её не обнаружим из-за отсутствия наблюдаемости. Если мы используем eBPF, а не sidecar’ы, поды можно обнаруживать без дополнительного кода YAML. Вместо этого мы используем CRD, чтобы настроить service mesh в кластере. Уже запущенный под можно включить в service mesh без перезапуска. Если злоумышленник попытается обойти оркестрацию Kubernetes, запустив рабочую нагрузку прямо на хосте, eBPF обнаружит это действие и возьмёт над ним контроль, потому что из ядра всё видно. В сети с eBPF пакеты могут обходить некоторые сетевые стеки ядра, что позволяет повысить производительность. Давайте посмотрим, как это реализуется на data plane в service mesh. В модели с eBPF и без sidecar’ов путь сетевых пакетов гораздо короче. В service mesh агент выполняется как sidecar в традиционной сети, и путь пакета к приложению неблизкий: входящий пакет проходит через стек TCP/IP хоста и подключается к пространству имён и к поду через виртуальный Ethernet. Оттуда пакет проходит через сетевой стек пода к агенту, а тот уже направляет пакет к приложению через loopback интерфейс. Не забывайте, что трафик проходит через прокси в обе стороны, так что service mesh ощутимо увеличивает задержку. Реализации Kubernetes CNI на основе eBPF, такие как Cilium, могут с помощью программы eBPF и хуков по-умному перенаправлять пакеты по более прямому маршруту. Cilium знает идентификаторы всех конечных точек и сервисов Kubernetes. Когда пакет поступает на хост, Cilium напрямую назначает его нужному прокси или конечной точке пода. Service mesh обычно отвечает за аутентификацию и шифрование всего трафика приложения. При использовании mTLS (взаимного TLS) прокси в service mesh выступают как конечные точки сетевого соединения и договариваются о защищённом TLS-соединении с собеседником. Соединение шифрует взаимодействие между агентами без изменения кода приложения. Реализовать аутентификацию между компонентами и шифрование трафика можно не только с помощью TLS на уровне приложения. Мы можем шифровать трафик на уровне сети, используя IPSec или WireGueard. Шифрование на уровне сети полностью прозрачно не только для приложений, но и для агентов, и его можно включить даже без service mesh. Если service mesh нужна вам только для шифрования, возможно, стоит просто наладить шифрование на уровне сети. Во-первых, так проще, а во-вторых, так мы сможем шифровать любой трафик на нодах, а не только для рабочих нагрузок с sidecar’ом. RSocket Broker — это система коммуникации между самыми разными приложениями по протоколу RSocket. Как работает RSocket Broker? Запрашивающий сервис (Requester) инициирует вызов к брокеру, брокер перенаправляет запрос отвечающему сервису (Responder), а потом возвращает запрашивающему сервису полученный результат. Когда отвечающий сервис запускается, он активно создаёт долгосрочное TCP-соединение с брокером и передаёт ему список сервисов, которые может обслуживать. Когда запрашивающий сервис запускается, он тоже создаёт с брокером долгосрочное соединение. Когда затем он хочет вызвать удалённый сервис, он инкапсулирует запрос в сообщение с уникальным идентификатором и направляет его брокеру. Брокер получает сообщение, определяет сервис-получатель по доступным метаданным и ищет сервис во внутренней таблице маршрутизации. Когда тот сервис обработает запрос, он тоже инкапсулирует результат в сообщение и отправляет его брокеру. Брокер считывает идентификатор сообщения и пересылает его нужному вызывающему сервису, который применяет соответствующую бизнес-логику в зависимости от ответа. Преимущества обмена сообщениями через брокер: Не нужны дополнительные проверки работоспособности, потому что мы уверены в соединении. Сервис, обрабатывающий запрос, больше не должен прослушивать порт, и этот подход кардинально отличается от HTTP REST API и gRPC и даёт больше безопасности. Запрашивающий и отвечающий сервис не обязаны знать о существовании друг друга. Появляется возможность управления потоком: если нагрузка на отвечающий сервис возрастает, брокер автоматически пересылает сообщение другому сервису с помощью интеллектуальной балансировки нагрузки. Регистрация и обнаружение сервисов теперь не требуют сторонних реестров, вроде Eureka, Consul или ZooKeeper, так что расходы на инфраструктуру ниже. Брокер проверяет права доступа сервисов на обоих концах, и мы развёртываем поддержку TLS только на брокере, чтобы защитить каналы коммуникации. У брокера есть свои минусы. Производительность будет чуть ниже, потому что стороны не общаются друг с другом напрямую. Кроме того, весь трафик перенаправляется через брокер, так что в сети появляется узкое место. Хотя эту проблему можно частично решить, обеспечив высокую надёжность кластера и брокера. Istio Service Mesh сложно реализовать за пределами дата-центров. А как же IoT-устройства? Нужно установить sidecar на каждом смартфоне? Эту задачу можно решить с помощью RSocket Broker. На схеме service mesh на базе RSocket Broker мы не видим ни sidecar, ни дублирования процессов. Давайте сравним характеристики двух архитектур: Уровень инфраструктуры. С одной стороны у нас связка sidecar-прокси + control plane, а с другой — централизованный брокер с интегрированными функциями control plane. Централизованное управление. Все логи, метрики и другая полезная информация находятся в одном месте, что упрощает управление. Протокол связи. Минус использования брокера RSocket в том, что между приложениями нужно использовать протокол RSocket. Доступ к приложениям и устройствам. Не на всех устройствах можно установить прокси, и тогда на выручку приходит схема на основе RSocket. Расходы на эксплуатацию и обслуживание выше. Управлять одним кластером RSocket Broker на 10 серверов — это не то же самое, что управлять 10 000 экземпляров прокси. Эффективность. У RSocket производительность в десять раз выше, чем у HTTP. Безопасность. В RSocket безопасность реализуется проще. Брокер использует TLS + JWT, а не mTLS, так что управлять сертификатами не нужно. При этом благодаря модели безопасности JWT мы получаем более детальный контроль разрешений. При использовании RSocket Broker можно сократить поверхность атаки. Старт практического интенсива по service mesh 24 марта.   Зависимость от инфраструктуры и сети. У RSocket Broker есть большое преимущество над Istio: он работает не только с Kubernetes. Да, считается, что Istio тоже работает с другими платформами, но по факту управлять sidecar-прокси за пределами Kubernetes слишком сложно. Зато RSocket Broker можно развернуть где угодно. Учитывая популярность микросервисных приложений, вопрос управления сервисами стоит довольно остро. В этой статье мы рассмотрели, как управлять сервисами с помощью Istio, eBPF и RSocket Broker. Service mesh — палочка-выручалочка для работы с микросервисами. Этот инструмент обеспечивает прозрачное управление входящим и исходящим трафиком в едином месте. Сегодня о знании service mesh спрашивают при трудоустройстве в таких крупных компаниях как Linkerd, Ozon, Сбер, ЮMoney, МТС, Спортмастер, Газпром и др. На интенсиве по Service mesh вы изучите инструмент на практике и научитесь решать целый ряд проблем, возникающих при работе с микросервисами. Бизнес-кейсы, которые будем решать: Проблема #1. Отсутствие или слабое развитие мониторинга. Периодическое торможение системы, но непонятно, где именно. Проблема #2. Выдача ошибок сервисами, со слов клиентов. Частые падения, хотя и кратковременные. Проблема #3. Нужно выкатить новую фичу, но нет уверенности, что все пойдет как надо. Проблема #4. В системе появляется новый компонент который отвечает за процессинг кредитных карт. Появляются требования к безопасности системы всей системы. Проблема #5. Приложение разрослось. Клиенты жалуются на долгое время ответа. На серверной стороне все ок. Похоже проблема с удаленными геолокациями. Интенсив пройдет с 24 по 26 марта. Вы узнаете о best practices от эксперта, который уже давно работает с технологией. Помимо голой теории и практики, вы получите рекомендации, как делать, а как – нет. Посмотреть программу и записаться : https://slurm.club/3ItpvWY