Выбор стратегии деплоя микросервисов
Решение микросервисов состоит из десятков или даже сотен сервисов. Сервисы написаны на разных языках и фреймворках. Каждое из них представляет собой мини-приложение со своими специфическими требованиями к развертыванию, ресурсам, масштабированию и мониторингу. Например, вам нужно запустить определенное количество экземпляров каждого сервиса в зависимости от спроса на этот сервис. Кроме того, каждому экземпляру сервиса должны быть предоставлены соответствующие ресурсы CPU, памяти и I/O. Еще более сложным является то, что развертывание сервисов должно быть быстрым, надежным и экономически эффективным. Существует несколько различных шаблонов развертывания микросервисов. Давайте сначала рассмотрим шаблон «Несколько экземпляров cервиса на хост».
Несколько экземпляров сервисов на хост (Multiple Service Instances per Host pattern)
Одним из способов развертывания ваших микросервисов является использование шаблона «Несколько экземпляров сервисов на хост». При использовании этого шаблона вы предоставляете один или несколько физических или виртуальных хостов и запускаете несколько экземпляров сервисов на каждом. Во многом это традиционный подход к развертыванию приложений. Каждый экземпляр сервиса запускается через известный порт на одном или нескольких хостах.
Следующая диаграмма показывает структуру этого шаблона:
Есть несколько вариантов этого шаблона. Один вариант для каждого экземпляра сервиса должен быть процессом или группой процессов. Например, вы можете развернуть экземпляр сервиса Java как веб-приложение на сервере Apache Tomcat. Экземпляр сервиса Node.js может состоять из родительского процесса и одного или нескольких дочерних процессов.
Другой вариант этого шаблона - запуск нескольких экземпляров сервисов в одном и том же процессе или группе процессов. Например, вы можете развернуть несколько веб-приложений Java на одном сервере Apache Tomcat или запустить несколько пакетов OSGI в одном контейнере OSGI.
Шаблон «Несколько экземпляров сервисов на хост» имеет как преимущества, так и недостатки. Одним из основных преимуществ является его эффективное использование ресурсов. Несколько экземпляров сервисов совместно используют сервер и его операционную систему. Еще эффективнее, если процесс или группа процессов запускают несколько экземпляров сервисов , например, несколько веб-приложений, совместно использующих один и тот же сервер Apache Tomcat и JVM.
Другое преимущество этого шаблона заключается в том, что развертывание экземпляра сервиса происходит относительно быстро. Вы просто копируете сервис на хост и запускаете его. Если сервис написан на Java, вы копируете файл JAR или WAR. Для других языков, таких как Node.js или Ruby, вы копируете исходный код. В любом случае количество байтов, скопированных по сети, является относительно небольшим.
Кроме того, из-за отсутствия накладных расходов запуск сервиса обычно происходит очень быстро. Если сервис - это собственный процесс, вы просто запускаете его. В противном случае, если сервис является одним из нескольких экземпляров, работающих в одном и том же процессе контейнера или группе процессов, вы либо динамически развертываете ее в контейнере, либо перезапускаете контейнер.
Несмотря на свою привлекательность, шаблон «Несколько экземпляров сервисов на хост» имеет ряд существенных недостатков. Один из основных недостатков заключается в том, что экземпляры сервисов практически не изолированы, если только каждый экземпляр сервиса не является отдельным процессом. Хотя вы можете точно отслеживать использование ресурсов каждого экземпляра сервиса, вы не можете ограничивать ресурсы, используемые каждым экземпляром. Неправильно работающий экземпляр сервиса может использовать всю память или ЦП хоста.
Нет никакой изоляции, если несколько экземпляров сервисов выполняются в одном и том же процессе. Все экземпляры могут, например, совместно использовать одну и ту же кучу JVM. Неправильно работающий экземпляр сервиса может легко сломать другие сервисы, работающие в том же процессе. Более того, у вас нет возможности отслеживать ресурсы, используемые каждым экземпляром сервиса.
Другая существенная проблема с этим подходом состоит в том, что операционная группа, которая развертывает сервис, должна знать конкретные детали того, как это сделать. Сервисы могут быть написаны на разных языках и в разных средах, поэтому есть много деталей, которыми команда разработчиков должна поделиться с операциями. Эта сложность увеличивает риск ошибок при развертывании.
Как вы можете видеть, несмотря на знакомство, шаблон множественных экземпляров сервисов на хост имеет некоторые существенные недостатки. Давайте теперь посмотрим на другие способы развертывания микросервисов, которые позволяют избежать этих проблем.
Экземпляр сервиса на каждый хост (Service Instance per Host Pattern)
Другой способ развертывания ваших микросервисов - это шаблон Service Instance per Host. При использовании этого шаблона каждый экземпляр сервиса запускается изолированно на своем собственном хосте. Существуют две разные специализации этого шаблона: экземпляр сервиса для виртуальной машины и экземпляр сервиса для контейнера.
Экземпляр сервиса на виртуальную машину (Service Instance per Virtual Machine Pattern)
Когда вы используете экземпляр сервиса для шаблона виртуальной машины, вы упаковываете каждый сервис как образ виртуальной машины (VM), такой как Amazon EC2 AMI. Каждый экземпляр сервиса- это виртуальная машина (например, экземпляр EC2), которая запускается с использованием этого образа виртуальной машины. Следующая диаграмма показывает структуру этого шаблона:
Это основной подход, используемый Netflix для развертывания сервиса потокового видео. Netflix упаковывает каждую из своих услуг в качестве EC2 AMI с использованием Aminator. Каждый запущенный экземпляр сервиса является экземпляром EC2.
Существует множество инструментов, которые вы можете использовать для создания своих виртуальных машин. Вы можете настроить свой сервер непрерывной интеграции (CI) (например, Teamcity) так, чтобы он вызывал Aminator для упаковки ваших услуг в качестве AMI EC2. Packer.io - еще одна опция для автоматического создания образа виртуальной машины. В отличие от Aminator, он поддерживает различные технологии виртуализации, включая EC2, DigitalOcean, VirtualBox и VMware.
Компания Boxfuse предлагает эффективный способ создания образов виртуальных машин, который преодолевает недостатки виртуальных машин, которые я опишу ниже. Boxfuse упаковывает ваше Java-приложение как минимальный образ виртуальной машины. Эти образы быстро создаются, загружаются быстро и более безопасны, поскольку они предоставляют ограниченную поверхность атаки.
Компания CloudNative имеет Bakery, SaaS-предложение для создания EC2 AMI. Вы можете настроить свой CI-сервер так, чтобы он вызывал Bakery после тестов на прохождение микросервиса. Затем Bakery упаковывает ваши сервисы в качестве AMI. Использование SaaS-предложения, такого как Bakery, означает, что вам не нужно тратить драгоценное время на настройку инфраструктуры создания AMI (Amazon Machine Images).
Шаблон Service Instance for Virtual Machine имеет ряд преимуществ. Основным преимуществом виртуальных машин является то, что каждый экземпляр сервиса работает в полной изоляции. Он имеет фиксированный объем ресурсов процессора и памяти и не может похищать ресурсы других сервисов.
Еще одно преимущество развертывания ваших микросервисов в качестве виртуальных машин заключается в том, что вы можете использовать развитую облачную инфраструктуру. Облака, такие как AWS, Azure, Google cloud, предоставляют полезные функции, такие как балансировка нагрузки и автоматическое масштабирование.
Еще одно большое преимущество развертывания вашего сервиса в качестве виртуальной машины заключается в том, что он включает в себя технологию реализации вашего сервиса. Как только сервис упакован как виртуальная машина, он становится черным ящиком. API управления виртуальной машиной становится API для развертывания сервиса. Развертывание становится намного проще и надежнее.
Однако шаблон Service Instance for Virtual Machine имеет некоторые недостатки. Один недостаток - менее эффективное использование ресурсов. Каждый экземпляр сервиса имеет накладные расходы на всю виртуальную машину, включая операционную систему. Кроме того, в типичном общедоступном IaaS виртуальные машины имеют фиксированные размеры, и возможно, что виртуальная машина будет использоваться недостаточно.
Переход, общедоступный IaaS обычно взимает плату за виртуальные машины независимо от того, заняты они или простаивают. IaaS, такой как AWS, обеспечивает автоматическое масштабирование, но сложно быстро реагировать на изменения спроса. Следовательно, вам часто приходится перегружать виртуальные машины, что увеличивает стоимость развертывания.
Другим недостатком этого подхода является то, что развертывание новой версии сервиса обычно происходит медленно. Образы виртуальных машин обычно создаются медленно из-за их размера. Кроме того, виртуальные машины обычно создаются медленно, опять же из-за их размера. Кроме того, для запуска операционной системы обычно требуется некоторое время. Однако обратите внимание, что это не всегда так, поскольку существуют легковесные виртуальные машины, такие как созданные в Boxfuse.
Экземпляр сервиса на каждый контейнер (Service Instance per Container Pattern)
Давайте теперь рассмотрим альтернативный способ развертывания микросервисов, который является более легковесным, но при этом обладает многими преимуществами виртуальных машин.
Когда вы используете Service Instance per Container Pattern, каждый экземпляр сервиса запускается в своем собственном контейнере. Контейнеры - это механизм виртуализации на уровне операционной системы. Контейнер состоит из одного или нескольких процессов, выполняющихся в песочнице. С точки зрения процессов, они имеют свое собственное пространство имен портов и корневую файловую систему. Вы можете ограничить память контейнера и ресурсы процессора. Некоторые реализации контейнеров также имеют ограничение скорости ввода-вывода. Примеры контейнерных технологий включают Docker и Solaris Zones.
Следующая диаграмма показывает структуру этого шаблона:
Чтобы использовать этот шаблон, вы упаковываете свой сервис как image контейнера. Образ контейнера - это образ файловой системы, состоящий из приложений и библиотек, необходимых для запуска сервиса. Некоторые образы контейнеров состоят из полной корневой файловой системы Linux. Другие более легкие. Например, для развертывания сервиса Java вы создаете образ контейнера, содержащий среду выполнения Java, возможно, сервер Apache Tomcat, и ваше скомпилированное приложение Java.
После того, как вы упаковали свой сервис как образ контейнера, вы запускаете один или несколько контейнеров. Обычно вы запускаете несколько контейнеров на каждом физическом или виртуальном хосте. Вы можете использовать менеджер кластеров, такой как Kubernetes или Marathon, для управления вашими контейнерами. Менеджер кластера рассматривает хосты как пул ресурсов. Он решает, где разместить каждый контейнер, основываясь на ресурсах, необходимых для контейнера, и ресурсах, доступных на каждом хосте.
Шаблон Service Instance per Container имеет как преимущества, так и недостатки. Преимущества контейнеров аналогичны преимуществам виртуальных машин. Они изолируют ваши сервисные экземпляры друг от друга. Вы можете легко отслеживать ресурсы, потребляемые каждым контейнером. Также, как и виртуальные машины, контейнеры инкапсулируют технологию, используемую для реализации ваших услуг. API управления контейнерами также служит API для управления вашими сервисами.
Однако, в отличие от виртуальных машин, контейнеры - это легковесная технология. Контейнерные изображения обычно очень быстро создаются. Например, на моем ноутбуке упаковка пакета Spring Boot в виде Docker-контейнера занимает всего 5 секунд. Контейнеры также запускаются очень быстро, так как нет длинного механизма загрузки ОС. Когда контейнер запускается, запускается сервис.
Есть некоторые недостатки использования контейнеров. Хотя контейнерная инфраструктура стремительно развивается, она не настолько развита, как инфраструктура для виртуальных машин. Кроме того, контейнеры не так безопасны, как виртуальные машины, поскольку контейнеры совместно используют ядро операционной системы хоста.
Другим недостатком контейнеров является то, что вы несете ответственность за работу по администрированию изображений контейнеров. Кроме того, если вы не используете размещенное контейнерное решение, такое как Google Container Engine или Amazon EC2 Container Service (ECS), то вы должны администрировать инфраструктуру контейнера и, возможно, инфраструктуру виртуальной машины, на которой он работает.
Кроме того, контейнеры часто развертываются в инфраструктуре с ценами на виртуальную машину. Следовательно, как описано ранее, вы, вероятно, понесете дополнительные расходы на виртуальные машины с избыточным выделением ресурсов для обработки скачков нагрузки.
Интересно, что различие между контейнерами и виртуальными машинами, скорее всего, стирается. Как упоминалось ранее, виртуальные машины Boxfuse быстро создаются и запускаются. Проект Clear Containers направлен на создание облегченных виртуальных машин. Также растет интерес к k8s, Docker и тд. недавно приобрела Unikernel Systems.
Существует также более новая и все более популярная концепция безсерверного развертывания, которая позволяет избежать необходимости выбора между развертыванием сервисов в контейнерах или виртуальных машинах. Давайте посмотрим на это дальше.
Serverless Deployment
Serverless – бессерверная архитектура приложений. На самом деле, не такая уж она и бессерверная. Основу архитектуры составляют микросервисы, или функции (lambda), выполняющие определённую задачу и запускаемые на логических контейнерах, спрятанных от посторонних глаз. Т.е. конечному пользователю дан только интерфейс загрузки кода функции (сервиса) и возможность подключения к этой функции источников событий (events).
Плюсы такой архитектуры:
- Отсутствие аппаратной части – серверов;
- Отсутствие прямого контактирования и администрирования серверной части;
- Практически безграничный горизонтальный рост вашего проекта;
- Оплата за использованное время ЦПУ.
К минусам же можно отнести:
- Отсутствие чёткого контроля контейнера (вы никогда не знаете, где и как они запускаются, кто имеет доступ)
- Отсутствие “целостности” приложения: каждая функция – это независимый объект, что часто приводит к некой разбросанности приложения и затруднениям сложить все воедино.
- Холодный старт контейнера оставляет желать лучшего (как минимум, в Амазоне). Первый запуск контейнера с лямбда функцией частенько может притормозить на 2-3 секунды, что не всегда хорошо воспринимается пользователями.
Итоги
Развертывание приложения микросервисов является сложной задачей. Существуют десятки или даже сотни сервисов, написанных на разных языках и в разных средах. Каждое из них представляет собой мини-приложение со своими специфическими требованиями к развертыванию, ресурсам, масштабированию и мониторингу. Существует несколько шаблонов развертывания микросервисов.