Разработка и масштабирование микросервиса

Разработка и масштабирование микросервиса

В этой статье разработчики компании DST Global знакомят с ключевыми моментами разработки сервисов и технологиями, которые следует учитывать при создании и масштабировании микросервиса.

Службы или серверы — это программные компоненты или процессы, которые выполняют операции с указанными входными данными, производя либо действия, либо данные в зависимости от их цели. Сторона, отправляющая запрос, является клиентом, а сервер управляет процессом запроса. Обычно связь между клиентом и сервером происходит по сети с использованием таких протоколов, как HTTP для REST или gRPC. Службы могут включать пользовательский интерфейс (UI) или функционировать исключительно как серверные процессы. Имея этот опыт, мы можем изучить шаги и обоснование разработки масштабируемого сервиса.

ПРИМЕЧАНИЕ. В этой статье не приводятся инструкции по разработке служб или пользовательского интерфейса, поэтому вы можете выбрать язык или технологический стек, соответствующий вашим требованиям. Вместо этого он предлагает комплексный взгляд на создание и расширение услуги, отражая то, что стартапам необходимо делать для масштабирования услуги. Кроме того, важно понимать, что, хотя этот подход дает ценную информацию о концепциях вычислений, он не является единственным методом проектирования систем.

Начало: контроль версий

При условии ясности относительно наличия пользовательского интерфейса и общего назначения службы, первый шаг перед разработкой службы включает внедрение системы контроля версий/системы контроля версий для поддержки кода. Обычно это влечет за собой использование таких инструментов, как Git, Mercurial и других, для резервного копирования кода и облегчения совместной работы, особенно по мере роста числа участников. Стартапы обычно начинают с Git в качестве системы контроля версий, часто используя такие платформы, как github, для размещения репозиториев Git.

Важным элементом контроля версий являются запросы на включение, облегчающие коллегиальную проверку внутри вашей команды. Этот процесс повышает качество кода, позволяя нескольким людям просматривать и утверждать предлагаемые изменения перед интеграцией. Хотя я не буду здесь вдаваться в подробности, быстрый поиск в Интернете предоставит достаточно информации по этой теме.

Разработка сервиса

После установки контроля версий следующим шагом будет настройка репозитория и начало разработки сервиса. В этой статье применяется независимый от языка подход, поскольку углубление в конкретные языки и оптимальные технологические стеки для каждой сервисной функции было бы слишком подробным. Для краткости давайте сосредоточимся на сервисе, который выполняет функции на основе входных данных и требует внутреннего хранилища (при этом оставаясь нейтральным в отношении решения для хранения, которое будет обсуждаться позже).

Когда вы начинаете разработку сервиса, очень важно понять, как запускать его локально на своем ноутбуке или в любой среде разработки. Следует внимательно отнестись к этому аспекту, поскольку локальное тестирование играет решающую роль в эффективной разработке. При разработке службы убедитесь, что классы, функции и другие компоненты организованы по модульному принципу и при необходимости в отдельные файлы. Такой организационный подход способствует созданию структурированного репозитория и обеспечивает всесторонний охват модульными тестами.

Модульные тесты представляют собой важнейший аспект тестирования, которому разработчики должны уделять пристальное внимание. В этом вопросе не должно быть никаких компромиссов! Бесчисленные инциденты или производственные проблемы можно было бы предотвратить, проведя несколько модульных тестов. Пренебрежение этой практикой потенциально может повлечь за собой значительные финансовые затраты для компании.

Разработчики DST Global не будут углубляться в особенности интеграции фреймворка gRPC, пакетов REST или любых других протоколов связи. У вас будет свобода исследовать и реализовывать их по мере разработки сервиса.

Как только сервис станет исполняемым и протестирован с помощью модульных тестов и базового ручного тестирования, следующим шагом будет изучение того, как сделать его «развертываемым».

Упаковка услуги

Обеспечение возможности развертывания службы подразумевает наличие метода более управляемого запуска процесса. Давайте углубимся в это понятие дальше. Что именно это влечет за собой? Теперь, когда у нас есть работоспособный процесс, кто будет инициировать его изначально? Тем более, где оно будет исполняться? Ответ на эти вопросы имеет решающее значение, и теперь мы продолжим давать ответы.

По моему скромному мнению, управление собственной вычислительной инфраструктурой — не лучший подход. Обеспечение доступности вашего сервиса в Интернете требует множества сложностей. Выбор поставщика облачных услуг (CSP) — более разумный выбор, поскольку он решает большую часть сложностей «за кулисами». Для наших целей будет достаточно любого доступного поставщика облачных услуг.

После выбора CSP следующим вопросом является управление процессом. Мы стремимся избегать ручного вмешательства каждый раз при сбое службы, особенно без уведомления. Решение заключается в организации нашего процесса посредством контейнеризации. Это включает в себя создание образа контейнера для нашего процесса, по сути, файловой системы, содержащей все необходимые зависимости на уровне приложения. «Dockerfile» используется для указания шагов по включению процесса и зависимостей в образ контейнера. После завершения Dockerfile можно использовать консоль сборки Docker для создания образа с тегами. Затем этот образ сохраняется локально или помещается в реестр контейнеров, служащий репозиторием для образов контейнеров, которые позже можно загрузить в вычислительный экземпляр. После описания этих шагов возникает следующий вопрос: как контейнеризация управляет нашим процессом? Это будет рассмотрено в следующем разделе, посвященном выполнению контейнера.

Выполнение контейнера

После создания образа контейнера следующим шагом является его выполнение, которое, в свою очередь, запускает разработанный нами сервис. Для облегчения этого процесса доступны различные среды выполнения контейнеров, такие как Containerd, podman и другие. В этом контексте мы используем интерфейс «docker» для управления контейнером, который взаимодействует с контейнером в фоновом режиме. Запуск контейнера прост: «запуск докера» запускает контейнер и, следовательно, разработанный процесс. Вы можете просматривать журналы в терминале (если он не запущен как демон) или использовать «журналы докера» для проверки журналов службы, если это необходимо. Кроме того, в команду можно включить такие параметры, как «--restart», для автоматического перезапуска контейнера (т. е. процесса) в случае сбоя, что позволяет выполнить настройку по мере необходимости.

На этом этапе наш процесс инкапсулирован в контейнер и готов к выполнению/оркестрации по мере необходимости. Хотя эта настройка подходит для локального тестирования, наш следующий шаг предполагает изучение того, как развернуть ее на базовом вычислительном экземпляре в рамках выбранного нами CSP.

Развертывание контейнера

Теперь, когда у нас есть контейнер, желательно опубликовать его в реестре контейнеров. Доступны многочисленные реестры контейнеров, управляемые CSP или самим докером. После публикации контейнера он становится легко доступен с любого CSP или платформы. Мы можем извлечь образ и запустить его на вычислительном экземпляре, таком как виртуальная машина (ВМ), выделенном в CSP. Начать с этого варианта, как правило, является наиболее экономичным и простым. Хотя мы кратко коснемся других форм вычислительной инфраструктуры далее в этой статье, развертывание на виртуальной машине включает в себя получение образа контейнера и его запуск, так же, как мы это делали в нашей среде разработки.

Все! Наш сервис развернут. Однако обеспечение доступности миру требует тщательного рассмотрения. Хотя прямой доступ к IP-адресу виртуальной машины внешнему миру может показаться заманчивым, это создает угрозу безопасности. Внедрение TLS для обеспечения безопасности имеет решающее значение. Вместо этого лучший подход предполагает использование обратного прокси-сервера для маршрутизации запросов к конкретным службам. Это обеспечивает безопасность и облегчает развертывание нескольких сервисов на одной виртуальной машине.

Чтобы обеспечить доступ к нашей службе через Интернет, нам нужен метод, по которому входящий трафик может достичь нашей виртуальной машины. Эффективное решение предполагает установку обратного прокси-сервера, такого как Nginx, непосредственно на виртуальной машине. Этого можно добиться, извлекая образ контейнера Nginx, обычно помеченный как «nginx:latest». Перед запуском контейнера необходимо настроить параметры Nginx, такие как серверы, местоположения и дополнительные конфигурации. Для повышения защиты также можно реализовать такие меры безопасности, как TLS.

После того как конфигурация Nginx установлена, она может быть доступна контейнеру через тома во время выполнения контейнера. Эта настройка позволяет обратному прокси-серверу эффективно маршрутизировать входящие запросы к контейнеру, работающему на той же виртуальной машине, используя указанный порт. Одним из заметных преимуществ является возможность размещения нескольких служб внутри виртуальной машины с эффективным управлением маршрутизацией с помощью обратного прокси-сервера.

