Luxms BI это аналитическая платформа от российских разработчиков. Вот их официальный сайт https://luxmsbi.com Представляет собой набор инструментов для построения отчетов и графиков с оглядкой на большие объемы данных. Из коробки уже имеет некоторое количество инструментов для построения отчетов в графическом интерфейсе, однако дает возможность подключить и «внешние» приложения, развернутые где угодно. Об этом типе приложений и пойдет речь далее, однако они будут встроены в саму систему, для возможности вклиниться в авторизацию на платформе. Данная конфигурация снимет практически все возможные ограничения и позволит реализовать любую идею.
Разработка приложения
Собственно, первым делом нужно заиметь приложение, которые мы будем встраивать в платформу Luxms.
Не буду описывать весь процесс разработки, однако уделю внимание важным деталям.
Наше кастомное приложение может быть реализовано на любых технологиях, однако мое сделано на python, фреймворк Django и далее примеры будут как раз для этого стека. Если нет представления что из себя представляет Django приложение, то можно ознакомиться с любой get strarted статьей для этого фреймворка
Итак, допустим у нас уже есть приложение, в dev версии оно запускается примерно следующей командой.
1 |
python manage.py runserver |
После того, как нас появилось необходимое нам приложение, займемся его упаковкой в docker. Нам нужно добиться, что бы на localhost, на каком-то определенном порте, на сервере, было запущено наше приложение. Для Django, в продуктивной конфигурации популярным решением является сервер Gunicorn. Весьма удобный способ запустить приложение, устанавливается как python библиотека, не требует тонких настроек.
В корне проекта нужно создать Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 |
FROM python:3 ENV PYTHONUNBUFFERED 1 RUN mkdir code WORKDIR code ADD . /code/ RUN pip install -r requirements.txt CMD gunicorn ServiceInterface.wsgi:application -b 0.0.0.0:8000 --timeout 300 |
Сразу стоит отметить, что это не оптимальный вариант, но рабочий. Можно использовать более легкий образ и воспользоваться трюками для оптимизации docker файла.
Сам же сценарий построение образа содержит следующие. Создается папка code, в нее копируются все файлы проекта (не ошибитесь папкой, что бы случайно не скопировать библиотеки из виртуального окружения)
Далее устанавливаются все библиотеки. Если у Вас отсутствует файл requirements.txt, то необходимо его предварительно создать командой
1 |
pip freeze > requirements.txt |
После этого запускается WSGI сервер. Обратите внимание, должен запуститься файл wsgi, этот файл создается автоматически при создании проекта, и в нем через двоеточие должно произойти обращение к переменной application
Далее нужно сделать контейнер для NGINX. NGINX будет перенаправлять запросы в наше приложение, а так же давать доступ к статичным файлам.
Создадим папку в корне проекта и назовем ее nginx, создадим в ней 2 файла. nginx.conf и Dockerfile
Содержимое nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
upstream interface { server web:8000; } server { listen 80; location / { proxy_pass http://interface; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Host $host; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Top-Header $request_uri/; add_header X-Top-Header-test $request_uri/; proxy_http_version 1.1; } location /kasudr/static/ { autoindex on; alias /code/static/; } } |
Если кратко, то NGINX «смотрит» на порт 8000 контейнера с Django приложением и транслирует его на порт 80, в своем контейнере. Тут же прописан путь до статик файлов.
Далее Dockerfile из этой папки
1 2 3 4 |
FROM nginx:1.21-alpine RUN rm /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d |
Тут ничего особенного не происходит, удаляются стандартные настройки и копируется наши, из файла nginx.conf.
Создаем docker-compose.yml файл в корне проекта
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
version: '3.3' services: web: build: . volumes: - static_volume:/code/static/ expose: - 8000 nginx: build: ./nginx volumes: - static_volume:/code/static/ ports: - 9238:80 depends_on: - web volumes: static_volume: |
Тут у нас запускается 2 контейнера и устанавливается связь между ними. Тут же пробрасывается порт с 80 контейнера NGINX на 9238 внешний. (порт внешний может быть любой свободный)
Запускается приложение под docker следующей командой
1 |
docker-compose up -d |
После этого, в браузере по URL http://127.0.0.1:9238/ должно быть доступно приложение.
В итоге, структура моего проекта выглядит следующим образом

А так же мой requirements.txt, обратите внимание, по мимо библиотек Вашего проекта, в нем обязан быть gunicorn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
asgiref==3.7.2 bcrypt==4.0.1 beautifulsoup4==4.12.2 cffi==1.15.1 cryptography==41.0.1 Django==4.2.2 gunicorn==20.1.0 lxml==4.9.2 paramiko==3.2.0 psycopg2==2.9.6 pycparser==2.21 PyNaCl==1.5.0 soupsieve==2.4.1 sqlparse==0.4.4 sshtunnel==0.4.0 suds-py3==1.4.5.0 typing-extensions==4.6.3 tzdata==2023.3 |
Развертывание приложения на сервере c Luxms bi
Переносим образы на сервер, либо билдим проект сразу там. В любом случаи должна быть подобная картина, при команде docker images

Это означает что образы готовы, можем запускать контейнеры.
Переносим на сервер файл docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
version: '3.3' services: web: image: serviceinterface-web volumes: - static_volume:/code/static/ expose: - 8000 nginx: image: serviceinterface-nginx volumes: - static_volume:/code/static/ ports: - 9238:80 depends_on: - web volumes: static_volume: |
Проходим в папку с этим файлом и запускаем наше приложение
1 |
docker-compose up -d |
После того, как docker отрапортует что все запущено, можно проверить рабочие контейнеры.
1 |
docker ps |

На этом этапе, наше приложение работает на localhost порт 9238. Следующим этапом нужно встроить его NGINX Luxms BI.
За NGINX в Luxms отвечет отдельная служба, конфигурационные файлы находятся по следующему адресу
/opt/luxmsbi/conf/nginx/conf.d
Если сильно не углубляться, то в файле nginx.conf написано, что настройки подгружаются из директории conf.d/*.conf
В папке conf.d в свою очередь наличествует следующий файл
entrypoint.conf
В нем есть такая строка include conf.d/luxms*.location; Что в свою очередь говорит о том, что любой файл в этой директории с префиксом luxms и расширением location будет подхвачен службой NGINX.
В таком случаи создадим такой файл в conf.d
touch luxms-kasudr.location
И заполним следующим содержанием
1 2 3 4 5 6 7 8 9 10 11 12 13 |
location /kasudr { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Top-Header $request_uri/; add_header X-Top-Header-test $request_uri/; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers "luxmsbi-context-id,tracestate,traceparent,luxmsbi-trace-id"; proxy_http_version 1.1; proxy_pass http://127.0.0.1:9238; } |
Итак, тут мы по обратному прокси выводим наше приложение, которое запущено через docker, на адрес /kasudr
Сохраняем файл и перезапускаем службу следующей командой
1 |
systemctl restart luxmsbi-web.service |
После этого, по адресу http://<IP Вашего сервера>/kasudr/ должно стать доступно приложение.

Встраивание приложения в платформу
Для того что бы встроить внешнее приложение в платформу luxms bi нужно пройти по ссылке http://<IP сервера>/admin.html Нажать на «Датасеты». Создать датасет либо выбрать интересующий. Затем нажать на него и еще раз нажать «дэшборд».
После этого можно видеть примерно следующие (в разных версиях Luxms интерфейс может существенно отличаться)

Нажимаем на плитку с плюсом, вводим название, нажимаем enter.

Затем заходим в основной интерфейс

Нажимаем «Наборы данных» затем на датасет с которым идет работа, а затем на созданный ранее «дэшборд»
После этого откроется рабочее поле отчета. Необходимо войти в режим редактирования


Выбрать иконку «Внешний» и перетащить (drag and drop) ее на рабочее пространство слева. Иконку расширить до нужных размеров.
Затем нажать на внешний отчет в рабочей области, откроется его конфигурация. В конфиге необходимо прописать URL к опубликованному на сервере приложению.
1 2 3 4 5 6 7 8 9 10 11 |
{ url: '/kasudr', frame: { h: 15, w: 12, x: 0, y: 0, }, view_class: 'external', title: '', } |
Затем нажать «applay» и «Сохранить»

Результат

Реализация авторизации
Одной из ключевых причин зачем было запускать приложение на том же сервер, что и платформа Luxms это возможность «встроиться» в авторизацию.
Если речь идет о приложении или отчете из коробки, то там реализован примерно следующий алгоритм.
- при авторизации на платформе, в cookie добавляется session id;
- запуск приложения в «деше» производится во фрейме, если у приложения и во фрейме и платформе один домен, это означает что у них общие cookie, следовательно далее производится проверка наличия session id в базе данных;
- если id найдено, то приложение является авторизованным, если нет, то не авторизованным.
На уровне приложения было сделано следующие. Берутся cookie, название LuxmsBI-User-Session. В них хранится ключ авторизации от платформы. Эти куки доступны нам т.к. мы захосчены на том же домене что и платформа. Далее мы делаем запрос в БД. Нужные нам данные хранятся в схеме adm, таблице user_sessions, и запрос будет выглядеть следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
params = { 'database': getattr(settings, 'DATABASE'), 'user': getattr(settings, 'DB_USER'), 'password': getattr(settings, 'DB_PASSWORD'), 'host': getattr(settings, 'DB_HOST'), 'port': getattr(settings, 'DB_PORT') } conn = psycopg2.connect(**params) curs = conn.cursor() curs.execute('select * from adm.user_sessions where cookie = %s', [session_id, ]) records = curs.fetchall() curs.close() |
Где session_id, это значение из cookie. В итоге можно понять, является ли пользователь авторизованным или нет, кто именно является пользователем приложения и т.д. По сути БД Luxms содержит все административные данные, которые нам могут понадобиться.
Коннект к базе данных из приложения
Стоит пару слов сказать о коннекте к БД. При развертывании платформы Luxms BI, может быть как минимум 2 варианта.
- База данных находится на отдельном сервере
- База данных находится на том же сервере, что и сама платформа Luxms
В первом варианте, дополнительно делать ничего не нужно. Мы просто из приложения подсоединяемся так же, как это делается в Luxms, уже все должно быть настроено.
Во втором варианте, возможно, понадобятся следующие действия
- Проходим в директорию /pgdata/data/
- Открываем на изменение файл pg_hba.conf
- Добавляем строчку
host all all 0.0.0.0/0 md5

После этого перезапустить службу с БД
Как служба будет перезапущена, из контейнера Docker можно воспользоваться ДНС адресом для коннекта к локальной базе данных.
Выглядит он следующим образом — 172.17.0.1
Итог
Итак, было реализовано приложение, упаковано в Docker, затем оно было захосчено на сервере, после этого была произведена интеграция этого приложения в платформу Luxms BI. Данный подход к разработки мне видится очень перспективным. Мы одновременно находимся в аналитической платформе, отвечающей запросам импортозамещения, и при этом мы ни чем не ограничены в разработке. Таким образом может быть реализована любая, самая смелая задача, подключены любые библиотеки.