![](https://habrastorage.org/getpro/habr/upload_files/2d6/413/c51/2d6413c5105b7bf5c9eabe78be185f17.png)
Все мы знаем, как трудно управлять современными приложениями, которые состоят из микросервисов со сложным взаимодействием между ними. Причём управлять одними только микросервисами будет недостаточно, нужен комплексный подход.
В этой статье мы под разными углами рассмотрим управление сервисами и узнаем, как решать эти задачи с помощью service mesh, Istio, eBPF и RSocket Broker.
1. Управление сервисами
Микросервисы должны слаженно работать вместе, чтобы приложение выполняло задуманные бизнес-задачи. Сервисы не должны выходить за определённые нами границы контекста.
Мы управляем сервисами с разных сторон:
Регистрация и обнаружение сервисов: Consul, ZooKeeper
Конфигурация сервисов: Spring Cloud Config
Взаимодействие между сервисами: Hystrix
Шлюз: Zuul, Spring Cloud Gateway
Балансировка нагрузки: Ribbon, Feign
Отслеживание: Sleuth, Zipkin, Htrace
Мониторинг: Grafana, Promethues
Чуть позже мы обсудим некоторые из этих инструментов.
Регистрация и обнаружение сервисов
Для эффективной работы в облаке монолитные приложения делят на небольшие фрагменты — микросервисы, которые можно развёртывать независимо друг от друга.
Когда один сервис хочет пообщаться с другим, ему нужно узнать IP-адрес и номер порта своего будущего собеседника. Можно, конечно, просто создать файл конфигурации, где будут записаны IP-адреса и порты всех целевых сервисов, но у этого подхода много недостатков. Например он плохо масштабируется, что противоречит самой сути облака, где количество экземпляров серверов можно менять в зависимости от текущей нагрузки.
Эту задачу можно решить с помощью обнаружения сервисов. Когда новый сервис готов обслуживать клиентские запросы, он регистрируется в реестре сервисов, сообщая свой IP и номер порта. Клиенты обращаются к реестру сервисов, чтобы получить эту информацию. Кроме того, мониторинг работоспособности позволяет направлять трафик только к здоровым экземплярам.
Балансировка нагрузки
Балансировка нагрузки — это стратегия планирования запросов, при которой мы координируем несколько серверов для эффективного распределения поступающих запросов. Ниже мы посмотрим, как интеллектуально распределять клиентские запросы по пулам серверов с помощью RSocket.
2. Sidecar
Некоторые функции приложения, не связанные с бизнес-логикой, можно выделить в так называемые sidecar’ы, при этом сократив и упростив код. Sidecar-контейнер, развёрнутый рядом с сервисом, скажем, в одном поде в Kubernetes, может взять на себя наблюдаемость, мониторинг, логирование, конфигурация и размыкатели цепи (circuit breaker).
![](https://habrastorage.org/getpro/habr/upload_files/269/076/2a0/2690762a0b0fca4c850b029de4a356b5.jpg)
В режиме sidecar прокси-контейнеры делят с контейнерами приложения в этом поде пространство имён сети. Пространство имён сети — это структура ядра Linux, благодаря которой у контейнеров и подов могут быть свои независимые сетевые стеки, изолирующие друг от друга контейнеризованные приложения. Поэтому сколько угодно подов могут выполнять веб-приложение и использовать порт 80. У прокси будет то же пространство имён сети, так что он сможет перехватывать и обрабатывать входящий и исходящий трафик контейнера приложения.
3. Service mesh
В микросервисной архитектуре мы можем объединять несколько модулей с одной задачей и функцией, чтобы создавать очень большие и сложные приложения.
Сначала микросервисы использовали внутренний SDK для обнаружения сервисов, повторных запросов и других функций (например, Spring Cloud). Это серьёзно усложняло жизнь разработчикам, которым приходилось включать SDK в программный стек и использовать ограниченный набор языков программирования, не говоря уже о сложностях обновления и развёртывания, нарушениях принципов agility (при обновлении SDK приходилось обновлять и приложение), фрагментации версий и необходимости получать новые навыки и знания. Новые версии приложений приходилось выпускать даже тогда, когда бизнес-логика совсем не менялась, потому что нефункциональный код был неотделим от бизнес-кода.
Service mesh
Service mesh — это инфраструктурный уровень, где обрабатываются коммуникации между сервисами через sidecar, который служит как прозрачный прокси для безопасного, быстрого и надёжного обмена данными.
![](https://habrastorage.org/getpro/habr/upload_files/ed2/13b/e95/ed213be9554c6e7e58bf99479eef39ad.jpg)
При использовании 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’ам, которые берут на себя управление запросами и прочие тонкости коммуникаций.
4. Как работает Istio
Istio — это опенсорс-платформа для управления сервисами с помощью service mesh в облачном окружении с Kubernetes. Istio отвечает за балансировку нагрузки, аутентификацию между сервисами, мониторинг и другие задачи.
Архитектура Istio
Istio состоит из control plane и data plane.
Data plane состоит из агентов, которые развёртываются как sidecar’ы при каждом сервисе. Sidecar перехватывает весь входящий и исходящий трафик сервиса и вместе с control plane применяет к нему настроенные правила и действия.
Control plane управляет sidecar’ами на data plane и занимается распределением, обнаружением сервисов, авторизацией, аутентификацией и другими функциями. Благодаря control plane можно согласованно управлять data plane.
Основные компоненты
Давайте рассмотрим главные функции основных компонентов архитектуры Istio.
![](https://habrastorage.org/getpro/habr/upload_files/740/638/eb1/740638eb1d92fb4c61e3c9798dcd32ab.jpg)
Envoy
Envoy — это высокопроизводительный агент, написанный на C++. Istio внедряет Envoy в виде sidecar-контейнера к контейнеру приложений, чтобы он перехватывал весь входящий и исходящий трафик сервиса и выполнял с ним разные задачи, включая балансировку нагрузки, размыкание цепи, внесение ошибок, а также предоставлял метрики, скажем, для Prometheus и Jaeger. Все внедрённые агенты вместе образуют data plane.
Istiod
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
Для входящего и исходящего трафика существуют отдельные процессы маршрутизации.
![](https://habrastorage.org/getpro/habr/upload_files/539/0cc/7dd/5390cc7dd1327452f338a80e791570a8.jpg)
Обработчики входящего трафика пересылают трафик в контейнер своего приложения, а обработчики исходящего ждут исходящий трафик. Istio использует init-контейнер для работы с iptables в пространстве имён сети пода и обработки правил, чтобы входящие и исходящие пакеты передавались в sidecar-прокси пода.
Init-контейнер отличается от контейнера приложения:
Он запускается до главного контейнера и всегда выполняется до конца.
Если init-контейнеров несколько, они выполняются друг за другом.
Когда мы говорим о service mesh, мы обычно представляем себе sidecar, управляемый Istio + Envoy. На первый взгляд, это неплохой подход, но не без недостатков:
Снижение производительности. Прокси — это отдельное приложение, которое тоже потребляет процессорные ресурсы и оперативную память. Для Envoy обычно требуется около 1 ГБ памяти.
Сложная архитектура. Нам нужен control plane, data plane, правила для разных приложений, безопасность взаимодействия между прокси и т. д.
Больше расходов на эксплуатацию и обслуживание. Невозможно развернуть sidecar без автоматизированных инструментов. Обычно service mesh работает в Kubernetes, что позволяет сократить нагрузку.
5. Что такое eBPF
Когда мы используем sidecar, нам приходится развёртывать в каждом поде контейнер с нужной конфигурацией. У каждой ноды только одно ядро, и все контейнеры на одной ноде используют это ядро. А нельзя ли в таком случае ограничиться одним sidecar-прокси на ноду? eBPF как раз об этом.
Это технология, с помощью которой в ядре можно запускать пользовательские программы в ответ на события. События могут быть самыми разными, включая вход в функцию и выход из неё (в ядре или пользовательском пространстве) или поступление важных сетевых пакетов. Если добавить программу eBPF к событию, она будет срабатывать независимо от того, какой процесс вызвал это событие и где он находится — в контейнере приложения или на хосте. Решения на базе eBPF дают возможности мониторинга, безопасности и взаимодействий без sidecar.
![](https://habrastorage.org/getpro/habr/upload_files/c2c/21a/642/c2c21a642aeafde510735a1ed6099998.jpg)
2 декабря 2021 года проект Cilium объявил о программе бета-тестирования Cilium Service Mesh. Cilium использует eBPF и обходится без sidecar’ов для выполнения таких задач, как маршрутизация на L7, балансировка нагрузки, TLS, политики доступа, проверки работоспособности, логирование и трассировка.
![](https://habrastorage.org/getpro/habr/upload_files/67c/020/047/67c020047ea52b774a8bd90f05855c52.jpg)
Что нам это даёт?
Сокращение YAML
Когда мы используем 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.
![](https://habrastorage.org/getpro/habr/upload_files/83e/93b/dfa/83e93bdfa3803981aa52adfb950874f9.jpg)
В модели с 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’ом.
6. RSocket Broker
RSocket Broker — это система коммуникации между самыми разными приложениями по протоколу RSocket.
![](https://habrastorage.org/getpro/habr/upload_files/428/31e/d73/42831ed73e16d36a9bb1b874b863e900.jpg)
Как работает RSocket Broker? Запрашивающий сервис (Requester) инициирует вызов к брокеру, брокер перенаправляет запрос отвечающему сервису (Responder), а потом возвращает запрашивающему сервису полученный результат.
![](https://habrastorage.org/getpro/habr/upload_files/7dc/e72/aea/7dce72aea15b2b81f18fbb48dda01c11.jpg)
Когда отвечающий сервис запускается, он активно создаёт долгосрочное TCP-соединение с брокером и передаёт ему список сервисов, которые может обслуживать. Когда запрашивающий сервис запускается, он тоже создаёт с брокером долгосрочное соединение. Когда затем он хочет вызвать удалённый сервис, он инкапсулирует запрос в сообщение с уникальным идентификатором и направляет его брокеру. Брокер получает сообщение, определяет сервис-получатель по доступным метаданным и ищет сервис во внутренней таблице маршрутизации. Когда тот сервис обработает запрос, он тоже инкапсулирует результат в сообщение и отправляет его брокеру. Брокер считывает идентификатор сообщения и пересылает его нужному вызывающему сервису, который применяет соответствующую бизнес-логику в зависимости от ответа.
Преимущества обмена сообщениями через брокер:
Не нужны дополнительные проверки работоспособности, потому что мы уверены в соединении.
Сервис, обрабатывающий запрос, больше не должен прослушивать порт, и этот подход кардинально отличается от HTTP REST API и gRPC и даёт больше безопасности.
Запрашивающий и отвечающий сервис не обязаны знать о существовании друг друга.
Появляется возможность управления потоком: если нагрузка на отвечающий сервис возрастает, брокер автоматически пересылает сообщение другому сервису с помощью интеллектуальной балансировки нагрузки.
Регистрация и обнаружение сервисов теперь не требуют сторонних реестров, вроде Eureka, Consul или ZooKeeper, так что расходы на инфраструктуру ниже.
Брокер проверяет права доступа сервисов на обоих концах, и мы развёртываем поддержку TLS только на брокере, чтобы защитить каналы коммуникации.
Недостатки
У брокера есть свои минусы. Производительность будет чуть ниже, потому что стороны не общаются друг с другом напрямую. Кроме того, весь трафик перенаправляется через брокер, так что в сети появляется узкое место. Хотя эту проблему можно частично решить, обеспечив высокую надёжность кластера и брокера.
7. Управление сервисами с помощью RSocket Broker
Istio Service Mesh сложно реализовать за пределами дата-центров. А как же IoT-устройства? Нужно установить sidecar на каждом смартфоне? Эту задачу можно решить с помощью RSocket Broker.
На схеме service mesh на базе RSocket Broker мы не видим ни sidecar, ни дублирования процессов.
![](https://habrastorage.org/getpro/habr/upload_files/4ed/8c3/56b/4ed8c356b6bdddbfc87e2282b9966cf2.jpg)
Давайте сравним характеристики двух архитектур:
Уровень инфраструктуры. С одной стороны у нас связка sidecar-прокси + control plane, а с другой — централизованный брокер с интегрированными функциями control plane.
Централизованное управление. Все логи, метрики и другая полезная информация находятся в одном месте, что упрощает управление.
Протокол связи. Минус использования брокера RSocket в том, что между приложениями нужно использовать протокол RSocket.
Доступ к приложениям и устройствам. Не на всех устройствах можно установить прокси, и тогда на выручку приходит схема на основе RSocket.
Расходы на эксплуатацию и обслуживание выше. Управлять одним кластером RSocket Broker на 10 серверов — это не то же самое, что управлять 10 000 экземпляров прокси.
Эффективность. У RSocket производительность в десять раз выше, чем у HTTP.
Безопасность. В RSocket безопасность реализуется проще. Брокер использует TLS + JWT, а не mTLS, так что управлять сертификатами не нужно. При этом благодаря модели безопасности JWT мы получаем более детальный контроль разрешений. При использовании RSocket Broker можно сократить поверхность атаки.
![](https://habrastorage.org/getpro/habr/upload_files/b64/a00/97f/b64a0097fed7f52dd0e1c27ff6703dd9.jpg)
Старт практического интенсива по service mesh 24 марта.
Зависимость от инфраструктуры и сети. У RSocket Broker есть большое преимущество над Istio: он работает не только с Kubernetes. Да, считается, что Istio тоже работает с другими платформами, но по факту управлять sidecar-прокси за пределами Kubernetes слишком сложно. Зато RSocket Broker можно развернуть где угодно.
Заключение
Учитывая популярность микросервисных приложений, вопрос управления сервисами стоит довольно остро. В этой статье мы рассмотрели, как управлять сервисами с помощью Istio, eBPF и RSocket Broker.
Узнать о best practices в работе с service mesh
Service mesh — палочка-выручалочка для работы с микросервисами. Этот инструмент обеспечивает прозрачное управление входящим и исходящим трафиком в едином месте. Сегодня о знании service mesh спрашивают при трудоустройстве в таких крупных компаниях как Linkerd, Ozon, Сбер, ЮMoney, МТС, Спортмастер, Газпром и др.
На интенсиве по Service mesh вы изучите инструмент на практике и научитесь решать целый ряд проблем, возникающих при работе с микросервисами.
Бизнес-кейсы, которые будем решать:
Проблема #1. Отсутствие или слабое развитие мониторинга. Периодическое торможение системы, но непонятно, где именно.
Проблема #2. Выдача ошибок сервисами, со слов клиентов. Частые падения, хотя и кратковременные.
Проблема #3. Нужно выкатить новую фичу, но нет уверенности, что все пойдет как надо.
Проблема #4. В системе появляется новый компонент который отвечает за процессинг кредитных карт. Появляются требования к безопасности системы всей системы.
Проблема #5. Приложение разрослось. Клиенты жалуются на долгое время ответа. На серверной стороне все ок. Похоже проблема с удаленными геолокациями.
Интенсив пройдет с 24 по 26 марта. Вы узнаете о best practices от эксперта, который уже давно работает с технологией. Помимо голой теории и практики, вы получите рекомендации, как делать, а как – нет.
Посмотреть программу и записаться : https://slurm.club/3ItpvWY