Перейти к содержанию

Развёртывание

Страница для тех, кто ставит и обновляет платформу на сервере. Боевой стек описан в файле docker-compose.yml в корне репозитория и рассчитан на Coolify: четыре сервиса платформы, а PostgreSQL и Redis вы создаёте в Coolify сами и передаёте адреса переменными окружения.

Из чего состоит стек

Сервис Что делает Порт Снаружи Данные
tgt-asr распознавание речи (модель T-one), хранит только модели 8000 нет том asr-models (/models)
tgt-migrate один раз за развёртывание приводит схему базы к текущей версии и завершается нет нет нет
tgt-api API, фоновые очереди и расписание обслуживания в одном процессе 3001 да том audio-data (/data)
tgt-dashboard веб-интерфейс, перенаправляет запросы браузера в API 3000 да нет
PostgreSQL записи, расшифровки, люди, настройки, копия CRM 5432 нет ресурс Coolify
Redis очереди фоновых заданий (это не кэш) 6379 нет ресурс Coolify

Файл не публикует ни одного порта: два публичных сервиса отдаёт прокси Coolify. Распознаватель, PostgreSQL и Redis остаются закрытыми. Порядок запуска задан зависимостями: tgt-api стартует после успешной миграции и готового распознавателя, tgt-dashboard после готового API.

Контейнеры tgt-asr, tgt-api и tgt-dashboard перезапускаются сами (unless-stopped). Журнал каждого из них ограничен тремя файлами по 10 МБ.

Порядок первого запуска

  1. Домены. Создайте две A-записи DNS на адрес сервера: для панели и для API.
  2. Базы. В том же проекте и окружении Coolify добавьте ресурсы PostgreSQL и Redis. Скопируйте их внутренние адреса подключения. Для Redis оставьте политику noeviction и постоянное хранение (appendonly): очередь не должна терять задания. Включите расписание резервных копий PostgreSQL.
  3. Приложение. New Resource → репозиторий, ветка main, сборка Docker Compose, файл /docker-compose.yml. В настройках приложения (Advanced) включите Connect To Predefined Network, чтобы контейнеры видели базы по внутренним именам.
  4. Домены в Coolify. Сервису tgt-dashboard задайте https://<адрес-платформы>:3000, сервису tgt-api задайте https://<адрес-API>:3001.
  5. Переменные. Задайте обязательные переменные из таблицы ниже. Секреты (ASR_API_KEY, SESSION_SECRET) сгенерируйте случайными строками (openssl rand -hex 24 и openssl rand -hex 32).
  6. Развёртывание. Образы собираются несколько минут. При первом запуске распознаватель скачивает модели с Hugging Face в том asr-models, и развёртывание ждёт его: серверу нужен исходящий доступ в интернет и около 7 ГБ свободного места под модели (меньше 1 ГБ с декодером greedy). Держите не менее 15 ГБ свободного места на диске: образы и сборка занимают ещё несколько гигабайт.
  7. Проверка. Откройте https://<адрес-API>/readyz: в ответе "status":"ready". Затем откройте панель и войдите под первым владельцем.

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

Переменные окружения

Задавайте их в Coolify. Пустое значение равно отсутствующему. Контейнеры получают только те переменные, которые перечислены в разделе environment файла docker-compose.yml; значения по умолчанию в таблицах ниже взяты из этого файла. В файле зафиксированы и из окружения не меняются: NODE_ENV=production, порт API 3001, адрес распознавателя http://tgt-asr:8000, каталог звука /data/audio, адрес API для панели http://tgt-api:3001 и один процесс распознавателя.

Обязательные

Без них compose отказывается запускаться.

Переменная Что задаёт
DATABASE_URL Подключение к PostgreSQL (внутренний адрес ресурса Coolify). Нужно и миграции, и API
REDIS_URL Подключение к Redis; rediss:// включает TLS
ASR_API_KEY Секрет, общий для API и распознавателя
SESSION_SECRET Подпись cookie сессии, не короче 32 символов. Смена выводит всех из системы

