mesh agent что это
Навмеш агент (#NavMesh Agent)
NavMeshAgent components help you to create characters which avoid each other while moving towards their goal. Agents reason about the game world using the NavMesh and they know how to avoid each other as well as other moving obstacles. Pathfinding and spatial reasoning are handled using the scripting API of the NavMesh Agent.
Свойства
Property | Function |
---|---|
Agent Size | |
Radius | Radius of the agent, used to calculate collisions between obstacles and other agents. |
Height | Высота зазора, требуемая агенту для прохождения под препятствием. |
Base offset | Offset of the collision cylinder in relation to the transform pivot point. |
Steering | |
Speed | Максимальная скорость движения (в мировых единицах в секунду) |
Angular Speed | Максимальная скорость вращения (градусов в секунду). |
Acceleration | Максимальное ускорение (в мировых единицах в секунду, в квадрате) |
Stopping distance | The agent will stop when this close to the goal location. |
Auto Braking | When enabled, the agent will slow down when reaching the destination. You should disable this for behaviors such patrolling, where the agent should move smoothly between multiple points |
Obstacle Avoidance | |
Quality | Obstacle avoidance quality. If you have high number of agents you can save CPU time by reducing the obstacle avoidance quality. Setting avoidance to none, will only resolve collision, but will not try to actively avoid other agents and obstacles. |
Priority | Agents of lower priority will be ignored by this agent when performing avoidance. The value should be in the range 0–99 where lower numbers indicate higher priority. |
Path Finding | |
Auto Traverse OffMesh Link | Set to true to automatically traverse off-mesh links. You should turn this off when you you want to use animation or some specific way to traverse off-mesh links. |
Auto Repath | When enabled the agent will try to find path again when it reaches the end of a partial path. When there is no path to the destination, a partial path is generated to the closest reachable location to the destination. |
Area Mask | Area mask describes which area types the agent will consider when finding a path. When you prepare meshes for NavMesh baking, you can set each meshes area type. For example you can mark stairs with special area type, and forbid some character types from using the stairs. |
Детали
The agent is defined by an upright cylinder whose size is specified by the Radius and Height properties. The cylinder moves with the object but always remains upright even if the object itself rotates. The shape of the cylinder is used to detect and respond to collisions between other agents and obstacles. When the GameObject’s anchor point is not at the base of the cylinder, you can use the Base Offset property to take up the height difference.
The height and radius of the cylinder are actually specified in two different places: the NavMesh bake settings and the properties of the individual agents.
Most often you set the size of the agent the same in both places. But, for example, a heavy soldier may have larger radius, so that other agents will leave more space around him, but otherwise he’ll avoid the environment just the same.
Реализация поиска путей для ИИ-агентов с помощью NavMesh
Следование по пути и управление движением
Иногда нам нужно, чтобы ИИ-персонажи бродили по игровому миру, следуя по грубо очерченному или точно заданному пути. Например в гоночной игре ИИ-противники должны ехать по дороге, а в RTS юниты должны уметь перемещаться в нужную точку, двигаясь по рельефу и учитывая положение друг друга.
Чтобы казаться умными, ИИ-агенты должны уметь определять, что они делают, и если не могут достичь нужной точки, то они должны уметь прокладывать наиболее эффективный маршрут и изменять свой путь, когда на пути появляются препятствия.
Избегание препятствий — это простое поведение, позволяющее ИИ-сущностям достигать целевых точек. Важно заметить, что поведение, реализуемое в этом посте, предназначается для таких поведений, как симуляция толпы, в которой основной целью каждого агента является избегание других агентов и достижение цели. Они не определяют наиболее эффективный и кратчайший путь.
Технические требования
Необходима версия Unity 2017, установленная в системе с Windows 7 SP1+, 8, 10 или с Mac OS X 10.9+. Код из данной статьи не будет работать на Windows XP и Vista, а серверные версии Windows и OS X не тестировались.
Файлы кода для этого поста можно найти на GitHub.
Чтобы изучить код в действии, посмотрите это видео.
Навигационный меш
Давайте узнаем, как использовать встроенный генератор навигационных мешей Unity, который может сильно упростить поиск путей для ИИ-агентов. На ранних этапах Unity 5.x функция NavMesh стала доступна всем пользователям, в том числе и с лицензиями personal edition, хотя раньше это была функция только для Unity Pro. Перед релизом 2017.1 система была обновлена, чтобы обеспечить рабочий процесс на основе компонентов, но поскольку для него требуется дополнительный скачиваемый пакет, который на момент написания статьи доступен только в preview-версии, мы будем придерживаться стандартного рабочего процесса на основе сцен. Не волнуйтесь, концепции обоих подходов похожи, и когда готовая реализация наконец доберётся до 2017.x, значительных изменений быть не должно.
Подробнее о системе компонентов NavMesh в Unity можно узнать на GitHub.
Теперь мы изучим все возможности, которые нам может предложить эта система. Для поиска путей ИИ сцена должна быть представлена в определённом формате; на 2D-карте используется двухмерная сетка (массив) для поиска путей алгоритмом A*. ИИ-агенты должны знать, где находятся препятствия, особенно статические. Работа с избеганием коллизий между динамически движущимися объектами — это другой вопрос, обычно называемый steering behavior (управлением движением). В Unity есть встроенный инструмент для генерации NavMesh, представляющий сцену в контексте, удобном для поиска ИИ-агентами оптимального пути к цели. Чтобы приступить к работе, откройте демо-проект и перейдите к сцене NavMesh.
Изучение карты
После открытия демо-сцены NavMesh она должна выглядеть, как на скриншоте:
Сцена с препятствиями и склонами
Это будет наша песочница для объяснения и тестирования функционала системы NavMesh. Общая схема похожа на игру в жанре RTS (real-time strategy). Мы управляем синим танком. Нажимайте на разные точки, чтобы танк двигался к ним. Жёлтый индикатор — это текущая цель танка.
Navigation Static
Во-первых, нужно сказать, что следует пометить всю геометрию в сцене, запекаемую в NavMesh, как Navigation Static. Вы уже могли встречаться с этим раньше, например, в системе карт освещения Unity. Сделать игровые объекты статическими очень просто, достаточно поставить флажок Static для всех их свойств (navigation, lighting, culling, batching и так далее), или воспользоваться раскрывающимся списком для конкретного указания свойств. Флажок находится в правом верхнем углу инспектора выбранных объектов.
Свойство Navigation Static
Это можно делать по отдельности для разных объектов или, если у вас есть встроенная иерархия игровых объектов, применить параметр к родительскому объекту, после чего Unity предложит применить его ко всем дочерним.
Запекание навигационного меша
Для всей сцены параметры навигации navmesh применяются с помощью окна Navigation. Это окно можно открыть, перейдя в панели меню к Window|Navigation. Как и любое другое окно, его можно отсоединить для свободного перемещения или закрепить. На наших скриншотах оно показано как закреплённая вкладка рядом с иерархией, но вы можете поместить это окно в любое удобное место.
Открыв окно, вы увидите отдельные вкладки. Это будет выглядеть примерно так:
В нашем случае на предыдущем скриншоте показана вкладка Bake, но в вашем редакторе может быть по умолчанию выбрана любая вкладка.
Давайте посмотрим на каждую из вкладок, начиная слева и двигаясь направо. Начнём со вкладки Agents, которая выглядит, как показано на скриншоте:
Если вы работаете над другим проектом, то можете обнаружить, что некоторые из настроек отличаются от тех, которые мы задали для примера проекта, показанного на скриншоте. В верхней части вкладки есть список, в который можно добавить новые типы агентов, нажав на кнопку +. Удалить дополнительных агентов можно, выбрав их и нажав на кнопку —. В окне в наглядной форме показано, что делают различные настройки при их изменении. Давайте посмотрим, что делает каждая из настроек:
Как видно на скриншоте, Unity предоставляет несколько типов областей, которые невозможно изменять: Walkable, Not Walkable и Jump. В дополнение к присвоению названий и созданию новых областей можно назначать этим областям стоимость перемещения по ним.
Области (Areas) служат двум целям: делают области доступными или недоступными для агента, а также помечают области как менее желательные с точки зрения затрат на перемещение. Например, вы можете разрабатывать RPG, в которой враги-демоны не могут заходить в области, помеченные как «освящённая земля». Также можно пометить некоторые области карты как «трясина» или «болото», которых агент будет избегать из-за высокой стоимости перемещения.
Третья вкладка Bake — наверно, самая важная. Она позволяет создавать сам NavMesh для сцены. Вам уже должны быть знакомы некоторые из параметров. Вкладка Bake выглядит так:
Параметры размера агента на этой вкладке определяют, как агенты будут взаимодействовать со средой, в то время как параметры во вкладке Agents управляют взаимодействием с другими агентами и подвижными объектами. Но они управляют одинаковыми параметрами, поэтому мы пропустим их. Drop Height и Jump Distance управляют тем, насколько далеко агент может «запрыгнуть», чтобы достичь части NavMesh, которая не связан напрямую с той, в которой в данный момент находится агент. Подробнее мы рассмотрим это ниже, поэтому если вы не уверены, то пока можете не изучать эти параметры.
Кроме того, есть расширенные параметры, которые обычно по умолчанию скрыты. Чтобы развернуть эти опции, просто нажмите на треугольник раскрывающегося списка рядом с заголовком Advanced. Параметр Manual Voxel Size можно воспринимать как настройку «качества». Чем меньше размер, тем больше деталей будет сохранено в меше. Min Region Area используется для пропуска запекания платформ или поверхностей ниже выбранного порога. Height Mesh даёт нам более подробные вертикальные данные при запекании меша. Например, этот параметр позволяет сохранить правильное расположение агента при поднимании по лестницам.
Кнопка Clear удаляет все данные NavMesh сцены, а кнопка Bake создаёт меш для сцены. Процесс запекания довольно быстр. Пока у вас выбрано окно, вы можете наблюдать за генерированием NavMesh кнопкой Bake в окне сцены. Давайте нажмём на кнопку Bake, чтобы посмотреть на результаты. В нашем примере сцены мы в результате получим нечто, похожее на такой скриншот:
Голубыми областями показан NavMesh. Ниже мы вернёмся к этому. А пока давайте перейдём к последней вкладке — Object, которая выглядит так:
Три показанные на предыдущем скриншоте кнопки — All, Mesh Renderers и Terrains — используются как фильтры сцены. Они полезны при работе в сложных сценах со множеством объектов в иерархии. Выбор опции отфильтровывает соответствующий тип из иерархии, что упрощает их выбор. Можно использовать кнопки при исследовании своей сцены в поисках объектов, которые нужно пометить как navigation static.
Использование Nav Mesh Agent
Теперь, когда мы настроили сцену с NavMesh, нам нужен способ, которым агент сможет использовать эту информацию. К счастью для нас, в Unity есть компонент Nav Mesh Agent, который можно перетащить на персонажа. В нашем примере сцены есть игровой объект с названием Tank, к которому уже прикреплён компонент. Посмотрите на иерархию, и вы увидите нечто подобное:
Здесь довольно много параметров, и мы не будем рассматривать все, потому что они довольно понятны, а описание можно найти в официальной документации Unity. Но мы упомянем основные вещи:
Задание целевой точки
После настройки ИИ-агента нам нужен способ сообщить ему, куда двигаться. В нашем примере проекта есть скрипт с названием Target.cs, выполняющий именно эту задачу.
Это простой класс, выполняющий три действия:
Здесь происходят следующие действия: в методе Start мы инициализируем массив navAgents с помощью метода FindObjectsOfType().
Метод UpdateTargets() проходит по нашему массиву navAgents и задаёт для них целевую точку в заданном Vector3. Это ключ к работе кода. Для получения целевой точки вы можете использовать любой механизм, и чтобы агент переместился туда, достаточно задать поле NavMeshAgent.destination; всё остальное сделает агент.
В нашем примере для перемещения используются клики, поэтому когда игрок нажимает мышью, мы выпускаем луч из камеры в мир по направлению курсора мыши, и если он с чем-то пересечётся, то мы назначаем точку столкновения новой targetPosition агента. Также мы соответствующим образом настраиваем маркер цели для простой визуализации точки назначения в игре.
Чтобы протестировать работу, необходимо запечь NavMesh в соответствии с описанием из предыдущего раздела, затем запустить режим Play и выбрать любую область на карте. Если пощёлкать много раз, то можно заметить, что некоторых областей агент достичь не может — вершин красных кубов, самой верхней платформы и платформы внизу экрана.
Красные кубы находятся слишком высоко. Наклон, ведущий к самой верхней платформе, слишком резок для наших настроек Max Slope, и агент не может взобраться на него. На следующих скриншотах показано, как настройки Max Slope влияют на NavMesh:
NavMesh со значением max slope = 45
Если изменить значение Max Slope на что-то типа 51, а затем снова нажать на кнопку Bake, чтобы заново запечь NavMesh, то результаты будут такими:
NavMesh со значением max slope = 51
Как видите, мы можем настраивать дизайн уровней, делая целые области недоступными с помощью изменения единственного параметра. Это может быть полезно, например, когда у вас есть платформа или выступ, для подъёма на которые необходимы верёвка, лестница или лифт. А может быть, особый навык, например, способность лазать?
Применение Off Mesh Links
Вы можете заметить, что на нашей сцене есть два разрыва. В первый наш агент может попасть, но находящийся в нижней части экрана находится слишком далеко. Эти расчёты не полностью произвольны. Off Mesh Links по сути создают «мост» через пробелы между несвязанными сегментами NavMesh. Эти связи можно увидеть в редакторе:
Синие круги с соединительными линиями — это связи.
Unity может генерировать эти связи двумя способами. Первый мы уже рассмотрели. Помните значение Jump Distance во вкладке Bake окна Navigation? Unity автоматически использует то значение для генерирования этих связей при запекании NavMesh. Попробуйте изменить значение в нашей тестовой сцене на 5 и выполнить повторное запекание. Видите — платформы теперь связаны? Так произошло потому, что меши теперь находятся в пределах нового заданного порогового значения.
Снова измените значение на 2 и выполните запекание. Теперь давайте рассмотрим второй способ. Создайте сферы, которые будут использоваться для соединения двух платформ. Разместите их приблизительно так, как показано на скриншоте:
Вы уже можете увидеть, что происходит, но давайте разберём процесс, позволяющий их соединить. В нашем случае я назвал сферу справа start, а сферу слева end. Скоро вы поймёте, почему. Далее я добавил к платформе справа (относительно предыдущего скриншота) компонент Off Mesh Link. Вы заметите, что у компонента есть поля start и end. Как можно догадаться, мы перетащим созданные ранее сферы в соответствующие слоты — сферу start в поле start, а сферу end — в поле end. Инспектор будет выглядеть так:
Значение Cost Override учитывается, когда ему задаётся положительное значение. Оно применяет множитель затрат при использовании этой связи в противоположность более эффективного по стоимости маршрута к цели.
Bi Directional при значении true позволяет агенту двигаться в обоих направлениях. Для создания связей с односторонней проходимостью можно отключить это значение. Значение Activated используются в соответствии со своим названием. При значении false агент игнорирует эту связь. Можно включать и отключать его для создания игровых сценариев, в которых, например, игрок для активации связи должен нажать на переключатель.
Для включения этой связи повторное запекание выполнять не нужно. Посмотрите на свой NavMesh и вы увидите, что он выглядит точно так, как на скриншоте:
Как видите, меньший разрыв по-прежнему соединяется автоматически, и теперь у нас есть новая связь, сгенерированная компонентом Off Mesh Link между двумя сферами. Запустите режим Play и нажмите на дальнюю платформу. Как и ожидалось, агент теперь может перейти к отсоединённой платформе:
На уровнях вашей игры вам может понадобиться изменять эти параметры, чтобы добиться нужных результатов, но сочетание этих возможностей предоставляет вам удобный готовый инструмент. Вы можете довольно быстро создать простую игру с помощью функционала NavMesh.
Этот туториал является фрагментом книги Unity 2017 Game AI Programming — Third Edition, написанной Ray Barrera, Aung Sithu Kyaw и Thet Naing Swe.
Service Mesh: что нужно знать каждому Software Engineer о самой хайповой технологии
Прим. перев.: service mesh — явление, которое ещё не имеет устойчивого перевода на русский язык (более 2 лет назад мы предлагали вариант «сетка для сервисов» или «сервисная сетка», а чуть позже некоторые коллеги стали продвигать сочетание «сервисное сито»). Постоянные разговоры об этой технологии привели к ситуации, в которой слишком тесно переплелись маркетинговая и техническая составляющие. Этот замечательный материал от одного из авторов оригинального термина призван внести ясность для инженеров и не только.
Комикс от Sebastian Caceres
Введение
Если вы инженер-программист, работающий где-то в районе бэкенд-систем, термин «service mesh», вероятно, уже прочно закрепился в вашем сознании за последние пару лет. Благодаря странному стечению обстоятельств, это словосочетание захватывает отрасль все сильнее, а хайп и связанные с ним рекламные предложения нарастают словно снежный ком, летящий вниз по склону и не подающий никаких признаков замедления.
Service mesh зародилась в мутных, тенденциозных водах экосистемы cloud native. К сожалению, это означает, что значительная часть связанной с ней полемики варьируется от «низкокалорийной болтовни» до — если воспользоваться техническим термином — откровенной чуши. Но если отсеять весь шум, можно обнаружить, что у service mesh есть вполне реальная, определенная и важная функция.
В этой публикации я попытаюсь проделать именно это: представить честное, глубокое, ориентированное на инженеров руководство по сервисным сеткам. Я собираюсь ответить не только на вопрос: «Что это такое?», — но и «Зачем?», а также «Почему именно сейчас?». Наконец, попытаюсь обрисовать, почему (по моему мнению) конкретно эта технология вызвала такой сумасшедший ажиотаж, что само по себе интересная история.
Кто я?
Привет всем! Меня зовут William Morgan. Я являюсь одним из создателей Linkerd — самого первого проекта service mesh и проекта, который виноват в появлении термина service mesh как такового (простите, парни!). (Прим перев.: К слову, на заре появления этого термина, более 2,5 лет назад, мы уже переводили ранний материал того же автора под названием «Что такое service mesh и почему он мне нужен [для облачного приложения с микросервисами]?».) Также я возглавляю Buoyant — стартап, занимающийся созданием таких классных service mesh-штук, как Linkerd и Dive.
Вы, наверное, догадываетесь, что у меня весьма пристрастное и субъективное мнение по этому вопросу. Впрочем, я постараюсь свести тенденциозность к минимуму (за исключением одного раздела: «Почему так много разговоров о service mesh?», — в котором все же поделюсь своими предвзятыми идеями). Также я приложу все силы к тому, чтобы сделать это руководство максимально объективным. В конкретных примерах я преимущественно буду полагаться на опыт Linkerd, при этом указывая на известные мне различия (если они имеются) в реализации других типов service mesh.
Окей, пора переходить к вкусняшкам.
Что такое service mesh?
Несмотря на весь хайп, структурно service mesh устроена довольно просто. Это всего лишь куча userspace-прокси, расположенных «рядом» с сервисами (потом мы немного поговорим о том, что такое «рядом»), плюс набор управляющих процессов. Прокси в совокупности получили название data plane, а управляющие процессы именуются control plane. Data plane перехватывает вызовы между сервисами и делает с ними «всякое-разное»; control plane, соответственно, координирует поведение прокси и обеспечивает доступ для вас, т.е. оператора, к API, позволяя манипулировать сетью и измерять её как единое целое.
Что это за прокси? Это TCP-прокси категории «Layer 7-aware» (т.е. знающие особенности протокола прикладного уровня) вроде HAProxy и NGINX. Можно выбрать прокси по своему вкусу; Linkerd использует прокси на Rust, незамысловато названный linkerd-proxy. Мы собрали его специально для service mesh. Остальные mesh’и предпочитают другие прокси (Envoy — частый выбор). Впрочем, выбор прокси — это всего лишь вопрос реализации.
Что делают эти прокси-серверы? Очевидно, они проксируют вызовы к сервисам и от них (строго говоря, они выполняют функцию прокси и обратных прокси, обрабатывая как входящие, так и исходящие вызовы). И они реализуют набор функций, концентрирующийся на вызовах между сервисами. Этот фокус на трафике между сервисами и отличает service mesh-прокси от, скажем, шлюзов API или ingress-прокси (последние фокусируются на вызовах, поступающих в кластер из внешнего мира). (Прим. перев.: сравнение существующих контроллеров Ingress для Kubernetes, многие из которых используют уже упомянутый Envoy, см. в этой статье.)
Итак, с data plane мы разобрались. Control plane устроена проще: это набор компонентов, обеспечивающих всю механику, которая необходима data plane, чтобы работать скоординированным образом, включая обнаружение сервисов, выпуск сертификатов TLS, агрегацию метрик и т. д. Data plane информирует control plane о своем поведении; в свою очередь, control plane предоставляет API, позволяющий менять и отслеживать поведение data plane как единого целого.
Ниже представлена схема control plane и data plane в Linkerd. Как видно, control plane включает в себя несколько различных компонентов, в том числе экземпляр Prometheus, который собирает метрики с прокси-серверов, а также другие компоненты, такие как destination (обнаружение сервисов), identity (центр сертификации, CA) и public-api (endpoint’ы для web и CLI). В отличие от этого, data plane представляет собой простой linkerd-proxy рядом с экземпляром приложения. Это всего лишь логическая схема; в реальных условиях при развертывании у вас может быть три реплики каждого компонента control plane и сотни или тысячи прокси в data plane.
(Синие прямоугольники на этой схеме символизируют границы pod’ов Kubernetes. Видно, что контейнеры с linkerd-proxy находятся в одном pod’е с контейнерами приложения. Подобная схема известна как sidecar-контейнер.)
Архитектура service mesh имеет несколько важных последствий. Во-первых, поскольку задача прокси — перехватывать вызовы между сервисами, service mesh имеет смысл только в том случае, если ваше приложение создавалось как некий набор сервисов. Mesh можно использовать с монолитами, но это явно избыточно ради одного единственного прокси, да и ее функционал вряд ли будет востребован.
Другое важное последствие состоит в том, что service mesh требует огромного количества прокси. На самом деле, Linkerd цепляет linkerd-proxy к каждому экземпляру каждого сервиса (другие реализации добавляют прокси к каждому узлу/хосту/виртуальной машине. В любом случае, это немало). Столь активное использование прокси само по себе несет ряд дополнительных осложнений:
Настал черед вопроса «Зачем?»
Для чего нужна service mesh?
Тем, кто впервые столкнулся с идеей service mesh, простительно испытывать легкий трепет. Конструкция service mesh означает, что она не только увеличит задержки в приложении, но также будет потреблять ресурсы и добавит кучу новых механизмов в инфраструктуру. Сперва вы устанавливаете service mesh, а потом внезапно обнаруживаете, что необходимо обслуживать сотни (если не тысячи) прокси. Спрашивается, кто добровольно пойдет на это?
Ответ на этот вопрос состоит из двух частей. Во-первых, операционные издержки, связанные с разворачиванием этих прокси, могут быть значительно снижены благодаря некоторым изменениям, происходящим в экосистеме (подробнее об этом позже).
Во-вторых, подобное устройство — на самом деле отличный способ ввести дополнительную логику в систему. И не только потому, что с помощью service mesh можно добавить множество новых функций, но и потому, что это можно сделать, не вмешиваясь в экосистему. На самом деле вся модель service mesh основана на этом постулате: в мультисервисной системе, независимо от того, что делают отдельные сервисы, трафик между ними является идеальной точкой для добавления функциональности.
Например, в Linkerd (как и в большинстве mesh’ей) функциональность сфокусирована преимущественно на вызовах HTTP, включая HTTP/2 и gRPC*. Функциональность довольно богатая — ее можно разделить на три класса:
Многие из этих механизмов работают на уровне запросов (отсюда и «L7-прокси»). Например, если сервис Foo посылает HTTP-вызов сервису Bar, linkerd-proxy на стороне Foo может провести интеллектуальную балансировку нагрузки и направлять вызовы с Foo на экземпляры Bar в зависимости от наблюдаемой задержки; он может повторить запрос при необходимости (и если тот идемпотентен); он может записать код ответа и время ожидания, и т.д. Аналогичным образом linkerd-proxy на стороне Bar может отклонить запрос, если он не разрешен или превышен лимит запросов; может зафиксировать задержку со своей стороны, и т.д.
Прокси могут «что-то делать» и на уровне подключения. Например, linkerd-proxy на стороне Foo может инициировать TLS-подключение, а linkerd-proxy на стороне Bar — разрывать его, и обе стороны могут проверять TLS-сертификаты друг друга*. Это обеспечивает не только шифрование между сервисами, но и криптографически безопасный способ идентификации сервисов: Foo и Bar могут «доказать», что они те, кем себя называют.
* «Друг друга» означает, что сертификат клиента также проверяется (mutual TLS). В «классическом» TLS, например, между браузером и сервером, обычно проверяется сертификат только одной стороны (сервера).
Независимо от того, работают ли они на уровне запросов или подключений, важно подчеркнуть, что все функции service mesh носят эксплуатационный характер. Linkerd не в состоянии трансформировать семантику полезной нагрузки — например, добавить поля в JSON-фрагмент или внести изменения в protobuf. В этой важной особенности мы поговорим позже, когда речь пойдет о ESB и middleware.
Таков набор функций, которые предлагает service mesh. Возникает вопрос: почему бы не реализовать их непосредственно в приложении? И зачем вообще связываться с прокси?
Почему service mesh — это хорошая идея
Хотя возможности service mesh захватывают воображение, ее основная ценность на самом деле лежит не в функциях. В конце концов, мы можем реализовать их непосредственно в приложении (позже мы увидим, что таково было происхождение service mesh). Если попытаться выразить эту мысль одним предложением, ценность service mesh состоит в следующем: она предоставляет функции, критически важные для работы современного серверного программного обеспечения, единообразным для всего стека и независимым от кода приложения образом.
Давайте проанализируем это предложение.
«Функции, критически важные для работы современного серверного программного обеспечения». Если вы создаете транзакционное серверное приложение, связанное с публичным интернетом, принимающее запросы из внешнего мира и отвечающее на них в течение короткого времени — например, веб-приложение, API-сервер, да и подавляющее большинство других современных приложений, — и если вы реализуете его как набор из сервисов, которые синхронно взаимодействуют друг с другом, и если вы постоянно модернизируете это ПО, добавляя новые возможности, и если вы вынуждены поддерживать эту систему в работоспособном состоянии в процессе модификации — в этом случае поздравляю вас, вы занимаетесь созданием современного серверного ПО. И все эти замечательные функции, перечисленные выше, на самом деле оказываются критически важными для вас. Приложение должно быть надежным, безопасным, и вы должны иметь возможность наблюдать за тем, что оно делает. Как раз эти вопросы и помогает решить service mesh.
(Окей, в предыдущий абзац все же пробралась моя убежденность в том, что этот подход является современным способом создавать серверное ПО. Другие предпочитают разрабатывать монолиты, «реактивные микросервисы» и иные штуки, не подпадающие под определение, приведенное выше. У этих людей наверняка имеется свое мнение, отличное от моего. В свою очередь, я считаю, что они «не правы» — хотя в любом случае service mesh для них не слишком полезна).
«Единообразным для всего стека». Функции, предоставляемые service mesh, не просто критически важны. Они применяются ко всем сервисам в приложении независимо от того, на каком языке те написаны, какой фреймворк используют, кто их написал, как они были развернуты и от всех остальных тонкостей их разработки и применения.
«Независимым от кода приложения». Наконец, service mesh не только предоставляет единые функциональные возможности для всего стека — она делает это способом, не требующем правки приложения. Фундаментальная основа функциональности service mesh, включая задачи по настройке, обновлению, эксплуатации, обслуживанию и т.д., находится исключительно на уровне платформы и независима от приложения. Приложение может меняться, не затрагивая service mesh. В свою очередь, service mesh может меняться без какого-либо участия приложения.
Короче говоря, service mesh не только предоставляет жизненно важные функции, но и делает это глобальным, единообразным и независимым от приложения способом. И поэтому, хотя функциональность service mesh можно реализовать в коде сервиса (например, в виде библиотеки, включенной в каждый сервис), этот подход не обеспечит однородность и независимость, столь ценные в случае service mesh.
И все, что для этого нужно, — добавить кучу прокси! Обещаю, очень скоро мы рассмотрим эксплуатационные издержки, связанные с добавлением этих прокси. Но сначала давайте остановимся и взглянем на эту идею о независимости с точки зрения различных людей.
Кому помогает service mesh?
Как бы неудобно это ни было, но для того, чтобы некая технология стала важной частью экосистемы, она должна быть принята людьми. Так кто же заинтересован в service mesh? Кто выигрывает от ее использования?
Если вы разрабатываете современное серверное ПО, то можете приблизительно представить свою команду как группу владельцев сервисов, которые вместе разрабатывают и внедряют бизнес-логику, и владельцев платформы, занимающихся разработкой внутренней платформы, на которой эти сервисы работают. В малых организациях это могут быть одни и те же люди, но вместе с ростом компании эти роли, как правило, становятся более выраженными и даже делятся на подроли… (Тут можно многое сказать о меняющейся природе devops’а, организационном влиянии микросервисов и т. п. Но пока давайте примем эти описания как данность).
С такой точки зрения явными бенефициарами service mesh являются владельцы платформы. Ведь в конечном итоге цель платформенной команды состоит в том, чтобы создать внутреннюю платформу, на которой владельцы сервисов могут реализовывать деловую логику и делать это способом, который гарантирует их максимальную независимость от мрачных деталей её эксплуатации. Service mesh не только предлагает возможности, критически важные для достижения этой цели: она делает это способом, который, в свою очередь, не налагает зависимостей на владельцев сервисов.
Владельцы сервисов также выигрывают, хотя и более опосредованным образом. Цель владельца сервиса — быть максимально продуктивным в реализации логики бизнес-процесса, и чем меньше ему приходится заботиться о вопросах эксплуатации, тем лучше. Вместо того, чтобы заниматься реализацией, скажем, политик повторных запросов или TLS, они могут сосредоточиться исключительно на задачах бизнеса и надеяться, что платформа позаботится обо всем остальном. Для них это большое преимущество.
Организационную ценность такого разделения между владельцами платформ и сервисов трудно переоценить. Я думаю, что она вносит основной вклад в ценность service mesh.
Мы усвоили этот урок, когда один из первых поклонников Linkerd рассказал нам, почему они выбрали service mesh: потому что она позволила им «свести говорильню к минимуму». Вот немного подробностей: ребята из одной крупной компании мигрировали свою платформу в Kubernetes. Поскольку приложение работало с конфиденциальной информацией, они хотели зашифровать все коммуникации в кластерах. Однако ситуация осложнялась наличием сотен сервисов и сотен команд разработчиков. Перспектива связываться со всеми и убеждать внести поддержку TLS в свои планы совершенно их не радовала. Установив Linkerd, они перенесли ответственность с разработчиков (с точки зрения которых это были лишние хлопоты) на платформеров, для которых это являлось приоритетом высшего уровня. Другим словами, Linkerd решал для них не столько техническую, сколько организационную проблему.
Короче говоря, service mesh — это, скорее, решение не технической, а социо-технической проблемы. (Спасибо Cindy Sridharan за знакомство с этим термином.)
Решит ли service mesh все мои проблемы?
Если посмотреть на три класса функций, озвученных выше: надежность, безопасность и наблюдаемость, — становится понятно, что service mesh не является полноценным решением ни для одной из этих проблем. Хотя Linkerd может посылать повторные запросы (если знает, что они идемпотентны), он не в состоянии принимать решения о том, что возвращать пользователю, если сервис окончательно упал — такие решения должно принимать приложение. Linkerd может вести статистику успешных запросов, однако он не в состоянии заглянуть в сервис и предоставить его внутренние метрики — подобный инструментарий должен быть у приложения. И хотя Linkerd способен организовывать mTLS, полноценные решения в деле обеспечения безопасности требуют гораздо большего.
Подмножество функций в этих областях, предлагаемых service mesh, относятся к фичам платформы. Под этим я подразумеваю функции, которые:
Примеры возможностей service mesh
Подводя итог, хочу сказать, что service mesh не является полным решением для обеспечения надежности, наблюдаемости или безопасности. Размах этих областей подразумевает обязательное участие владельцев сервисов, Ops/SRE-команд и других субъектов компании. Service mesh предоставляет только «срез» на уровне платформы для каждой из этих областей.
Почему service mesh стала популярна именно сейчас?
Вероятно, в настоящий момент вы задаетесь вопросом: окей, если service mesh настолько хороша, почему мы не начали разворачивать миллионы прокси в стеке лет десять назад?
Есть банальный ответ на этот вопрос: десять лет назад все строили монолиты, и service mesh никому не была нужна. Это правда, но, по моему мнению, в таком ответе теряется суть. Даже десять лет назад концепция микросервисов как перспективного способа создания крупномасштабных систем широко обсуждалась и применялась в таких компаниях, как Twitter, Facebook, Google и Netflix. Общее представление — по крайней мере, в тех частях отрасли, с которыми я контактировал, — состояло в том, что микросервисы — это «правильный способ» создавать крупные системы, даже если это было чертовски трудно.
Конечно, хотя десять лет назад были компании, эксплуатирующие микросервисы, они вовсе не втыкали прокси везде где только можно, чтобы сформировать service mesh. Однако если присмотреться, они делали нечто подобное: во многих из этих компаний предписывалось использовать особую внутреннюю библиотеку для сетевого взаимодействия (иногда называемую библиотекой толстого клиента, fat client library).
У Netflix была Hysterix, у Google была Stubby, у Twitter — библиотека Finagle. Finagle, например, была обязательной для каждого нового сервиса в Twitter. Она обрабатывала как клиентскую, так и серверную часть соединений, позволяла выполнять повторные запросы, поддерживала маршрутизацию запросов, балансировку нагрузки и измерения. Она обеспечивала согласованный слой надежности и наблюдаемости для всего стека Twitter, независимо от того, чем именно занимался сервис. Конечно, она работала только для JVM-языков и основывалась на модели программирования, которую приходилось использовать для всего приложения. Однако ее функциональные возможности были почти такими же, как и у service mesh. (На самом деле первая версия Linkerd просто представляла собой Finagle, обернутый в форму прокси.)
Таким образом, десять лет назад существовали не только микросервисы, но и специальные прото-service-mesh библиотеки, решавшие те же самые проблемы, что service mesh решает сегодня. Однако самой service mesh тогда не было. Должен был произойти еще один сдвиг, прежде чем она появилась.
И именно здесь лежит более глубокий ответ, скрытый в другой перемене, случившейся за последние 10 лет: произошло резкое снижение стоимости развертывания микросервисов. Упомянутые выше компании, использовавшие микросервисы десять лет назад: Twitter, Netflix, Facebook, Google, — были компаниями огромного масштаба и огромных ресурсов. У них была не только потребность, но и возможность создавать, развертывать и эксплуатировать крупные приложения на основе микросервисов. Энергия и усилия, приложенные инженерами Twitter к переходу с монолитного на микросервисный подход, просто поражают воображение. (Честно говоря, как и тот факт, что это удалось.) Подобного рода инфраструктурные маневры тогда были невозможны для меньших по размеру компаний.
Перенесемся в настоящее. Сегодня существуют стартапы, где соотношение микросервисов к разработчикам составляет 5:1 (или даже 10:1), и более того, они успешно с ними справляются! Если стартап из 5 человек способен, не напрягаясь, эксплуатировать 50 микросервисов, значит что-то явно снизило стоимость их внедрения.
1500 микросервисов в Monzo; каждая линия — предписанное сетевое правило, разрешающее трафик
Резкое снижение стоимости эксплуатации микросервисов является результатом одного процесса: роста популярности контейнеров и оркестраторов. Именно в этом и состоит глубокий ответ на вопрос о том, что способствовало появлению service mesh. Одна и та же технология сделала привлекательными как service mesh, так и микросервисы: Kubernetes и Docker.
Почему? Ну, Docker решает одну большую проблему — проблему упаковки. Упаковывая приложение и его (несетевые) runtime-зависимости в контейнер, Docker превращает приложение во взаимозаменяемую единицу, которую можно разместить и запустить где угодно. В то же время он значительно упрощает эксплуатацию многоязычного стека: поскольку контейнер — атомарная единица выполнения, для целей развертывания и эксплуатации не важно, что находится внутри, будь то приложение на JVM, Node, Go, Python или Ruby. Вы просто запускаете его, и все.
Kubernetes выводит все на новый уровень. Теперь, когда есть куча «выполняемых штук» и множество машин, на которых можно их запускать, возникает потребность в инструменте, способном сопоставлять их друг с другом. В широком смысле, вы даете Kubernetes множество контейнеров и множество машин, а он сопоставляет их друг с другом (конечно, это динамический и постоянно меняющийся процесс: новые контейнеры перемещаются по системе, машины запускаются и останавливаются и т.д. Однако Kubernetes учитывает все это).
После настройки Kubernetes временные затраты на развертывание и эксплуатацию одного сервиса слабо отличаются от затрат на развертывание и эксплуатацию десяти сервисов (на самом деле, они практически аналогичны и для 100 сервисов). Добавьте к этому контейнеры как механизм упаковки, поощряющий мультиязычную реализацию, и получите массу новых приложений, реализованных в форме микросервисов, написанных на различных языках — как раз ту среду, для которой так хорошо подходит service mesh.
Итак, мы подошли к ответу на вопрос, почему идея service mesh стала популярна именно сейчас: та однородность, которую Kubernetes обеспечивает для сервисов, прямым образом применима к эксплуатационным задачам, стоящим перед service mesh. Вы пакуете прокси в контейнеры, даете Kubernetes задачу прилепить их куда только можно, и вуаля! На выходе получаете service mesh, при этом всей механикой её развертывания заправляет Kubernetes. (По крайней мере, с высоты птичьего полета. Конечно, в этом процессе есть множество нюансов.)
Подводя итог: причина, по которой service mesh стала популярна именно сейчас, а не десять лет назад, состоит в том, что Kubernetes и Docker не только значительно увеличили потребность в ней, упростив реализацию приложений как наборов мультиязычных микросервисов, но и существенно сократили издержки на ее эксплуатацию, обеспечив механизмы развертывания и поддержки парков sidecar-прокси.
Почему так много разговоров о service mesh?
Предупреждение: в этом разделе я прибегаю ко всяческим предположениям, догадкам, измышлениям и внутренней информации.
Проведя поиск по фразе «service mesh», вы наткнетесь на кучу переработанного низкокалорийного контента, странных проектов и калейдоскопа искажений, достойных эхо-камеры. Любой модной новой технологии свойственно это, но в случае service mesh проблема стоит особенно остро. Почему?
Ну, частично это моя вина. Я прилагал все силы, чтобы продвинуть Linkerd и service mesh при любой удобной возможности, путем бесчисленных публикаций в блоге и статей, подобных этой. Но я не настолько могуч. Чтобы действительно ответить на этот вопрос, следует немного поговорить об общей ситуации. А говорить о ней невозможно, не упомянув один проект: Istio — service mesh с открытым исходным кодом, разрабатываемую совместно Google, IBM и Lyft.
(У этих трех компаний совершенно разные роли: участие Lyft, похоже, сводится к одному лишь названию; они являются авторами Envoy, но не используют Istio или участвуют в его разработке. IBM участвует в разработке Istio и использует его. Google активно участвует в разработке Istio, но, насколько могу судить, на самом деле не использует его.)
Проект Istio примечателен двумя особенностями. Во-первых, это огромные маркетинговые усилия, которые Google, в частности, прикладывает к его продвижению. По моим оценкам, большинство людей, осведомленных о концепции service mesh в настоящее время, впервые узнали о ней благодаря Istio. Вторая особенность состоит в том, насколько плохо Istio был принят. В этом вопросе я, очевидно, сторона заинтересованная, но пытаясь оставаться максимально объективным, всё же не могу не отметить весьма негативный настрой, не очень-то характерный (хотя и не уникальный: на ум приходит systemd, сравнение проводилось уже неоднократно…) для Open Source-проекта.
(На практике у Istio, похоже, проблемы не только со сложностью и UX, но и с производительностью. Например, во время оценки производительности Linkerd, проведенной третьей стороной, специалисты обнаружили ситуации, в которых хвосты задержек (tail latency) Istio в 100 раз превышали аналогичный показатель для Linkerd, а также ситуации с недостатком ресурсов, когда Linkerd продолжал успешно функционировать, а Istio полностью прекращал работу.)
Оставляя в стороне мои теории о том, почему так произошло, я считаю, что зашкаливающий ажиотаж вокруг service mesh объясняется как раз участием Google. А именно, комбинацией следующих трех факторов:
С точки зрения Linkerd это… я бы описал как неоднозначное благо. Я имею в виду, замечательно, что service mesh вошла в мейнстрим — чего не было в 2016-м, когда Linkerd только появился и было по-настоящему трудно привлечь внимание окружающих к проекту. Теперь такой проблемы нет! Но плохо то, что ситуация с service mesh сегодня настолько запутанная, что практически невозможно понять, какие проекты действительно относятся к категории service mesh (не говоря уже о том, чтобы понять, какой из них лучше всего подходит для конкретного варианта использования). Это, безусловно, мешает всем (и, определено, в некоторых случаях Istio или другой проект подходит больше, чем Linkerd, поскольку последний все же не является универсальным решением).
Со стороны Linkerd наша стратегия заключалась в том, чтобы игнорировать шум, продолжать концентрироваться на решении реальных проблем сообщества и, по сути, ждать, когда ажиотаж поутихнет. В конечном итоге хайп пойдет на убыль, и мы сможем продолжать спокойно работать.
Пока же нам всем придется немного потерпеть.
Пригодится ли service mesh мне, скромному software engineer?
С ответом на этот вопрос поможет определиться следующий опросник:
Вы занимаетесь исключительно реализацией бизнес-логики? В этом случае service mesh вам не пригодится. То есть, конечно, вы можете ею заинтересоваться, но в идеале service mesh не должна прямым образом влиять на что-либо в вашем окружении. Продолжайте работать над тем, за что вам платят.
Вы поддерживаете платформу в компании, которая использует Kubernetes? Да, в этом случае service mesh вам необходима (конечно, если вы не используете K8s просто для запуска монолита или пакетной обработки — но тогда я хотел бы поинтересоваться, зачем вам K8s). Скорее всего, вы окажетесь в ситуации со множеством микросервисов, написанных разными людьми. Все они взаимодействуют друг с другом и связаны в клубок runtime-зависимостей, а вам нужно найти способ справиться со всем этим. Применение Kubernetes позволяет выбрать service mesh «под себя». Для этого ознакомьтесь с их возможностями и особенностями и ответьте на вопрос, подходит ли вам вообще какой-либо проект из имеющихся (рекомендую начать исследование с Linkerd).
Вы занимаетесь платформой в компании, которая НЕ использует Kubernetes, но использует микросервисы? В этом случае service mesh вам будет полезна, однако ее использование будет нетривиальным. Конечно, вы можете имитировать работу service mesh, разместив кучу прокси, но важным преимуществом Kubernetes является именно модель развертывания: обслуживание этих прокси вручную потребует гораздо больших времени, сил и затрат.
Вы отвечаете за платформу в компании, которая работает с монолитами? В этом случае service mesh вам, вероятно, не нужна. Если вы работаете с монолитами (или даже с наборами монолитов), имеющими четко определенные и редко меняющиеся паттерны взаимодействия, то service mesh мало что сможет вам предложить. Так что можете просто не замечать ее и надеяться, что она исчезнет как страшный сон…
Заключение
Наверное, service mesh всё-таки не стоит называть «самой хайповой технологией мира» — эта сомнительная честь, вероятно, принадлежит биткоину или ИИ. Возможно, она входит в первую пятерку. Но если пробиться сквозь слои шума и гама, становится ясно, что service mesh приносит реальную пользу тем, кто создает приложения в Kubernetes.
Я хотел бы, чтобы вы попробовали Linkerd — его установка в кластер Kubernetes (или даже в Minikube на ноутбуке) занимает около 60 секунд, и вы сможете сами увидеть, о чем я говорю.
— Если я буду игнорировать service mesh, она исчезнет?
— Должен огорчить вас: service mesh с нами надолго.
— Но я НЕ ХОЧУ использовать service mesh!
— Ну и не надо! Только почитайте мой опросник выше, чтобы понять, следует ли ознакомиться хотя бы с ее азами.
— Разве это не старое доброе ESB/middleware под новым соусом?
— Service mesh занимается эксплуатационной логикой, а не смысловой. Это было главным недостатком сервисной шины предприятия (ESB). Сохранение этого разделения помогает service mesh избежать той же участи.
— Чем service mesh отличается от API-шлюзов?
— Существует миллион статей на эту тему. Просто погуглите.
— Envoy — это service mesh?
— Нет, Envoy — это не service mesh, это прокси-сервер. Его можно использовать для организации service mesh (и многого другого — это прокси общего назначения). Но сам по себе он не является service mesh.
— Network Service Mesh — это service mesh?
— Нет. Несмотря на название, это не service mesh (как вам чудеса маркетинга?).
— Поможет ли service mesh с моей реактивной асинхронной системой на базе очереди сообщений?
— Нет, service mesh вам не поможет.
— Какую service mesh мне использовать?
— Linkerd, ежу понятно.
— Статья — отстой! / Автора — на мыло!
— Пожалуйста, поделитесь ссылкой на неё со всеми друзьями, чтобы они смогли в этом убедиться!
Благодарности
Как вы могли догадаться по названию, эта статья была вдохновлена фантастическим трактатом Jay Kreps «The Log: What every software engineer should know about real-time data’s unifying abstraction». Я встретил Jay’я десять лет назад, когда брал интервью в Linked In, и с тех пор он служит вдохновением для меня.
Хотя я люблю называть себя «разработчиком Linkerd», реальность такова, что я скорее maintainer файла README.md в проекте. Над Linkerd сегодня работает очень, очень, очень много людей, и этот проект не состоялся бы без участия замечательного сообщества контрибьюторов и пользователей.
И в завершение особая благодарность создателю Linkerd, Oliver Gould (primus inter pares), который вместе со мной много лет назад нырнул с головой во всю эту суету с service mesh.