«Битрикс24» — продукт, который позволяет клиентам удобно управлять бизнес-процессами и эффективно использовать доступные данные для глубокой аналитики. В последнем особенно помогает «BI Конструктор» — внутреннее решение, с помощью которого можно реализовывать даже самую сложную аналитику и прозрачно контролировать все метрики.
Меня зовут Александр Сербул. Я руководитель больших данных, высоконагруженных систем и машинного обучения «Битрикс24». В этой статье я расскажу, как мы строили «BI Конструктор»: какие требования предъявляли к решению, как решали возникающие задачи и что получили в результате.
Статья подготовлена по мотивам моего доклада на VK Cloud Conf 24. Вы можете посмотреть его здесь.
Контекст и исходные требования
Чтобы удовлетворять запросы каждого клиента и предоставлять более широкую функциональность, мы хотели дать пользователям возможность выполнять бизнес-аналитику на данных внутри портала «Битрикс24».
При этом мы отталкивались от потребностей клиентов, поэтому изначально предъявляли к решению несколько требований. Среди них:
BI-аналитика на данных внутри портала «Битрикс24» и быстрое открытие дашбордов;
возможность пересборки дашборда и фильтрации данных на лету;
наличие типовых дашбордов и возможности быстрого создания своих;
возможность объединения данных разных сущностей, например лиды, товары, суммы и другие;
наличие подробной документации и возможность развития BI-инструмента независимо от «Битрикс24»;
отсутствие рисков внезапного закрытия или недоступности из-за санкций и других ограничений.
Декомпозиция по целям как способ удовлетворить все запросы
Анализ рынка показал, что готовых коробок, способных закрыть все потребности «здесь и сейчас», просто нет. Поэтому, чтобы удовлетворить все запросы, нам требовалось разделить задачу на подзадачи и решать их независимо.
BI-аналитика на данных портала «Битрикс24». Чтобы предоставить клиентам возможность BI-аналитики, мы стали отдавать из портала «Битрикс24» через BI-коннектор JSON-файлы с нужными сырыми данными в формате простой и понятной таблицы с колонками и строками.
Быстрое открытие дашбордов. MySQL и подобные решения не могут обеспечить оперативное открытие дашбордов. Поэтому нам требовался быстрый кластерный OLAP-движок для BI. Причем готовый, Open Source.
Возможность пересборки дашборда и фильтрации данных на лету. Для этого нам нужно было найти решение, способное выполнять функции «BI-мордочки», то есть интерфейса с соответствующими возможностями перестраивания рабочего пространства.
Наличие типовых дашбордов и возможности быстрого создания своих. Чтобы дать возможность гибко работать с существующими и новыми дашбордами, у «BI-мордочки» должно быть API. Примечательно, что многие доступные решения под эти задачи либо неоправданно дорогие, либо дают очень ограниченные возможности.
Возможность объединения данных разных сущностей. Для удовлетворения этого требования важно, чтобы ВI-OLAP-движок умел джойнить данные из разных источников.
Наличие подробной документации и возможность развития BI-инструмента независимо от «Битрикс24». Соответственно, нужны Open-Source-компоненты с развитым и активным комьюнити.
Отсутствие рисков внезапного закрытия или недоступности из-за санкций и других ограничений. Эта боль закрывается размещением Open Source в облаке российского провайдера.
Подобная декомпозиция задачи позволила нам выделить основные векторы реализации BI-инструмента. Но на этом всё не закончилось — помимо технических требований, у нас были и запросы от бизнеса. Так, нам было важно:
обеспечить быстрое масштабирование BI-системы на десятки тысяч компаний в России;
реализовать продукт за полгода — релизный цикл.
Это, очевидно, существенно осложняло задачу, но вариант отказа от создания BI-сервиса мы даже не рассматривали. Поэтому мы решили строить решение на базе готовых компонентов и приступили к реализации.
От требований к реализации
Эффективная отдача данных с портала «Битрикс24»
В первую очередь мы начали с оптимизации отдачи JSON-данных с портала «Битрикс24».
Здесь примечательно, что сейчас тренд — не централизованное, а разрозненное хранение данных, при котором данные лежат в той системе, к которой относятся. Чтобы следовать тренду, мы изначально проработали:
поддержку выбора и отдачи отдельных колонок данных — можем отдавать данные выборочно;
поддержку фильтрации данных по дате и выбор колонки применения фильтра по дате;
поддержку LIMIT — можем передавать по запросу нужное количество строк;
поддержку фильтрации колонок по логическим предикатам.
Таким образом, мы обеспечили возможность забирать только нужные данные из MySQL, в котором портал «Битрикс24» хранит данные.
Trino с плагином как read-only OLAP
Чтобы обеспечить быстрое открытие дашбордов, нам нужен был быстродействующий кластерный BI-движок OLAP.
После поиска и изучения доступных вариантов мы остановились на Trino. Среди причин выбора именно этого инструмента были ориентированность на бизнес, поддержка традиционного ANSI SQL, быстрое оперирование запросами и данными. Кроме того, сервис активно используют крупные компании для OLAP-аналитики.
В итоге мы написали к Trino плагин на Java, который ходит за JSON к BI-коннектору, и получили read-only OLAP-решение под свои задачи.
Apache Superset — легкая «BI-мордочка»
Далее наша задача состояла в поиске удобного интерфейса, в котором можно было бы перестраивать дашборды по клику и фильтровать данные на лету.
Под эти задачи мы выбрали Apache Superset — активно развивающийся Open Source BI-проект, который позволяет строить легкий семантический BI-слой с Drag&Drop вместо программирования.
Среди прочего нам было важно, что Apache Superset поддерживает интерактивную работу с графиками (React), а также может работать с Trino и десятками других SQL-хранилищ. Соответственно, с помощью этого сервиса мы смогли закрыть потребность в гибком интерфейсе.
Одновременно с выбором Apache Superset мы также нативно закрыли запрос на кастомизацию дашбордов. Здесь помогло то, что у Apache Superset есть большое API, благодаря которому можно не только использовать готовые (типовые) дашборды, но и загружать новые.
Собственно, так мы и сделали — настроили импорт ролей, прав и типовых дашбордов из портала «Битрикс24» в Apache Superset через его API.
Стоит отметить, что на каждого клиента мы выделяем свою инсталляцию Apache Superset с 512 МБ оперативной памяти.
«Пересечения» BI-данных
Далее стал вопрос обеспечения возможности консолидации данных, чтобы «собирать конструктор» из нужных данных из разных таблиц.
Изначально у нас был запрос на ВI OLAP-движок, способный выполнять JOIN данных.
Чтобы достичь этого, мы решили комбинировать возможности уже выбранных компонентов. Так, в нашей реализации:
для JOIN мы применили Trino (Apache Superset не умеет объединять данные). При этом Trino не хранит данные, а только запрашивает их, выполняет SQL-операции и отдает результат;
Apache Superset сохраняет подзапросы с JOIN как «виртуальные датасеты» для дашбордов на его основе.
В результате мы получили удобный и быстрый механизм джойна данных из разных источников.
No vendor lock
Наряду с остальными аспектами нам было важно, чтобы выстраиваемая BI-система могла развиваться независимо от «Битрикс24» и была защищена от рисков Vendor lock-in.
Вместе с тем в нашей реализации эти потребности закрывались нативно:
Trino, Apache Superset, Java — открытые и развиваемые сообществом инструменты с документацией и открытым кодом, на которые можно переселиться.
Мы развернули решение на мощностях облачного провайдера VK Cloud: подняли на платформе виртуальные машины, используем облачное S3-совместимое хранилище и другие сервисы.
Таким образом мы гарантируем, что наша реализация всегда будет доступной.
От реализации к первым вызовам
После получения реализации и даже ее демонстрации бизнесу мы убедились, что проект работает успешно и закрывает все запросы потенциальных клиентов. Но одновременно мы неожиданно столкнулись с тем, что решение работает ограниченно — исключительно на ноутбуке разработчика :) С масштабированием BI-системы на всех пользователей возникали трудности.
Проблема оказалась в том, что Apache Superset «не запускается без контейнера»: чтобы установить Apache Superset на сервер, нужны сложные и не всегда очевидные зависимости в библиотеках, что делало инсталляцию затруднительной, если не невозможной.
В итоге после недель безрезультатных попыток мы решили попробовать запустить Superset в Docker-контейнере и, к счастью, получили ожидаемый результат — всё заработало успешно. Аналогично решили поступить с Trino — его тоже успешно подняли и запустили в Docker-контейнере. Следом проделали то же самое с MySQL. В результате мы получили Docker Compose на 10 контейнеров, в котором всё работает стабильно и прогнозируемо — убедиться в этом помогло нагрузочное тестирование внутри Docker Compose: MySQL, Nginx, Redis (для кэша), Superset, Trino (с плагином), портал «Битрикс24».
Контейнеры в запущенном Docker Compose
Развертывание в облаке, или жизнь после Docker Compose
После «оборачивания» всех компонентов конфигурации в контейнеры мы фактически получили готовый продукт. Далее нам предстояло масштабировать решение на всех клиентов «Битрикс24», то есть надо было развернуть его в облаке. Здесь мы столкнулись с тем, что с Docker Compose реализовать подобную задачу сложно, особенно без глубокой экспертности в этом вопросе.
Поэтому мы сначала перенесли Docker Compose на Minikube, а потом и вовсе решили перейти к «полноразмерному» Kubernetes.
Более того, после изучения литературы, курсов и других доступных материалов мы поняли, что нам нужен не «чистый» K8s, работа с которым подразумевает «копание под капотом», а управляемый Kubernetes, за поддержку, функционирование и обновление которого будет отвечать вендор.
Мы выбрали облачный управляемый Kubernetes в VK Cloud (Cloud Containers). Выбор в пользу такой реализации позволил нам оставить в своей зоне ответственности только верхнеуровневое администрирование, снизить порог входа в работу с K8s и сократить любые потенциальные издержки.
Архитектура решения внутри VK Cloud
В итоге мы выстроили с помощью сервисов и мощностей облака VK Cloud требуемую отказоустойчивую конфигурацию, которая стабильно выдерживает высокие нагрузки.
Сейчас у нас поднят в VK Cloud управляемый Kubernetes на более 25 000 подов и более 200 серверов. Внутри каждого пода контейнер Superset (512 МБ оперативной памяти) и Redis (64 МБ памяти кэша).
MySQL для контейнеров Superset мы держим снаружи: хранить БД внутри K8s — не лучший вариант (хотя приходит понимание, что это утверждение тоже становится архаизмом и БД в K8s запускают все чаще).
Для подключения клиентов мы выполняем установку helm-приложения со своими конфигами. При этом, по рекомендации опытных инженеров K8s VK Cloud, релизы helm мы перенесли в Postgres-бэкенд, поскольку со временем они просто перестали помещаться в память внутреннего хранилища K8s на базе etcd.
В Ingress-контроллере вместо стандартного аддона ingress-nginx мы используем contour-envoy. Такой переход был обусловлен тем, что ingress-nginx при добавлении нового клиента по умолчанию обновляет полностью весь конфиг, и уже на 5000 клиентов это стало значительной проблемой. С переходом на contour-envoy алгоритм действий при добавлении клиентов изменился (конфиг не перечитывается, а просто и быстро меняется в памяти, регулярно автоматически сохраняясь на диск), и проблема была решена.
Управляющее внутреннее API у нас реализовано на python/flask. Мониторинг Kubernetes мы осуществляем с помощью аддона kube prometheus stack, а мониторинг приложения — используя экспорт метрик Prometheus из Flask API, Prometheus, Grafana.
Что касается аддонов. Нам удобно пользоваться стандартными аддонами K8s от VK Cloud — они созданы и поддерживаются провайдером и быстро решают возникающие бизнес-задачи. Вместе с тем мы всегда можем поменять их на более кастомизированное решение, как мы и сделали, заменив ingress-nginx на contour-envoy.
Итоговый алгоритм работы с компаниями-клиентами
После создания и вывода в прод новой BI-системы «Битрикс24», мы выстроили следующий порядок работы с клиентами продукта.
Каждая компания-клиент получает персональный под с уникальным доменным именем. Внутри пода — Apache Superset и Redis.
Обновление конфигов клиента выполняется через helm upgrade.
Для экономии ресурсов мы отключаем «недавно» неактивных клиентов с помощью установки нулевого значения числа подов в деплойменте Apache Superset = 0. При этом в случае необходимости мы можем легко вернуть клиенту BI-конструктор, указав в деплойменте Apache Superset = 1 после анализа логов балансеров с помощью fluent--bit.
Давно неактивные клиенты удаляются из Kubernetes (helm uninstall), но могут быть восстановлены в K8s из MySQL.
Для масштабирования мощностей с учетом фактической нагрузки мы используем штатный аддон, автоскейлер VK Cloud.
Вместо выводов
Создание BI-системы — большой шаг в развитии такого продукта, как «Битрикс24». Но наш опыт показал, что «съесть можно даже слона, если делать это последовательно, небольшими частями и с пониманием своих возможностей».
Именно поэтому мы пошли по пути декомпозиции задачи, частичного делегирования задач вендору VK Cloud и выстраивания реализации из готовых компонентов. Благодаря этому при довольно скромных затратах сил и времени мы получили полнофункциональный эффективный продукт, который успешно помогает вести бизнес десяткам тысяч клиентов «Битрикс24».