Первый пользователь

Переменная Что задаёт По умолчанию
BOOTSTRAP_ADMIN_EMAIL, BOOTSTRAP_ADMIN_PASSWORD Создают первого владельца, только если в базе нет ни одного пользователя. Пароль не короче 8 символов не заданы

Пока пользователей нет, без этих переменных войти нельзя. После первого входа удалите пароль из окружения.

Распознавание и очередь

Переменная Что задаёт По умолчанию
DECODER Декодер распознавателя: beam_search или greedy. Читается при старте: смена требует перезапуска, не пересборки beam_search
ASR_MAX_CONCURRENCY Сколько файлов распознаватель обрабатывает одновременно, остальные ждут 2
ASR_MEMORY_LIMIT Лимит памяти контейнера распознавателя 3g
TRANSCRIPTION_CONCURRENCY Сколько записей API передаёт распознавателю одновременно (от 1 до 8); не больше ASR_MAX_CONCURRENCY 2
MAX_UPLOAD_MB Наибольший размер загружаемого или скачиваемого файла 512
API_RUN_MODE all: HTTP и очереди в одном процессе; api: только HTTP; worker: только очереди all

Доступ и сессии

Переменная Что задаёт По умолчанию
COOKIE_SECURE Cookie сессии только по HTTPS true
TRUSTED_PROXY_HOPS Сколько обратных прокси стоит перед API (нужно, чтобы видеть настоящий адрес клиента) 1
LOGIN_MAX_FAILURES, LOGIN_FAILURE_WINDOW_SECONDS Сколько неудачных входов с одного адреса за какое окно (секунды) до ответа 429 10, 300
CALLBACK_SIGNING_SECRET Секрет подписи колбэков, не короче 16 символов. Без него колбэки идут без подписи не задан

Хранилище и источники записей

Переменная Что задаёт По умолчанию
STORAGE_KIND Где хранится звук: local (том audio-data) или s3 local
INGESTION_SOURCE Источник старого забора записей: none, local-inbox или s3 none
INGESTION_LOCAL_PREFIX Папка внутри хранилища для local-inbox inbox
S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY Бакет и ключ доступа; обязательны при STORAGE_KIND=s3 не заданы
S3_ENDPOINT_URL, S3_REGION Адрес и регион провайдера (нужны, если это не AWS) пусто, us-east-1
S3_UPLOAD_PREFIX, S3_INGEST_PREFIX Куда складываются загрузки и откуда забираются чужие файлы; не должны пересекаться uploads/, пусто
S3_FORCE_PATH_STYLE Адресация «путь в адресе»; для AWS false true

Подробнее: Хранилище и модели.

Битрикс24 и адрес платформы

Переменная Что задаёт По умолчанию
BITRIX24_WEBHOOK_URL Входящий вебхук для чтения (секрет) не задан
BITRIX24_EVENT_TOKEN Токен приложения исходящего вебхука «звонок завершён» не задан
BITRIX24_WRITE_WEBHOOK_URL Отдельный вебхук для записи комментариев в сделки (секрет) не задан
PUBLIC_BASE_URL Публичный адрес панели для ссылки на запись в комментарии не задан

Подробнее: Подключение Битрикс24.

Проверка звонков

Переменная Что задаёт По умолчанию
ANALYSIS_PROVIDER Чем проверяются звонки: rules (словари на сервере, ничего не отправляется), openai-compatible (внешняя языковая модель), mock (выдуманные оценки для демонстраций), none (проверки нет) rules
ANALYSIS_BASE_URL, ANALYSIS_MODEL, ANALYSIS_API_KEY Адрес API модели, имя модели и ключ; адрес и имя обязательны при openai-compatible не заданы
ANALYSIS_ALLOW_EXTERNAL_EGRESS Разрешение отправлять тексты звонков за пределы сервера; при openai-compatible без true сервер не запускается false
ANALYSIS_RESPONSE_FORMAT Как просить ответ у модели: json_object или json_schema json_object
ANALYSIS_EXTRA_BODY Дополнительные параметры каждого запроса, JSON-объект отключает «рассуждения» модели
ANALYSIS_CONCURRENCY, ANALYSIS_REQUESTS_PER_MINUTE, ANALYSIS_REQUEST_TIMEOUT_SECONDS Параллельные вопросы модели (1-8), запросов в минуту (1-600), время ожидания ответа в секундах (5-600) 2, 60, 180
ANALYSIS_PRICE_INPUT_PER_MILLION, ANALYSIS_PRICE_OUTPUT_PER_MILLION, ANALYSIS_PRICE_CURRENCY Цена миллиона токенов для расчёта стоимости в редакторе правил; цены задаются парой не заданы, BYN
ANALYSIS_SEED_RULES Загружать ли при старте правила, поставляемые с системой, для наборов без опубликованной версии true

Подробнее о том, что уходит модели: Персональные данные и хранение.

Прочее

Переменная Что задаёт По умолчанию
APP_TIMEZONE Часовой пояс дней в статистике, каталоге и выгрузках Europe/Moscow
CORRELATION_BOOTSTRAP_REPLICATES Сколько повторных выборок при расчёте интервала на странице «Связь с продажами» (20-5000) 1000

Что compose не передаёт в контейнеры

Эти переменные есть в коде, но в docker-compose.yml их нет, поэтому в Coolify они не действуют: работают значения по умолчанию. Чтобы изменить, добавьте переменную в environment сервиса в файле.

Переменная Что задаёт По умолчанию
SESSION_TTL_HOURS Срок жизни сессии 168 (7 суток)
TRANSCRIPTION_ATTEMPTS, TRANSCRIPTION_RETRY_DELAY_SECONDS Попытки расшифровки одной записи и первая пауза между ними (удваивается) 5, 30
BITRIX24_FETCH_RETRY_DELAY_SECONDS Первая пауза между попытками загрузить звонок или шаг чтения CRM (удваивается) 30
BITRIX24_EXPORT_PAUSE_SECONDS, CRM_SYNC_PAUSE_SECONDS Паузы между страницами выгрузки звонков и чтения CRM 2, 1
CALLBACK_RETRY_DELAY_SECONDS Первая пауза между доставками колбэка (удваивается) 30
ALLOW_PRIVATE_NETWORK_DOWNLOADS Разрешить скачивание и колбэки на адреса частной сети (и обращение к модели в частной сети) false
QUEUE_PREFIX Префикс ключей очередей в Redis; нужен, если два окружения делят один Redis tgt
DATABASE_WAIT_SECONDS Сколько tgt-migrate ждёт ответа базы 60

Домены и проверки состояния

Сервис Адрес проверки Что отвечает
tgt-dashboard GET /login 200, когда страница входа отдаётся
tgt-api GET /healthz {"status":"ok"}: процесс жив
tgt-api GET /readyz всегда HTTP 200; смотрите поля status (ready или degraded), database, redis, asr (up или down)
tgt-asr GET /healthz, GET /readyz ok; ready вместе с моделью, версией, декодером и точностью вычислений

Все проверки открыты без ключа. Docker использует их как проверки состояния контейнеров: встроенные в образы, поэтому отдельно в Coolify включать их не нужно. У распознавателя стартовый период 30 минут: пока модель скачивается, порт сервиса закрыт и контейнер ещё не считается неисправным.

Примечание

/readyz API отвечает 200 даже при неисправной зависимости. Если следите за сервисом внешним монитором, проверяйте поле status, а не код ответа.

Если Coolify считает развёртывание неудачным из-за tgt-migrate (он завершается с кодом 0 и больше не работает), исключите этот сервис из проверок: в редакторе compose-файла добавьте ему exclude_from_hc: true.