Чтобы завершить настройку, мы должны предоставить IP-адрес виртуальной машины и порт прокси-сервера Интернету с шифрованием TLS, поддерживаемым обратным прокси-сервером. Эту настройку конфигурации обычно можно настроить через настройки CSP.

ПРИМЕЧАНИЕ. В приведенных ниже примерах решений в качестве CSP может использоваться ссылка на GCP. Это сделано исключительно в иллюстративных целях и не должно интерпретироваться как рекомендация. Целью является исключительно эффективная передача концепций.

Рассмотрим сценарий, в котором управление одной виртуальной машиной вручную становится трудоемким и не обеспечивает масштабируемости. Чтобы решить эту проблему, CSP предлагают решения, аналогичные управляемым группам экземпляров, состоящим из нескольких одинаково настроенных виртуальных машин. Эти группы часто имеют такие функции, как сценарии запуска, которые выполняются при инициализации виртуальной машины. Все конфигурации, обсуждавшиеся ранее, можно включить в эти сценарии запуска, что упрощает процесс запуска виртуальной машины и повышает масштабируемость. Эта настройка оказывается полезной, когда для эффективной обработки запросов требуется несколько виртуальных машин.

Теперь возникает вопрос: как нам решить, куда направлять запросы, когда мы имеем дело с несколькими виртуальными машинами? Решение состоит в том, чтобы использовать балансировщик нагрузки, предоставляемый CSP. Этот балансировщик нагрузки выбирает одну виртуальную машину из пула для обработки каждого запроса. Кроме того, мы можем упростить процесс, внедрив общую балансировку нагрузки. Чтобы удалить отдельные обратные прокси-серверы, мы можем использовать несколько групп экземпляров для каждой необходимой службы, сопровождаемые балансировщиками нагрузки для каждой. Общий балансировщик нагрузки может предоставить свой IP-адрес с помощью конфигурации TLS и настройки маршрута, гарантируя, что на виртуальной машине будут работать только сервисные контейнеры. Крайне важно гарантировать, что IP-адреса и порты виртуальных машин доступны исключительно балансировщику нагрузки на входном пути. Эту задачу можно решить с помощью конфигураций, предоставляемых CSP.

На данный момент у нас есть балансировщик нагрузки, который безопасно управляет запросами, направляя их в конкретный контейнерный сервис внутри виртуальной машины из пула виртуальных машин. Сама эта настройка способствует масштабированию нашего сервиса. Для дальнейшего повышения масштабируемости и устранения необходимости непрерывной работы виртуальных машин мы можем выбрать политику автомасштабирования. Эта политика динамически масштабирует группу виртуальных машин вверх или вниз на основе таких параметров, как ЦП, память или другие параметры, предоставленные CSP.

Теперь давайте углубимся в концепцию «Инфраструктура как код» (IaC), которая имеет большое значение для эффективного управления компонентами CSP, способствующими масштабированию. По сути, IaC включает в себя управление компонентами инфраструктуры CSP через файлы конфигурации, интерпретируемые инструментом IaC (например, Terraform) для соответствующего управления инфраструктурой CSP. Для более подробной информации обратитесь к вики.

Хранилище данных

Ранее мы обсуждали масштабирование нашего сервиса, но важно помнить, что обычно требуется где-то поддерживать состояние. Именно здесь базы данных или хранилища данных играют ключевую роль. По опыту, справиться с этим аспектом может быть довольно сложно, и я бы еще раз посоветовал не разрабатывать собственное решение. Решения CSP идеально подходят для этой цели. CSP обычно справляются со сложностями, связанными с управлением базами данных, более эффективно реализуя такие концепции, как архитектура «главный-подчиненный», управление репликами, синхронно-асинхронная репликация, резервное копирование/восстановление, согласованность и другие сложные аспекты. Управление базой данных может оказаться сложной задачей из-за опасений потери данных из-за неправильной конфигурации. Каждый CSP предлагает разные предложения баз данных, и важно учитывать конкретные случаи использования, с которыми работает служба, чтобы выбрать подходящее предложение. Например, может потребоваться выбрать между использованием реляционной базы данных и предложением NoSQL. В данной статье не рассматриваются эти различия. База данных должна быть доступна из группы виртуальных машин и служить центральным хранилищем данных для всех экземпляров, состояние которых является общим. Стоит отметить, что база данных или хранилище данных должны быть доступны только внутри VPC, а в идеале — только из группы виртуальных машин. Это крайне важно для предотвращения раскрытия входящего IP-адреса базы данных, обеспечивая безопасность и целостность данных.

Queues

При проектировании сервисов мы часто сталкиваемся со сценариями, в которых определенные задачи необходимо выполнять асинхронно. Это значит, что при получении запроса часть обработки можно перенести на более позднее время, не блокируя ответ клиенту. Одним из распространенных подходов является использование баз данных в качестве очередей, в которых запросы упорядочиваются по некоторому идентификатору. службы CSP, такие как Amazon SQS или GCP pub/sub В качестве альтернативы для этой цели можно использовать . Сообщения, опубликованные в очереди, затем могут быть получены для обработки отдельной службой, которая прослушивает очередь. Однако мы не будем здесь углубляться в подробности.

Мониторинг

В дополнение к мониторингу на уровне виртуальных машин, обычно предоставляемому CSP, может возникнуть необходимость в более детальной информации посредством мониторинга уровня обслуживания. Например, могут потребоваться метрики задержки для запросов к базе данных, метрики, основанные на взаимодействии с очередями, или метрики для службы ЦП и использования памяти. Эти метрики следует собирать и передавать в решение для мониторинга, такое как Datadog, Prometheus или другие. Эти решения обычно поддерживаются базой данных временных рядов (TSDB), что позволяет пользователям получать представление о состоянии системы за определенный период времени. Эта настройка мониторинга также облегчает отладку определенных типов проблем и может вызывать оповещения или сигналы тревоги, если это настроено. Альтернативно вы можете настроить собственное развертывание Prometheus, поскольку это решение с открытым исходным кодом.

Учитывая вышеупомянутые концепции, должно быть возможным развертывание масштабируемой службы. Такого уровня масштабируемости оказалось достаточно для многочисленных стартапов, которым я давал консультации. В дальнейшем мы рассмотрим использование «оркестратора контейнеров» вместо развертывания контейнеров на виртуальных машинах, как описано ранее. В этой статье мы будем использовать Kubernetes (k8s) в качестве примера, чтобы проиллюстрировать этот переход.

Оркестрация контейнеров: знакомьтесь с Kubernetes (K8s)

Внедрив вышеупомянутую конструкцию, мы можем эффективно управлять многочисленными запросами к нашему сервису. Теперь наша цель — добиться разделения для дальнейшего повышения масштабируемости. Такое разделение имеет решающее значение, поскольку ошибка в любой службе внутри виртуальной машины может привести к сбою виртуальной машины, что потенциально может привести к сбою всей экосистемы. Более того, разделенные сервисы можно масштабировать независимо. Например, один сервис может иметь достаточную масштабируемость и эффективно обрабатывать запросы, а другой может плохо справляться с нагрузкой. Рассмотрим пример торгового веб-сайта, где каталог может получить значительно больше посещений, чем страница оформления заказа. Следовательно, масштаб запросов на чтение может значительно превышать масштаб проверок. В таких случаях развертывание нескольких сервисных контейнеров в Kubernetes (K8s) в качестве отдельных сервисов обеспечивает независимое масштабирование.

Прежде чем углубляться в подробности, стоит отметить, что CSP предлагают Kubernetes в качестве варианта вычислительной платформы, что необходимо для масштабирования на следующий уровень.

Kubernetes (K8s)

В этой статье мы не будем углубляться в тонкости контроллеров Kubernetes или другие аспекты. Предоставленной здесь информации будет достаточно для развертывания сервиса в Kubernetes. Kubernetes (K8s) служит абстракцией кластера узлов с ресурсами хранения и вычислений. В зависимости от того, где запланирована услуга, узел предоставляет необходимые возможности вычислений и хранения.

Наличие образов контейнеров необходимо для развертывания службы в Kubernetes (K8s). Ресурсы в K8s представлены путем создания конфигураций, которые могут быть в формате YAML или JSON, и они определяют конкретные объекты K8s. Эти объекты принадлежат определенному «пространству имен» внутри кластера K8s. Базовой единицей вычислений в K8s является «Под», который может запускать один или несколько контейнеров. Таким образом, можно создать конфигурацию для модуля, а затем развернуть службу в пространстве имен с помощью интерфейса командной строки K8s, kubectl . После создания модуля ваш сервис по сути работает, и вы можете отслеживать его состояние с помощью kubectl с пространством имен в качестве параметра.

Для развертывания нескольких модулей требуется «развертывание». Kubernetes (K8s) предлагает различные ресурсы, такие как развертывания, наборы с отслеживанием состояния и наборы демонов. Документация K8s дает достаточные объяснения этих абстракций, мы не будем здесь обсуждать каждую из них. Развертывание — это, по сути, ресурс, предназначенный для развертывания нескольких модулей одинакового типа. Это достигается за счет опции «реплики» в конфигурации, также вы можете выбрать стратегию обновления в соответствии с вашими требованиями. Выбор подходящей стратегии обновления имеет решающее значение для обеспечения отсутствия простоев во время обновлений. Поэтому в нашем сценарии мы будем использовать развертывание нашего сервиса, которое масштабируется до нескольких модулей.

При использовании развертывания для наблюдения за вашим приложением модули могут создаваться и завершаться динамически. Следовательно, количество и идентификаторы работающих и исправных модулей могут меняться непредсказуемо. Kubernetes управляет созданием и удалением подов для поддержания желаемого состояния вашего кластера, рассматривая поды как временные ресурсы без гарантированной надежности и долговечности. Каждому поду назначается собственный IP-адрес, обычно управляемый сетевыми плагинами в Kubernetes. В результате набор модулей, связанных с развертыванием, может меняться с течением времени, что затрудняет последовательное обнаружение и взаимодействие компонентов кластера с конкретными модулями. Эта проблема решается за счет использования ресурса службы .

После создания объекта службы следующей темой обсуждения является Ingress . Ingress отвечает за маршрутизацию к нескольким службам внутри кластера. Это облегчает доступ к маршрутам HTTP, HTTPS или даже gRPC извне кластера к службам внутри него. Маршрутизация трафика управляется правилами, указанными на ресурсе Ingress, который поддерживается балансировщиком нагрузки, работающим в фоновом режиме.

Благодаря внедрению всех этих компонентов наш сервис достиг похвального уровня масштабируемости. Стоит отметить, что концепции, обсуждавшиеся до перехода к сфере Kubernetes, в некотором смысле здесь отражены — у нас есть балансировщики нагрузки, контейнеры и маршруты, хотя и реализованные по-другому. Кроме того, существуют другие объекты, такие как Horizontal Pod Autoscaler (HPA) для масштабирования модулей на основе использования памяти/ЦП, а также конструкции хранения, такие как постоянные тома (PV) или постоянные тома (PVC), в которые мы не будем подробно углубляться. Не стесняйтесь изучить их для более глубокого понимания.

CI/CD

Наконец, я хотел бы остановиться на важном аспекте повышения эффективности разработки: непрерывной интеграции/развертывании (CI/CD). Непрерывная интеграция (CI) предполагает выполнение автоматических тестов (таких как модульные, сквозные или интеграционные тесты) по любому запросу разработчика или регистрации в системе контроля версий, обычно перед слиянием. Это помогает выявить регрессии и ошибки на ранних этапах процесса разработки. После слияния CI генерирует изображения и другие артефакты, необходимые для развертывания службы. Такие инструменты, как Jenkins (Jenkins X), Tekton, Git action и другие, облегчают процессы CI.

Непрерывное развертывание (CD) автоматизирует процесс развертывания, подготавливая различные среды для развертывания, такие как разработка, промежуточное размещение или производство. Обычно сначала развертывается среда разработки, а затем выполняется несколько комплексных тестов для выявления любых проблем. Если все работает правильно, компакт-диск приступает к развертыванию в других средах. Все вышеупомянутые инструменты также поддерживают функции компакт-диска.

Инструменты CI/CD значительно повышают эффективность разработки за счет сокращения ручной работы. Они необходимы для того, чтобы разработчики не тратили часы на выполнение задач вручную. Кроме того, во время развертывания вручную крайне важно убедиться, что никто другой не выполняет развертывание в той же среде одновременно, чтобы избежать конфликтов — проблемы, которую можно эффективно решить с помощью нашей инфраструктуры компакт-дисков.

Существуют и другие аспекты, такие как динамическое управление конфигурациями и безопасное хранение секретов/паролей и система журналирования, хотя мы не будем вдаваться в подробности.

Начать дискуссию