Инструкция по установке Lucidata (BI-платформа)

Установка делается одной командой: ./deploy.sh ваш-домен.ru
Доступ в интернет не нужен ни на одном шаге.

Порядок работы — сверху вниз по этому файлу. Пропускать разделы нельзя:
каждый следующий опирается на предыдущий.


=============================================================================
1. СОСТАВ ПОСТАВКИ
=============================================================================

    instalation_manual.txt  этот файл
    deploy.sh               скрипт установки
    docker-compose.yml      описание сервисов
    Caddyfile               шаблон конфигурации веб-сервера
    .env                    настройки — ЭТО ЕДИНСТВЕННЫЙ ФАЙЛ, КОТОРЫЙ ВЫ ПРАВИТЕ
    basicauth.conf.example  образец общего пароля на вход (по желанию)
    metadata.json           версия, дата сборки, платформа, список образов
    images/*.tar            образы контейнеров
    geo/sources.json        список источников географических данных
    keys/                   каталог для ключей (см. раздел 4); внутри только
                            README.txt — короткая записка о назначении
    ssl/                    каталог для ваших сертификатов (см. раздел 4);
                            внутри только README.txt

В каталоге архива исходного кода нет. Внутри образов лежит установленное
приложение: образ веб-интерфейса содержит собранные файлы сайта, образ
сервера приложений — установленные пакеты приложения (Python не компилируется
в машинный код, поэтому модули приложения читаемы, как в любой поставке на
Python; исходного дерева проекта, тестов и файлов сборки в образе нет).
Образ отрисовки графиков содержит только собранные файлы (dist/server.js,
dist/builders.js): карт исходного кода (*.js.map) нашего кода в нём нет —
сборка образа проверяет это и прерывается, если карта появится. Файлы
*.js.map, которые в этом образе всё же есть, принадлежат сторонним
библиотекам построения графиков (echarts, zrender) и приходят из их
официальных пакетов.


=============================================================================
2. ЧТО ДОЛЖНО БЫТЬ НА СЕРВЕРЕ
=============================================================================

1) Linux x86_64. ПРОВЕРЕНО НАМИ: Ubuntu 24.04. Ubuntu 22.04 и Astra Linux
   Special Edition 1.7 предъявляют те же требования (Docker 24.0+ и плагин
   compose 2.20+), но на них эта поставка нами не проверялась.

2) Архитектура процессора — ТА ЖЕ, под которую собран архив. Проверьте это
   первым делом, сразу после распаковки (раздел 3):

       ./deploy.sh --status
       sudo docker version --format '{{.Server.Os}}/{{.Server.Arch}}'

   Первая команда работает и без sudo (она читает только metadata.json).
   Вторая обращается к демону docker: без sudo она сработает, только если ваш
   пользователь состоит в группе docker.
   Первая команда печатает строку «платформа», вторая — платформу сервера.
   Значения должны совпасть (обычно linux/amd64). Если они разные, архив на
   этом сервере работать не будет — запросите у поставщика архив под вашу
   платформу. Скрипт установки сверяет это сам и откажется продолжать.

3) Docker Engine 24.0 или новее и плагин docker compose 2.20 или новее:

       docker --version
       docker compose version

   Старый docker-compose 1.x не подойдёт.

4) Свободное место. Скрипт установки проверяет только то, что нужно ему
   самому — место под образы (около 4 ГБ) — и откажется запускаться, если
   его нет. Этого достаточно, чтобы установка поднялась, но НЕ достаточно
   для работы: под данные, журналы и обновления закладывайте не меньше
   40 ГБ на том, где лежат тома docker (обычно /var/lib/docker).

5) Оперативная память: не меньше 8 ГБ, рекомендуется 16 ГБ. Установка
   поднимает 7 постоянно работающих контейнеров и ещё два одноразовых,
   которые отрабатывают и завершаются. Это ОЦЕНКА: измеренных требований
   у нас пока нет.

6) Свободные порты 80 и 443. Если они заняты, задайте другие в .env
   (EDGE_HTTP_PORT, EDGE_HTTPS_PORT) — ДО первого запуска, см. раздел 5.
   Скрипт проверяет занятость портов и не станет ставить систему рядом с
   чужим сервисом.

7) DNS-запись вашего домена, указывающая на этот сервер.

Сервер Lucidata никуда не «звонит домой»: телеметрии нет, внешние реестры не
опрашиваются. Наружу система ходит только в те интеграции, которые вы
настроите сами (почта, мессенджеры, внешняя языковая модель).


=============================================================================
3. РАСПАКОВКА
=============================================================================

ВСЯ УСТАНОВКА ВЫПОЛНЯЕТСЯ ОТ ROOT (через sudo). Это не перестраховка: каталог
установки лежит в /opt, а скрипт меняет владельца вашего TLS-ключа на
пользователя веб-сервера — обычному пользователю это не разрешено. Скрипт
установки проверяет права и откажется работать без них, назвав нужную команду.

Распакуйте архив туда, где он будет жить постоянно: каталог остаётся рабочим
после установки, из него же выполняются обслуживание и обновление.

    sudo tar -xzf lucidata-deploy-<версия>.tar.gz -C /opt
    cd /opt/lucidata-deploy-<версия>

Файлы в архиве принадлежат root — так и должно быть: в .env лежат все пароли
установки, и читать его посторонним на сервере не следует.

Дальше все команды выполняются из этого каталога (с sudo там, где указано).


=============================================================================
4. СЕРТИФИКАТЫ И КЛЮЧИ
=============================================================================

Положите сертификаты вашего домена в каталог ssl/ ИМЕННО с такими именами:

    ssl/ssl.pem   цепочка сертификатов
    ssl/ssl.key   приватная часть

Если сертификата пока нет, можно выпустить самоподписанный (браузеры будут
показывать предупреждение — для проверки контура этого достаточно):

    sudo openssl req -x509 -newkey rsa:2048 -nodes -days 825 \
        -keyout ssl/ssl.key -out ssl/ssl.pem \
        -subj "/CN=ваш-домен.ru" \
        -addext "subjectAltName=DNS:ваш-домен.ru"

(sudo здесь обязателен: каталог установки распакован от root, и без него
openssl не сможет создать файлы в ssl/.)

Есть ещё два запасных пути, оба задаются переменной LUCIDATA_TLS_MODE в .env:

    LUCIDATA_TLS_MODE=acme      автоматический выпуск Let's Encrypt. Нужны
                                доступ сервера в интернет и входящие порты
                                80/443 снаружи. В закрытом контуре не работает.
    LUCIDATA_TLS_MODE=internal  сертификат локального центра сертификации
                                веб-сервера. Файлы в ssl/ не нужны.

В каталогах ssl/ и keys/ лежит по файлу README.txt — это записка о назначении
каталога, она ни на что не влияет, удалять её не нужно.

Каталог keys/ оставьте пустым. Он нужен, только если вы будете включать
доступ к данным по протоколу Postgres (psql, DBeaver, Tableau) — тогда туда
кладётся пара PEM для TLS этого порта. Внутри контейнеров каталог виден как
/etc/lucidata/keys, только на чтение.

Если вы копировали файлы из-под root, отдайте их пользователю веб-сервера —
он работает под непривилегированным идентификатором и иначе не прочитает
собственный ключ:

    sudo chown 10001:10001 ssl/ssl.pem ssl/ssl.key
    sudo chmod 640 ssl/ssl.key

Скрипт установки делает это сам, если запущен от root, и предупреждает, если
не смог.


=============================================================================
5. ФАЙЛ .env
=============================================================================

Откройте .env от root — файл принадлежит root и закрыт от остальных (права
600), обычным пользователем он не откроется:

    sudo nano .env        (или sudoedit .env)

Замените в нём ВСЕ значения, начинающиеся на CHANGEME_. Их восемь строк —
семь разных значений, пароль встроенного хранилища записан дважды
(один раз для самого хранилища, второй — для приложения; значения должны
совпадать). Рядом с каждой строкой написано, зачем она.

    LUCIDATA_SESSION_SECRET          подпись сессий
    LUCIDATA_SHEETS_SECRET           токены коннектора Excel/Sheets
    LUCIDATA_EMBED_SECRET            токены встраивания дашбордов
    LUCIDATA_ENCRYPTION_KEY          шифрование паролей к вашим базам на диске
    STORE_POSTGRES_PASSWORD          пароль встроенной базы метаданных
    LUCIDATA_BOOTSTRAP_ADMIN_EMAIL   адрес первого администратора
    MINIO_ROOT_PASSWORD              пароль встроенного объектного хранилища
    LUCIDATA_S3_SECRET_KEY           то же самое значение ещё раз

Пять из них — случайные строки (три секрета и два пароля), каждая
генерируется одной командой:

    python3 -c "import secrets; print(secrets.token_urlsafe(48))"

Ключ шифрования LUCIDATA_ENCRYPTION_KEY генерируется иначе:

    openssl rand -base64 32 | tr '+/' '-_'

Про него отдельно: если оставить его пустым, ключ будет выведен из
LUCIDATA_SESSION_SECRET, и тогда любая будущая смена сессионного секрета молча
сделает сохранённые пароли подключений к вашим базам нерасшифровываемыми.
Задайте его явно и сохраните отдельно от сервера.

ЗАДАЙТЕ ЗДЕСЬ ЖЕ ПАРОЛЬ ПЕРВОГО АДМИНИСТРАТОРА:

    LUCIDATA_BOOTSTRAP_ADMIN_PASSWORD=<ваш пароль, не короче 10 символов>

Это не обязательно, но настоятельно рекомендуется. Если оставить пусто,
система выдаст одноразовый пароль ровно один раз, и вы будете обязаны
прочитать его сразу после установки (см. раздел 7).

ЗАПОЛНИТЕ СРАЗУ И ВСЁ ОСТАЛЬНОЕ, ЧТО СОБИРАЕТЕСЬ МЕНЯТЬ: порты края, почту
(блок LUCIDATA_SMTP_*), свой Postgres или своё S3-хранилище. Причина в
разделе 7: правка .env после установки пересоздаёт контейнеры приложения и
стирает их журналы вместе с одноразовым паролем администратора.

Домен в .env указывать НЕ нужно — он передаётся скрипту аргументом.

Свой Postgres и/или своё S3-совместимое хранилище указываются в разделе 4
файла .env (LUCIDATA_STORE_URL и блок LUCIDATA_S3_*); встроенное хранилище
при этом отключается строкой LUCIDATA_BUNDLED_MINIO=0. По умолчанию установка
самодостаточна и поднимает своё.

Если вы переустанавливаете существующую систему — сделайте резервную копию,
см. раздел 9.


=============================================================================
6. ЗАПУСК
=============================================================================

    sudo ./deploy.sh ваш-домен.ru

Именно от root — см. раздел 3. Запущенный без sudo скрипт сразу скажет об
этом и ничего не сделает.

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

Установка занимает несколько минут: первый запуск создаёт схему базы данных.

Если что-то не так, скрипт останавливается и называет файл и строку, которые
надо исправить. Пока он не написал «Установка завершена», установка не
принята.

Другие режимы того же скрипта:

    ./deploy.sh --dry-run ваш-домен.ru    показать план, ничего не меняя
    sudo ./deploy.sh --status             что развёрнуто сейчас (до установки
                                          работает и без sudo)
    sudo ./deploy.sh --skip-load домен    перезапуск без повторной загрузки
                                          образов
    ./deploy.sh --help                    подробности по каждому шагу

Повторный запуск ./deploy.sh безопасен: он не трогает данные, а приводит
установку к состоянию, описанному в .env.


=============================================================================
7. ПЕРВЫЙ ВХОД
=============================================================================

СДЕЛАЙТЕ ЭТО СРАЗУ, ДО ЛЮБЫХ ДРУГИХ ДЕЙСТВИЙ.

Если вы задали LUCIDATA_BOOTSTRAP_ADMIN_PASSWORD в .env — входите этим
паролем, раздел закончен.

Если нет, скрипт напечатал одноразовый пароль в конце установки и сохранил
его в файл generated/first-admin.txt (владелец root, права 600). Посмотреть:

    sudo cat generated/first-admin.txt

Пароль выдаётся ОДИН раз за установку. Система печатает его в журнал
единственный раз при создании учётной записи; журнал исчезает при первом же
пересоздании контейнеров, а оно происходит при любой правке .env. Повторный
запуск нового пароля не выдаёт и старый не сбрасывает. Поэтому:

    1) откройте https://ваш-домен.ru/
    2) войдите под адресом из LUCIDATA_BOOTSTRAP_ADMIN_EMAIL и этим паролем
    3) система потребует сменить пароль — смените
    4) удалите файл:  sudo rm generated/first-admin.txt

До смены пароля остальные разделы интерфейса закрыты. Это нормально, а не
ошибка.

Дальше в интерфейсе: подключите источники данных, настройте вход через вашу
систему единого входа, пригласите пользователей и раздайте роли. Внешний
поставщик единого входа настраивается мастером в интерфейсе — заранее в .env
ничего указывать не нужно.


=============================================================================
8. ЧТО РАБОТАЕТ НЕ ПОЛНОСТЬЮ БЕЗ ИНТЕРНЕТА
=============================================================================

Это не дефекты установки, а следствие отсутствия внешнего доступа.

- Подложка географических карт (уличная карта мира) в архив НЕ входит: она
  весит от 2,4 ГБ до 128 ГБ и переносится отдельно. Без неё карты рисуются по
  границам стран и регионов (они есть в образе) и по вашим собственным
  геоданным, но улиц и городской детализации не будет.

- Шрифты интерфейса подгружаются из общедоступного сервиса шрифтов. Без
  интернета браузер их не получит и подставит системные: интерфейс останется
  полностью рабочим, изменится начертание.

- Функции с искусственным интеллектом требуют поставщика модели. В закрытом
  контуре это ваша локальная модель, её адрес указывается в .env
  (LUCIDATA_LLM_BASE_URL). Пока поставщик не настроен, мастера честно
  отвечают «провайдер не настроен» и ничего не выдумывают.


=============================================================================
9. РЕЗЕРВНОЕ КОПИРОВАНИЕ
=============================================================================

Обязательный минимум для восстановления.

Все команды этого раздела выполняются от root: и демон docker, и файл
generated/app.env (владелец root, права 600) обычному пользователю недоступны.
Файл выгрузки кладите туда, где у вас есть право записи, — каталог установки
принадлежит root.

1) База метаданных — пользователи, права, дашборды, аудит:

    sudo docker compose -f docker-compose.yml --env-file generated/app.env \
        exec -T store-postgres pg_dump -U postgres lucidata_store \
        | gzip > ~/lucidata-store-$(date +%F).sql.gz

2) Содержимое объектного хранилища — материализованные выборки, загруженные
   файлы: средствами вашего S3 или, для встроенного хранилища, копией тома
   lucidata_minio_data.

3) Том lucidata_app_data — склад приложения: семантические модели вместе с их
   историей изменений, загруженные таблицы, локальный кэш выборок. По
   важности это второй объект после базы метаданных.

4) Файл .env и каталог ssl/ — храните их отдельно от сервера.

Том rt_data восстанавливать не нужно, он пересоздаётся. Том caddy_data стоит
сохранить, если вы пользуетесь автоматическим выпуском сертификатов.


=============================================================================
10. ЧАСТЫЕ ВОПРОСЫ
=============================================================================

«Скрипт говорит, что в .env остались CHANGEME»
    Замените все значения, начинающиеся на CHANGEME_. Скрипт печатает номера
    строк и сами строки.

«Архив собран для платформы linux/…, а этот сервер — linux/…»
    Архив под другую архитектуру процессора. Образы загрузятся, но контейнеры
    работать не будут. Запросите архив под вашу платформу.

«Порты края уже заняты на этом сервере»
    Посмотрите, кто их держит:  sudo ss -ltnp
    Освободите порты или задайте другие в .env (EDGE_HTTP_PORT,
    EDGE_HTTPS_PORT) и запустите установку снова.

«Край не отвечает, в журнале caddy — ошибка чтения ключа»
    Веб-сервер работает под непривилегированным пользователем и не может
    прочитать ключ, скопированный из-под root:
        sudo chown 10001:10001 ssl/ssl.pem ssl/ssl.key
        sudo chmod 640 ssl/ssl.key
    и запустите установку снова.

«Нужен временный общий пароль на вход, пока система не настроена»
    Скопируйте basicauth.conf.example в basicauth.conf (sudo cp), сгенерируйте
    хэш пароля командой из шапки этого файла (её тоже запускают через sudo),
    поставьте LUCIDATA_BASIC_AUTH=1 в .env и запустите установку снова.
    Учтите: при включённом общем пароле не работают встраивание дашбордов и
    внешние клиенты по токену.

«Как посмотреть, что вообще происходит»
    sudo ./deploy.sh --status
    sudo docker compose -f docker-compose.yml --env-file generated/app.env ps
    sudo docker compose -f docker-compose.yml --env-file generated/app.env \
        logs -f app

    Все три — от root: generated/app.env принадлежит root и закрыт (600).

«Установку нужно запускать от root»
    Так и есть: sudo ./deploy.sh ваш-домен.ru. Без прав root скрипт не сможет
    отдать ваш TLS-ключ пользователю веб-сервера, и установка поднялась бы с
    неработающим https. Причина и разбор — раздел 3.

«Как остановить и как запустить снова»
    sudo docker compose -f docker-compose.yml --env-file generated/app.env \
        stop
    sudo ./deploy.sh --skip-load ваш-домен.ru