Что делает повторное развёртывание

  1. Coolify собирает образы. Неудачная сборка не трогает работающую версию.
  2. tgt-migrate ждёт ответа базы (до DATABASE_WAIT_SECONDS) и применяет миграции. Если он завершился с кодом 1, tgt-api не стартует.
  3. tgt-api получает 60 секунд, чтобы завершить текущие задания. Расшифровка, которая шла в этот момент, начинается заново; длинные записи занимают десятки минут, поэтому обновляйте при короткой очереди.
  4. Тома asr-models и audio-data и содержимое PostgreSQL сохраняются. Модели повторно не скачиваются.

Если tgt-migrate завершился с кодом 1, откройте его журнал: в первых строках указаны хост, порт, база и пользователь (без пароля) и причина отказа на каждой попытке. Частые причины:

В журнале tgt-migrate Что значит Что делать
does not resolve, ENOTFOUND имя базы не находится включите Connect To Predefined Network и возьмите хост из внутреннего адреса ресурса
ECONNREFUSED хост отвечает, но порт никто не слушает проверьте порт и что база запущена
ETIMEDOUT адрес не отвечает проверьте общую сеть приложения и базы и сетевые ограничения
28P01 или 28000 неверный пользователь или пароль исправьте DATABASE_URL
3D000 базы с таким именем нет создайте её или используйте ту, с которой стартует сервер (в новом PostgreSQL Coolify это postgres)

Миграции этого проекта не удаляют и не переименовывают таблицы и столбцы, поэтому откат на предыдущую версию, как правило, возможен без отката схемы. Откат: в Coolify откройте список развёртываний, выберите прежнее и нажмите Redeploy. Часть релизов сбрасывает позиции чтения CRM: тогда следующее чтение перечитывает данные заново и занимает больше времени.

Если между API и PostgreSQL стоит PgBouncer, ему нужно разрешить параметр запуска options (ignore_startup_parameters): API отключает параллельные рабочие процессы запросов, чтобы не зависеть от размера /dev/shm.

Диск не должен заполниться

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

В Coolify откройте Servers → сервер → Docker Cleanup и задайте:

  • частоту 0 * * * * (раз в час);
  • Force Docker Cleanup выключен, порог 60: очистка срабатывает, когда диск заполнен больше чем на 60%;
  • Delete Unused Volumes выключен: иначе удалятся тома остановленных контейнеров, то есть записи и модели.

Внимание

Ошибка no space left on device при сборке или скачивании моделей означает полный диск. Распознаватель сам проверяет свободное место перед скачиванием и пишет в журнал, сколько нужно.

Что владелец делает в панели после первого запуска

  1. Войдите под первым владельцем и удалите BOOTSTRAP_ADMIN_PASSWORD из окружения.
  2. «Настройки → Люди»: нажмите «Добавить человека» и заведите руководителей и менеджеров (пароль не короче 8 символов).
  3. «Настройки → Люди → Отделы»: проверьте номера в «Воронки сделок»: сделки читаются только из этих воронок.
  4. «Настройки → Система → API-ключи»: создайте ключи для интеграций.
  5. «Настройки → Автоматика»: убедитесь, что шаг «Битрикс24 подключён» выполнен, включите «Автоматический режим» и подтвердите. Сразу начнётся чтение данных CRM.
  6. Дождитесь шага «Данные CRM прочитаны», затем нажмите «Определить менеджеров»: откроется «Люди → Отделы → Менеджеры продаж». Нажмите «Определить из Битрикса», отметьте менеджеров продаж каждого отдела и нажмите «Применить состав». Пока состава нет, звонки не загружаются.
  7. «Настройки → Автоматика → Дополнительно → Загрузить историю»: посмотрите оценку и подтвердите.
  8. «Настройки → Проверка звонков»: проверьте правила. Автоматический режим сам включает проверку, если задан способ проверки.
  9. Запись итогов в сделки включайте последней и только если заказчик этого хочет.

Всё, что действует само, после свежего развёртывания выключено: платформа сама ничего не тянет, не читает и не пишет, пока владелец не включит.

См. также