...

Архитектура ELMA365 и возможности масштабирования нагрузки

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем arestov, 2 май 2024.

  1. arestov

    arestov Участник

    Текст выступления на конференции ELMA Day 2023 Антона Кононова
    [​IMG]
    Содержание:
    Обзор архитектуры. От общего к частному.
    • Архитектура в отдалении
      • Традиционная архитектура в три слоя
      • Каркас общей архитектуры
    • Как устроен веб-клиент
      • Angular
      • Что происходит в браузере
      • Взаимодействие Клиент-Сервер
      • Пользовательские виджет и страницы
      • Приложения
    • Микросервисы
      • Монолит vs Сервисы vs Микросервисы
      • Термин Микросервисы
      • Масштабирование
      • Отказоустойчивость
      • Изоляция
      • Контейнеры
      • Kebernetes
      • Вычисления и хранилище
      • On-Premises + Deckhouse
      • On-Premises Standard
    • Проваливаемся в сервер
      • Из клиента на сервер
      • Основные сервисы системы
      • Другие важные сервисы
      • Системные сервисы
    • Хранилища данных
      • Хранилища в системе
      • Кто нагружает хранилища
      • Хранилища в системе
      • Очередь сообщений
    • Интеграция в IT-ландшафт
      • Планирование размещения
      • Готовый Kubernetes
      • Планирование и анализ нагрузки
      • Планирование отказоустойчивости
    • Масштабирование
      • Автоматическое масштабирование
      • Ручное масштабирование сервисов
    Последнее редактирование модератором: 21 май 2024
  2. arestov

    arestov Участник

    Обзор архитектуры. От общего к частному.
    Архитектура в отдалении

    Традиционная архитектура в три слоя
    [​IMG]


    Архитектура ELMA365 – это классическая трёхзвенная архитектура:

    • Фронтэнд – отвечает за отображение данных;

    • Бэкэнд – занимается вычислениями и обработкой данных;

    • Хранилище данных – хранит, записывает, обновляет и удаляет данные.

    Каркас общей архитектуры
    [​IMG]

    Для реализации каждого из компонентов используются проверенные, открытые и в большинстве своём бесплатные технологии. В справке подробно описаны технологии и протоколы, используемые для реализации архитектуры ELMA365.
    Последнее редактирование модератором: 20 май 2024
  3. arestov

    arestov Участник

    Как устроен веб-клиент

    Angular
    [​IMG]
    Фреймворк Angular разрабатывался Google с 2016 года. На данный момент он является open source проектом. Этот фреймворк используют множество Enterprise проектов.
    В фреймворк всё включено. Он позволяет писать логику и визуальные элементы и не уделять внимание таким деталям, как хранение состояния, локализации, поддержка браузеров на низком уровне и другим вещам, не относящимся непосредственно к формированию визуальной части и взаимодействию с пользователем. Разработчики Angular постоянно реализовывают обновления, ускоряя работу фреймворка и предоставляя новые функции для совершенствования работы разработчиков. Не стоит думать, что Angular тяжёлый и сложный, так как Angular постоянно развивается. В 2024 году вышла очередная версия, которая ускоряет производительность. Команда ELMA365 планирует в этом году перейти на новую версию.
    Поэтому мы выбрали Angular как способ быстрее совершенствовать и улучшать наш продукт.

    Что происходит в браузере

    1. Грузится базовый минимальный набор JS + CSS.

    2. Догружаются части JS + CSS по мере надобности.

    3. Из JS + CSS формируется разметка HTML и отображение в браузере.

    4. Это кэшируется в браузере пользователя.

    5. При обновлении версии на сервере происходит обновление версии на клиенте (релоад страницы).
    Наше приложение устроено по принципу single page application, что подразумевает под собой при первой загрузке загрузку всех основных элементов интерфейса: скрипты, стили, графика. В дальнейшем, при работе с приложением, подгружаются некоторые недостающие части интерфейса, если их не было смысла загружать в самом начале, так называемая частичная загрузка. Всё это кэшируется в браузере пользователя.
    После этого при работе с приложением фронтэнд отправляет на бэкэнд только запросы некоторых кусочков данных для отображения. Это позволяет сильно разгрузить вычислительные ресурсы сервера, сконцентрировать их только на возвращении нужных данных пользователю. Следующая загрузка основных элементов произойдёт при базовом обновлении платформы, так происходит базовое обновление скриптов и стилей.

    Взаимодействие Клиент-Сервер

    [​IMG]
    Взаимодействие Фронтэнда и Бэкэнда происходит по стандартному принципу Клиент-Сервер.
    Для взаимодействия используются стандартные технологии:
    • AJAX запросы (асинхронные HTTP запросы), чтобы получить данные по требованию;

    • WebSocket (постоянно открытая сессия для отправки сообщений Сервер-Клиент и обратно), чтобы в реальном времени получать данные с сервера для чатов, для уведомлений;
    • Для передачи данных между клиентом и сервером используется формат данных JSON.
    Пользовательские виджет и страницы
    1. Загружается информация о шаблоне отображения.

    2. Загружается файл клиентских сценариев (TypeScript, JavaScript).

    3. Грузятся необходимые данные для отрисовки.

    4. Формируется HTML в браузере.
    Отдельно хочется сказать про то, как работают пользовательские виджеты и страницы. Это те самые Low-code виджеты, которые можно создавать в нашей платформе. Сначала загружается небольшая информация о самом шаблоне отображения. Загружается файл клиентских сценариев. То есть у нас есть и серверные скрипты, и клиентские скрипты. Клиентские скрипты попадают напрямую в браузер пользователя. В дальнейшем грузятся остальные необходимые данные, для того чтобы показать пользователю конечный сгенерированный HTML шаблон.

    Приложения
    [​IMG]

    У нас есть десктопное и мобильное приложение, и это на самом деле тонкие обёртки над веб-приложениями.
    На десктопе мы используем Electron. Для мобильных приложений мы используем Cordova.
    Приложения нужны в большей степени для того, чтобы обеспечить пуш-уведомления и дать пользователю более нативный интерфейс взаимодействия.
    Последнее редактирование модератором: 20 май 2024
  4. arestov

    arestov Участник

    Микросервисы
    Монолит vs Сервисы vs Микросервисы
    [​IMG]
    Многие знают, что есть сервисы, монолиты и микросервисы. Для нашей архитектуры мы выбрали микросервисы.

    Термин Микросервисы
    [​IMG]

    В основном, наша команда концентрируется на том, что микросервис должен:
    • Быть изолирован;

    • Владеть своими данными;

    • Уметь независимо обновляться;

    • Быть эластичным (уметь масштабироваться вверх и вниз прозрачно).
    Хочется быстренько пробежаться и сравнить, чем же Монолит отличается от микросервисов. Почему в платформе ELMA365 мы используем микросервисы.

    Масштабирование
    [​IMG]

    Если мы говорим про Монолит, то нам необходимо копировать его полностью и все его ресурсы.
    Таким образом, если есть необходимость добавить ещё один узел для большей обработки, то нужно копировать практически весь узел с ресурсами. Если он занимает 32 Gb оперативной памяти, то нужно добавить ещё 32 Gb.
    В случае с микросервисами, мы, во-первых, увеличиваем количество только нужных нам сервисов и, во-вторых, можем располагать их на более маленьких узлах. То есть брать больше узлов поменьше, поскольку каждый микросервис занимает не такое большое количество ресурсов.

    Отказоустойчивость

    [​IMG]
    Когда узел выходит из строя, то в случае с монолитом мы теряем какой-то огромный кусок вычислительных мощностей, теряем узел. Это может привести к лавинообразному падению, а при этом восстанавливать такой узел монолита тоже может быть накладным.
    В случае с микросервисами, если у нас опять же вычисления распределены по большему количеству, но более маленьких узлов, мы потеряем один узел на нём. Какое-то количество микросервисов можем потерять. Эти сервисы легковесные. Они могут либо восстановиться на других узлах, либо этот маленький узел можно тоже восстановить гораздо меньшими ресурсами и сроками.

    Изоляция
    [​IMG]

    Речь про изоляцию функционала, модулей одного от другого. Если мы в монолите получаем какую-то критичную ошибку в рамках одного модуля, то у нас отказывает или может отказать весь монолит. Он может восстановиться, это может опять же занять какое-то время. Мы можем даже потерять, возможно, узел целиком, если монолит как-то к этому привязан.
    В случае с микросервисами, мы теряем только маленький один сервис или даже реплику одного сервиса. Таким образом, в целом на работоспособности системы это не сказывается, и один маленький микросервис восстанавливается гораздо быстрее.

    Контейнеры
    [​IMG]

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

    Kubernetes

    [​IMG]
    [​IMG]
    Мы используем Kubernetes. Мы устанавливаемся в Kubernetes. Работа нашей платформы без Kubernetes невозможна. В том смысле, что мы привязаны к определённым сущностям. То есть Kubernetes – это тоже получается как платформа своего рода для микросервисов. Kubernetes – это оркестратор. Он открытый, бесплатный, и в том числе один из основных его контрибьюторов и разработчиков компания Google. Kubernetes обеспечивает такие базовые вещи, как связанность между сервисами, масштабирование этих сервисов, отказоустойчивость, мониторинг и много чего ещё. Kubernetes является достаточно большой платформой, обладающей своим набором API для взаимодействия между какими-то узлами или работами в кластере. В основном его задача – это распределять нагрузку по кластеру из нескольких узлов. Будь то узлы реально железные или виртуальные машины. Kubernetes распределяет эти контейнеры как сервисы по всему кластеру. Соответственно не нужно задумываться, какому сервису дать сколько железа. То есть, в случае масштабирования нужно дать больше железа на весь кластер и, допустим, добавить ещё один узел, и Kubernetes позаботится о том, чтобы всё это распределить равномерно.

    Вычисления и хранилище

    [​IMG]
    Отдельно отмечу, что наш кластер вычислений не содержит никакого состояния. Он эластичный, поскольку базируется на микросервисах и на Kubernetes, но кластер хранения данных нужно обслуживать отдельно и соответственно масштабировать тоже отдельно.

    On-Premises + Deckhouse

    [​IMG]
    У нас есть собственно поставка On-Premises, и у нас есть вариант Enterprise. Нами заключено партнёрство с компанией Deckhouse. Если у вас нет своего кластера Kubernetes, вы можете взять инструкцию, развернуть Kubernetes и Deckhouse на нескольких узлах. Делается это очень легко с использованием удобной консоли управления. Туда уже устанавливается наше приложение.

    On-Premises Standard

    [​IMG]
    Для тех, кто хочет только лишь попробовать посмотреть на своём железе, на своих внутренних ресурсах, как это может быть, с чем-то проверить интеграцию, есть поставка On-Premises Standard, которая по сути заключается в так называемом kubernetes in docker. Это один докер контейнер маленький, но внутри него уже развёрнуты Kubernetes с нашими контейнерами. Это всего лишь один контейнер для конечной установки.
    Понятно, что такая поставка не подходит для продуктового использования, так как нет никакой отказоустойчивости. Однако ознакомиться с продуктом, попробовать его в своём закрытом контуре это позволяет.
    Последнее редактирование модератором: 20 май 2024
  5. arestov

    arestov Участник

    Проваливаемся в сервер. Винтики и шестерёнки
    Из клиента на сервер
    [​IMG]
    Давайте немного провалимся глубже и обсудим внутренности сервера. Разберём, что происходит при работе приложения. У нас есть веб-приложение. Оно заходит в INGRESS – это так называемый прокси, который есть в Kubernetes. Он решает на основе маршрутизации веб-запросов, куда направить конкретный трафик. Основная точка входа в нашем приложении – это Main. Сервис Main авторизует пользователя, дополняет контекст запроса определёнными значениями и дальше отправляет его в нужные сервисы, и в том числе сам работает с приложениями. Далее сервисы взаимодействуют между собой, взаимодействуют с хранилищем. В дальнейшем либо веб-запрос возвращается клиенту в Web UI, либо запускается какая-то фоновая работа.

    Основные сервисы системы
    [​IMG]

    Более полный список сервисов с описанием смотрите в справке.
    [​IMG]
    [​IMG]
    Последнее редактирование модератором: 20 май 2024
  6. arestov

    arestov Участник

    Хранилища данных
    Хранилища в системе
    [​IMG]
    В работе нашего приложения используется набор различных баз данных в качестве хранилища данных.

    Основная база данных – это Postgres. В ней хранятся пользовательские данные, конфигурацию которой мы создаём. Основная нагрузка ляжет именно на эту базу данных. Поэтому разворачивая, нужно об этом помнить.

    Есть MongoDB. Она хранит данные чатов, данные ленты, которые либо каналы, либо ассоциированная Лента в приложениях, и некоторые настройки системы. Есть кэш Redis. Он не в состоянии, он без состояний, но тем не менее это отдельная система.

    Rabbit MQ для обмена сообщениями.

    Есть Minio S3. То есть мы поддерживаем протокол S3 для хранения именно бинарных файлов, которые прикладывают пользователи. Но в нашей поставке On-Premises мы рекомендуем разворачивать Minio S3.

    Кто нагружает хранилища
    [​IMG]
    Здесь перечислены сервисы, которые в основном нагружают хранилища:[​IMG]

    • Postgres – collector, main, processor;
    • MongoDB – feeder, chat, settings, auth;
    • Redis – processor, widget, main, auth;
    • Rabbit MQ – worker, processor, scheduler, event-bus;
    • Minio S3 – diskjokey.

    Хранилища в системе
    [​IMG] [​IMG]

    Про хранение файлов часто задают вопрос: «Зачем S3 торчит наружу?». Отмечу, что S3 – это распределённое хранилище, более того геораспределённое. Вы можете реализовать это на своих мощностях. Вы можете использовать какое-то облачное решение, которое геораспределено, и вся задача системы состоит в том, чтобы сформировать для пользователя, когда он нажимает на кнопку Скачать файл, уникальную ссылку. Эта ссылка будет вести пользователя на ближайшее к нему хранилище, соответственно уменьшая затраты на передачу данных и скорость скачивания. Поэтому мы рекомендуем такое решение. У нас есть способ, как можно закрыть в закрытый контур и спрятать S3 за прокси, но это лишит всех преимуществ от использования S3 протокола.

    Кэш нужен для кэширования высоко нагруженных участков кода, для ускорения вычислений. В нашем случае мы не требуем, чтобы Redis сохранялся, его можно просто как in-memory хранилище использовать. Cоответственно его легко потерять и легко восстановить.

    Очередь сообщений
    [​IMG]
    Как очередь сообщений мы используем Rabbit MQ. Она используется для того, чтобы запускать процессы, например, для серверных сценариев, для генерации по шаблонам или для работы таймеров. Или, например, для различных системных событий: редактирование приложений и изменения прав доступа. Все эти события тоже проходят через шину данных.

    Вложения:

    Последнее редактирование модератором: 21 май 2024
  7. arestov

    arestov Участник

    Интеграция в IT-ландшафт
    Планирование размещения
    [​IMG]
    Справка содержит много информации о том, как можно интегрировать систему в свой IT-ландшафт. Там же можно найти, какое количество реплик мы рекомендуем для каких узлов, какие порты нужно открыть и много технической информации, которая поддерживается в актуальном состоянии. При размещении на своих мощностях нужно будет опираться на данные в справке.

    Готовый Kubernetes
    [​IMG]
    Если у вас есть уже готовый Kubernetes, то для него мы можем поставить наше приложение как готовый набор Helm Chart’ов. Поэтому установить наше приложение будет легко, как запустить Helm Install с нашими Chart’ами и выделить определённые ресурсы. После такой установки вы сможете пользоваться системой.

    Есть приложения (сервисы), а есть хранилища. Через Chart’ы также можно развернуть хранилища.

    Планирование и анализ нагрузки
    [​IMG]
    В системе Enterprise мы также предоставляем Chart’ы для анализа нагрузки, для мониторинга, для сбора логов. Это позволит пользоваться проверенными технологиями Grafana, Prometheus, Loki. Из анализа понять, что и где нужно будет подкрутить при необходимости, и отслеживать, как это всё будет работать.

    Планирование отказоустойчивости
    Важно, что при размещении в своей инфраструктуре нужно планировать отказоустойчивость. Здесь я вывел несколько базовых параметров, о которых нужно думать очень подробно, рассуждая про отказоустойчивость.

    Базовые параметры:

    • Резервирование серверов;

    • Резервирование питания (ИБП);

    • Резервирование носителей (RAID);

    • Резервирование сетевого подключения.
    Последнее редактирование модератором: 20 май 2024
  8. arestov

    arestov Участник

    Масштабирование
    Автоматическое масштабирование
    [​IMG]
    Важно понимать, что масштабирование любой системы оценивается под нагрузкой. Нельзя заранее предугадать, сколько реплик и сколько памяти нам нужно выделить, чтобы наши тысячи и десятки тысяч пользователей были счастливы без выделения огромного количества ресурсов. Ресурсы стоят денег, поэтому нужно уметь более оптимально использовать свои ресурсы.

    Существуют определённые, так называемые, механики для автомасштабирования. У нас есть чарты с настройками, которые мы предоставляем дополнительно.

    • Kubernetes HPA (Horizontal Pod Autoscaler)

    • KEDA (Kubernetes Event Driven Autoscaler)

    • minReplicas / maxReplicas

    • Limits / requests (CPU,RAM)

    • GOMAXPROCS (default = 1)

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

    Есть KEDA. Он масштабируется в зависимости от количества сообщений в очередях, от количества запросов, которые поступают в тот или иной сервис. Это позволяет немного опосредовано масштабировать то, что есть. Может быть, что вам вдруг внезапно прилетело большое количество каких-то серверных скриптов и их нужно обработать. Они копятся в очереди. То есть узлы, которые обрабатывают скрипт, они как потребляли свою память и CPU, так и будут потреблять свои ресурсы, на них нагрузка не увеличится. KEDA решает эту проблему. Смотрит, сколько копится сообщений в очереди, и масштабирует узлы, чтобы эту очередь разгребать быстрее.

    Есть параметры minReplicas / maxReplicas. Ими стоит играть для того, чтобы не улететь по ресурсами, когда максимальное количество реплик ограничивается. С другой стороны, он позволяет обеспечить минимально комфортный пользовательский опыт. Например, в понедельник утром, когда все приходят на работу, начинают запускать свои задачи, процессы. В рамках какого-то короткого промежутка времени все это делают. Так вот, чтобы системе не надо было разогреваться, стоит правильно выставить значение минимального количества реплик, чтобы количество реплик находилось в каком-то приемлемом диапазоне.

    Есть лимиты и реквесты по CPU и памяти. На каждом сервисе это можно указать. При этом важно наблюдать под нагрузкой, как себя ведёт сервис в ваших условиях, под вашей нагрузкой, под вашими конкретными пользователями. Вы можете дать ему больше лимитов, больше памяти, больше CPU. Начнёт каждый сервис каждая реплика обрабатывать больше какой-то работы. И это может быть правильно для определённого профиля нагрузки. С другой стороны, нужно больше сервисов. И не нужно там ему увеличивать лимитов. Тот же самый варкер, где речь идёт про параллельное исполнение, чтобы исполнять больше скриптов, и в данном случае просто нужно большее количество реплик.

    GOMAXPROCS влияет на параллельность исполнения в рамках одной реплики сервиса. Чем он больше, тем большее количество ядер нужно этому сервису, этой реплике. Если определённые ресурсы есть для выделения под высокие нагрузки, а здесь уже речь про высокие нагрузки, в данному случае тоже стоит обратить внимание на этот параметр параллельности сервиса.

    Ручное масштабирование сервисов
    [​IMG]
    Стоит отметить каждый из самых основных сервисов.

    Самый первый сервис – это worker, который исполняет непосредственно скрипты. Если большое количество скриптов выполняется в моменте, у вас может копиться очередь скриптов. Или вы планируете запускать много методов API, у вас в процессах может быть очень много скриптов. Может вы планируете пользовательский интерфейс на виджетах с серверной логикой. Соответственно вам нужно больше worker’ов по количеству.

    Worker-gateway – сервис, который рядом с worker’ом живёт. Он обслуживает именно запросы из скриптов в нашу систему: то, что можно TS SDK написать, – различные Селекты, фетчи элементов приложений, какие-то вызовы асинхронных операций (переназначение задачи или согласование документов). Всё это проходит через worker-gateway. Поэтому при увеличении количества worker’ов и большом количестве подобных операций в скриптах нужно worker-gateway также масштабировать.

    Main – основной сервис, являющийся точкой входа. Через него проходят все запросы. Также он отвечает за работу элементов приложений, то есть пользовательских приложений, которые вы создаёте в конфигурацию. Main их обрабатывает, сохраняет в базу данных. Если у вас много пользователей, ведётся активная работа с этим данными с точки зрения записей, обновления, то нужно масштабировать Main. Нужно масштабировать как по количеству, так и по объёму памяти, большему процессору. Он всё это переварит и будет потреблять те ресурсы, которые вы ему дадите.

    Processor – это сервис, который исполняет бизнес-процессы. Бизнес-процессы исполняются внутри сервиса за счёт goroutine, в памяти. Поэтому если у вас много экземпляров процессов, или может много запуститься, или обрабатывается пачками по какому-то расписанию, то этому процессу можно дать больше ресурсов, больше экземпляров. В случае падения одного микросервиса система не порушится, но те экземпляры процессов, которые исполнялись, нужно будет заново поднимать и запускать. Поэтому чем больше будет экземпляров процесса, тем область потери будет меньше.

    Auth, settings, collector – в основном нужны для того, чтобы обеспечить чтение тех пользователей, которые пришли посмотреть. Посмотреть список задач, какой-то отчёт, приложения профильтровать. То есть пользователи не выполняют активной работы. Если таких пользователей много, то эти сервисы можно масштабировать, они дадут существенный прирост.

    Aspose-actions, templater, template-mapper – они делают работу с шаблонами, шаблонами наименований элементов приложений в различных бизнес-процессах, для различных сообщений, для документов, – в общем, всего, что связано с шаблонами. Если шаблоны используются активно, стоит посмотреть в сторону расширения этих сервисов, чтобы данные функции начали работать быстрее.

    Disklockey – работает с файлами. Если пользователи активно загружают файлы в систему, сохраняются файлы, приложения с файлами, скачивают много файлов, то нужно масштабировать этот сервис, дать ему больше реплик. Так он будет обслужить больше запросов, и пользователь будет счастлив.
    Последнее редактирование модератором: 20 май 2024