Прим. перев.: автор это статьи (Luc Perkins) — developer advocate в организации CNCF, являющейся домом для таких Open Source-проектов, как Linkerd, SMI (Service Mesh Interface) и Kuma (кстати, вы тоже задумывались, почему в этом списке нет Istio?..). В очередной раз пытаясь принести в DevOps-сообщество лучшее понимание в модный хайп под названием «service mesh», он приводит 16 характерных возможностей, которые предоставляют подобные решения.
Сегодня service mesh ― одна из самых горячих тем в области программной инженерии (и по праву!). Я считаю эту технологию невероятно перспективной и мечтаю стать свидетелем ее широкого распространения (конечно, когда это имеет смысл). Тем не менее, она до сих пор окружена ореолом таинственности для большинства людей. При этом даже те, кто хорошо знаком с ней, нередко затрудняются сформулировать ее плюсы и что именно она собой представляет (включая и вашего покорного слугу). В статье я попытаюсь исправить ситуацию, перечислив различные сценарии использования «сервисных сеток»*.
* Прим. перев.: здесь и далее в статье будет использоваться именно такой перевод («сервисная сетка») для всё ещё нового термина service mesh.
Но сперва хочу сделать несколько замечаний:
- Я никогда не работал с сервисными сетками и не использовал их вне проектов, затеянных ради собственного образования. С другой стороны, именно я написал кучу документации для внутренней service mesh компании Twitter в 2015 году (тогда она еще даже не называлась «сервисной сеткой») и участвовал в разработке сайта и документации для Linkerd, так что это что-нибудь значит.
- Мой список — примерный и неполный. Вполне возможны неизвестные мне сценарии использования, и со временем наверняка возникнут новые варианты, по мере развития технологии и роста ее популярности.
- В то же время далеко не каждая существующая реализация service mesh поддерживает все перечисленные случаи использования. Поэтому мои выражения вроде «service mesh может…» следует читать как «отдельные, а возможно, и все популярные реализации service mesh могут…».
- Порядок примеров не имеет какого-либо значения.
Краткий список:
- обнаружение сервисов;
- шифрование;
- аутентификация и авторизация;
- балансировка нагрузки;
- circuit breaking;
- автомасштабирование;
- канареечные развертывания;
- сине-зеленые развертывания;
- проверка здоровья;
- load shedding;
- зеркалирование трафика;
- изоляция;
- ограничение частоты запросов, повторные попытки и тайм-ауты;
- телеметрия;
- аудит;
- визуализация.
1. Обнаружение сервисов
TL;DR: Подключайтесь к другим сервисам в сети с помощью простых имен.
Сервисы должны иметь возможность автоматически «находить» друг друга с помощью адекватных имен ― например,
service.api.production
, pets/staging
или cassandra
. Облачные среды отличаются своей эластичностью, и за одним именем может скрываться множество экземпляров сервиса. Понятно, что в такой ситуации физически невозможно захардкодить все IP-адреса.Плюс ко всему, когда один сервис находит другой, он должен иметь возможность посылать запросы этому сервису, не опасаясь, что они окажутся на входе у его неработающего экземпляра. Другими словами, service mesh должна следить за работоспособностью всех экземпляров сервисов и поддерживать список хостов в максимально актуальном состоянии.
Каждая service mesh реализует механизм обнаружения сервисов по-своему. На данный момент самым распространенным способом является делегирования внешним процессам вроде DNS Kubernetes. В прошлом в Twitter для этих целей мы использовали систему имен Finagle. Кроме того, технология service mesh делает возможным появление пользовательских механизмов именования (хотя я еще не встречал ни одной реализации SM с таким функционалом).
2. Шифрование
TL;DR: Избавьтесь от незашифрованного трафика между сервисами и пускай этот процесс будет автоматизированным и масштабируемым.
Приятно осознавать, что злоумышленники не могут проникнуть в вашу внутреннюю сеть. Сетевые экраны прекрасно справляются с этим. Но что произойдет, если хакер все же проникнет внутрь? Он сможет делать с внутрисервисным трафиком все, что пожелает? Давайте надеяться, что этого все же не произойдет. Для того, чтобы предотвратить подобный сценарий, следует реализовать сеть с нулевым доверием (zero-trust), в которой весь трафик между сервисами зашифрован. Большинство современных сервисных сеток добиваются этого с помощью взаимного TLS (mutual TLS, mTLS). В некоторых случаях mTLS работает в целых облаках и кластерах (думаю, и межпланетные коммуникации когда-нибудь будут устроены аналогично).
Конечно, для mTLS service mesh необязательна. Каждый сервис может сам позаботиться о своем TLS, но это означает, что нужно будет найти способ генерировать сертификаты, распределять их по хостам сервиса, включать в приложение код, который будет загружать эти сертификаты из файлов. Да, еще не забудьте об обновлении этих сертификатов через определенные промежутки времени. Сервисные сетки автоматизируют mTLS с помощью систем вроде SPIFFE, которые, в свою очередь, автоматизируют процесс выпуска и ротации сертификатов.
3. Аутентификация и авторизация
TL;DR: Устанавливайте, кто является инициатором запроса, и определяйте, что им разрешено делать еще до того, как запрос достигнет сервиса.
Сервисы часто хотят знать, кто выполняет запрос (аутентификация), и, используя эту информацию, решают, что данному субъекту разрешено делать (авторизация). В данном случае за местоимением «кто» могут скрываться:
- Другие сервисы. Это называется «аутентификацией peer'а». Например, сервис
web
хочет получить доступ к сервисуdb
. Сервисные сетки обычно решают подобные проблемы с помощью mTLS: сертификаты в данном случае выступают в роли необходимого идентификатора. - Некие пользователи-люди. Это называется «аутентификацией запроса». Например, пользователь
haxor69
хочет приобрести новую лампу. Сервисные сетки предоставляют различные механизмы, например, JSON Web Tokens.
Многим из нас доводилось делать это в коде приложения. Приходит запрос, мы просматриваем таблицуusers
, находим пользователя и сравниваем пароль, затем проверяем столбецpermissions
и т.д. В случае service mesh это происходит еще до того, как запрос достигнет сервиса.
После того, как мы установили, от кого пришел запрос, нужно определить, что данному субъекту разрешено делать. Некоторые service mesh'и позволяют задавать базовые политики (о том, кто и что может делать) в виде YAML-файлов или в командной строке, в то время как другие предлагают интеграцию с фреймворками вроде Open Policy Agent. Конечная цель — добиться того, чтобы ваши сервисы принимали любые запросы, смело предполагая, что они исходят из надежного источника и действие это разрешено.
4. Балансировка нагрузки
TL;DR: Распределяйте нагрузку по экземплярам сервиса по определенному шаблону.
«Сервис» в составе сервисной секти очень часто состоит из множества идентичных экземпляров. Например, сегодня сервис
cache
состоит из 5 копий, а завтра их число может возрасти до 11. Запросы, направляющиеся в cache
, должны распределяться в соответствии с определенной целью. Например, минимизировать задержку или максимизировать вероятность попасть на работоспособный экземпляр. Чаще всего используется алгоритм кругового обслуживания (Round-robin), но существует и множество других — например, метод взвешенных (weighted) запросов (можно выбрать предпочтительные цели), кольцевое (ring) хеширование (использование согласованного хеширования для upstream-хостов) или метод наименьшего числа запросов (предпочтение отдается экземпляру с наименьшим числом запросов).Классические балансировщики имеют и другие функции, такие как кэширование HTTP и защита от DDoS, но они не очень актуальны для трафика типа east-west (т.е. для трафика, текущего в пределах датацентра ― прим. перев.)(типичная область применения service mesh). Конечно, не обязательно использовать service mesh для балансировки нагрузки, однако она позволяет задавать и контролировать политики балансировки для каждого сервиса из централизованного управляющего слоя, тем самым устраняя необходимость запускать и настраивать отдельные балансировщики в сетевом стеке.
5. Разрыв цепи (circuit breaking)
TL;DR: Останавливайте трафик к проблемному сервису и контролируйте ущерб при наихудших сценариях.
Если по какой-либо причине сервис не справляется с трафиком, service mesh предоставляет несколько вариантов решения этой проблемы (о других будет рассказано в соответствующих разделах). Circuit breaking ― это самый жесткий вариант отключения сервиса от трафика. Однако сам по себе он не имеет смысла ― необходим запасной план. Может предусматриваться противодавление (backpressure) на сервисы, выполняющие запросы (только не забудьте настроить свою service mesh для этого!), или, например, окрашивание статус-страницы в красный цвет и перенаправление пользователей на очередной вариант страницы с «падающим китом» («Twitter is down»).
Сервисные сетки позволяют не только определять, когда последует отключение и что за этим последует. В данном случае «когда» может включать любую комбинацию заданных параметров: общее число запросов за некий период, количество параллельных подключений, pending-запросы, активные повторные попытки и т.д.
Вы вряд ли захотите злоупотреблять circuit breaking'ом, но приятно осознавать, что есть резервный план на крайний случай.
6. Автомасштабирование
TL;DR: Увеличивайте или уменьшайте число экземпляров сервиса в зависимости от заданных критериев.
Service mesh'и ― это не планировщики, поэтому они не осуществляют масштабирование самостоятельно. Однако они могут поставлять информацию, на основе которой планировщики будут принимать решения. Поскольку service mesh'и имеют доступ ко всему трафику между сервисами, они располагают обширной информацией о том, что происходит: какие сервисы столкнулись с проблемами, какие загружены совсем слабо (выделенные на них мощности тратятся впустую) и т.д.
Например, Kubernetes масштабирует сервисы в зависимости от использования pod'ами CPU и памяти (см. наш доклад «Автомасштабирование и управление ресурсами в Kubernetes» — прим. перев.), но если вы решите проводить масштабирование на основе любого другого показателя (в нашем случае — связанного с трафиком), понадобится специальная метрика. Руководство вроде такого показывает, как сделать это с помощью Envoy, Istio и Prometheus, но сам процесс довольно сложен. Нам бы хотелось, чтобы service mesh его упростила, позволив просто задавать условия вроде «увеличь количество экземпляров сервиса
auth
, если число запросов, ожидающих выполнения, будет превышать пороговое значение в течение минуты».7. Канареечные развертывания
TL;DR: Испытывайте новые функции или версии сервиса на подмножестве пользователей.
Скажем, вы разрабатываете некий SaaS-продукт и намереваетесь выкатить его новую крутую версию. Вы протестировали ее в staging, и она прекрасно отработала. Но все же одолевают определенные опасения насчет ее поведения в реальных условиях. Другими словами, требуется проверить новую версию на реальных задачах, не рискуя при этом доверием пользователей. Канареечные развертывания отлично подходят для этого. Они позволяют продемонстрировать новую функцию некоторому подмножеству пользователей. Это подмножество может состоять из самых лояльных пользователей или тех, кто работает с бесплатной версией продукта, или пользователей, выразивших желание побыть «подопытными кроликами».
Service mesh'и реализуют это, позволяя указать критерии, определяющие, кто и какую версию приложения увидит, и соответствующим образом маршрутизируя трафик. При этом для самих сервисов ничего не меняется. Версия 1.0 сервиса считает, что все запросы поступают от пользователей, которые именно ее и должны увидеть, а версия 1.1 то же самое полагает в отношении своих пользователей. А вы, тем временем, можете менять процент трафика между старой и новой версией, перенаправляя растущее число пользователей на новую, если она работает стабильно и ваши «подопытные» дают добро.
8. Сине-зеленые развертывания
TL;DR: Выкатывайте новую крутую функцию, но будьте готовы немедленно вернуть все назад.
Смысл сине-зеленых развертываний в том, чтобы выкатить новый «синий» сервис, запустив его параллельно со старым, «зеленым». Если все пойдет гладко и новый сервис хорошо себя зарекомендует, то старый можно будет постепенно отключить. (Увы, когда-нибудь и этот новый «синий» сервис повторит судьбу «зеленого» и исчезнет…) Сине-зеленые развертывания отличаются от канареечных тем, что новая функция охватывает сразу всех пользователей (а не часть); смысл здесь в том, чтобы иметь наготове «запасную гавань», если вдруг что-то пойдет не так.
Service mesh'и предлагают очень удобный способ тестирования «синего» сервиса и мгновенного переключения на рабочий «зеленый» в случае неполадок. Не говоря уже о том, что попутно они дают массу информации (см. пункт «Телеметрия» ниже) о работе «синего», которая помогает понять, готов ли тот к полноценной эксплуатации.
Прим. перев.: Подробнее о разных стратегиях развертывания в Kubernetes (включая упомянутые canary, blue/green и другие) можно почитать в этой статье.
9. Проверка здоровья
TL;DR: Следите за тем, какие экземпляры сервисов работоспособны, и реагируйте на те из них, которые таковыми быть перестают.
Проверка здоровья (health check) помогает принять решение о том, готовы ли экземпляры сервиса принимать и обрабатывать трафик. Например, в случае HTTP-сервисов проверка здоровья может выглядеть как GET-запрос на endpoint
/health
. Ответ 200 OK
будет означать, что экземпляр здоров, любой другой ― что он не готов принимать трафик. Service mesh'и позволяют указывать как способ, каким будет проверяться работоспособность, так и частоту, с которой эта проверка будет проводиться. Эту информацию можно затем использовать для других целей — например, для балансировки нагрузки и circuit breaking'а.Таким образом, проверка здоровья выступает не самостоятельным сценарием использования, а обычно используется для достижения других целей. Также, в зависимости от результатов health check'ов, могут потребоваться внешние (по отношению к другим целям сервисных сеток) действия: например, обновлять страницу состояния, создавать issue на GitHub или заполнять тикет JIRA. И service mesh предлагает удобный механизм для автоматизации всего этого.
10. Перебрасывание нагрузки (load shedding)
TL;DR: Перенаправляйте трафик в ответ на временный всплеск в использовании.
Если некий сервис оказывается перегружен трафиком, вы можете временно перенаправить часть этого трафика в другое место (то есть «сбросить», «перелить» (shed) его туда). Например, в резервный сервис или дата-центр, или в постоянный Pulsar topic. В результате, сервис продолжит обрабатывать часть запросов вместо того, чтобы упасть и перестать обрабатывать вообще все. Сбрасывание нагрузки предпочтительнее, чем разрыв цепи, но все равно нежелательно злоупотреблять им. Оно позволяет предотвратить каскадные сбои, в результате которых падают downstream-сервисы.
11. Распараллеливание/зеркалирование трафика
TL;DR: Отправляйте один запрос сразу в несколько мест.
Иногда возникает необходимость отправить запрос (или некоторую выборку запросов) сразу в несколько сервисов. Характерный пример ― отправка части production-трафика в staging-сервис. Основной веб-сервер production'а отправляет запрос к нижестоящему сервису
products.production
и только к нему. А service mesh интеллектуально копирует этот запрос и отправляет его на products.staging
, о чем веб-сервер даже не подозревает.Еще один связанный сценарий использования сервисной сетки, который можно реализовать поверх распараллеливания трафика, ― это регрессионное тестирование. Оно предусматривает отправку одних и тех же запросов различным версиям сервиса и проверку того, ведут ли все версии себя одинаково. Мне пока не встречалась реализация service mesh с интегрированной системой регрессионного тестирования вроде Diffy, но сама идея кажется перспективной.
12. Изоляция
TL;DR: Разбивайте свою service mesh на мини-сети.
Также известная как сегментация, изоляция ― это искусство разделения сервисной сетки на логически обособленные сегменты, которые ничего не знают друг о друге. Изоляция немного похожа на создание виртуальных частных сетей. Принципиальная же разница состоит в том, что вы по-прежнему можете пользоваться всеми преимуществами service mesh (вроде обнаружения сервисов), но с дополнительной безопасностью. Например, если злоумышленник сумеет проникнуть в сервис в одной из подсетей, он не сможет увидеть, какие сервисы запущены в других подсетях, или перехватить их трафик.
Кроме того, преимущества могут быть и организационными. Возможно, вам захочется разбить сервисы на подсети в зависимости от структуры компании и освободить разработчиков от когнитивной нагрузки, вызванной необходимость держать в уме всю service mesh.
13. Ограничение частоты запросов, повторные попытки и тайм-ауты
TL;DR: Больше не надо включать в кодовую базу насущные задачи по управлению запросами.
Все эти вещи можно было бы рассматривать как отдельные случаи использования, но я решил объединить их из-за одной общей особенности: они перенимают на себя задачи по управлению жизненным циклом запросов, обычно отрабатываемые библиотеками приложений. Если вы разрабатываете веб-сервер на Ruby on Rails (не интегрированный с service mesh), который выполняет запросы к backend-сервисам через gRPC, приложение должно будет само решать, что делать, если N запросов завершатся неудачей. Также придется выяснять, какой объем трафика способны будут обработать эти сервисы и за'hardcode'ить эти параметры с помощью специальной библиотеки. Плюс ко всему, приложение должно будет решать, когда пора сдаться и позволить запросу протухнуть (по timeout). И для того, чтобы изменить любой из вышеперечисленных параметров, веб-сервер придется остановить, переконфигурировать и запустить заново.
Передача этих задач сервисной сетке означает не только то, что разработчикам сервисов не нужно будет думать о них, но и то, что их можно будет рассматривать более глобальным образом. Если используется сложная цепочка сервисов, скажем, A –> B –> C –> D –> E, необходимо учитывать весь жизненный цикл запроса. Если стоит задача продлить тайм-ауты в сервисе С, логично делать это все разом, а не по частям: обновив код сервиса и дожидаясь, пока pull request будет принят и CI-система развернет обновленный сервис.
14. Телеметрия
TL;DR: Собирайте всю нужную (и не совсем) информацию от сервисов.
Телеметрия ― это общий термин, включающий в себя метрики, распределенную трассировку и логи. Сервисные сетки предлагают механизмы для сбора и обработки всех трех типов данных. Здесь все становится немного расплывчатым, поскольку число возможных вариантов слишком велико. Для сбора метрик есть Prometheus и другие инструменты, для сбора логов можно использовать fluentd, Loki, Vector и др. (например, ClickHouse с нашим loghouse для K8s — прим. перев.), для распределенной трассировки есть Jaeger и т.п. Каждая service mesh может поддерживать одни инструменты и не поддерживать другие. Любопытно будет посмотреть, сможет ли проект Open Telemetry обеспечить некоторую конвергенцию.
В данном случае преимущество технологии service mesh в том, что sidecar-контейнеры могут, в принципе, собрать все вышеперечисленные данные со своих сервисов. Другими словами, в свое распоряжение вы получаете единую систему сбора телеметрии, и service mesh может обрабатывать все эту информацию различными способами. Например:
- tail'ить логи от некоего сервиса в CLI;
- отслеживать объем запросов с панели мониторинга service mesh;
- собирать распределенные трассировки и перенаправлять их в систему вроде Jaeger.
Внимание, субъективное суждение: Вообще говоря, телеметрия ― это та область, сильное вмешательство сервисной сетки в которую нежелательно. Сбор базовой информации и отслеживание на лету некоторых «золотых метрик» вроде процента успешных запросов и задержек ― это нормально, но давайте надеяться, что мы не станем свидетелями возникновения этаких Франкенштейн-стеков, которые попытаются заменить специализированные системы, некоторые из коих уже отлично себя зарекомендовали и хорошо изучены.
15. Аудит
TL;DR: Тот, кто забывает уроки истории, обречен их повторять.
Аудит ― это искусство наблюдения за важными событиями в системе. В случае service mesh это может означать отслеживание того, кто делал запросы к конкретным endpoint'ам определенных сервисов или сколько раз за последний месяц происходило некое событие, имеющее отношение к безопасности.
Понятно, что аудит очень тесно связан с телеметрией. Разница же состоит в том, что телеметрия обычно ассоциируется с такими вещами, как производительность и техническая корректность, в то время как аудит может иметь отношение к юридическим и другим вопросам, выходящим за рамки строго технической сферы (например, соответствие требованиям GDPR ― Общего регламента ЕС по защите данных).
16. Визуализация
TL;DR: Да здравствует React.js ― неистощимый источник причудливых интерфейсов.
Возможно, есть более подходящий термин, но я его не знаю. Я просто имею в виду графическое представление service mesh или ее некоторых компонентов. Эти визуализации могут включать индикаторы вроде средних задержек, информацию о конфигурации sidecar-контейнеров, результаты проверок здоровья и оповещения.
Работа в сервис-ориентированной среде сопряжена с гораздо более сильной когнитивной нагрузкой по сравнению с Его Величеством Монолитом. Поэтому когнитивное давление следует снижать любой ценой. Банальный графический интерфейс для service mesh с возможностью кликнуть на кнопку и получить нужный результат может иметь решающее значения для роста популярности этой технологии.
Не были включены в список
Изначально я намеревался включить в список еще несколько сценариев использования, но потом решил не делать этого. Вот они вместе с причинами моего решения:
- Мульти-датацентр. В моем представлении это не столько сценарий использования, сколько узкая и конкретная область применения сервисных сеток или некоторого набора функций вроде обнаружения сервисов.
- Ingress и egress. Это связанная область, но я ограничил себя (возможно, искусственно) сценарием использования «трафик east-west». Ingress и egress заслуживают отдельной статьи.
Заключение
На этом пока все! Опять же, этот список весьма условен и, скорее всего, неполон. Если вам кажется, что я что-то упустил, или в чем-то ошибся, обращайтесь ко мне в Twitter (@lucperkins). Пожалуйста, соблюдайте правила приличия.
P.S. от переводчика
В качестве основы для заглавной иллюстрации к статье взято изображение из статьи «What is a Service Mesh (and when to use one)?» (автор — Gregory MacKinnon). На нем показано, как часть функциональности из приложений (зеленым цветом) перешла к service mesh, обеспечивающей взаимосвязи между ними (голубой цвет).
Читайте также в нашем блоге: