lct-hack/docker-compose.yml

208 lines
7.9 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Полный стенд на CPU из готовых образов: `docker compose up`, затем
# http://127.0.0.1:5173. Без .env, без сборки; после скачивания образов сеть
# не нужна. Все порты — только на 127.0.0.1.
#
# У своих сервисов рядом с image стоит build: команда собирает те же образы
# локально (`docker compose build` или `docker buildx bake`). Веса и сценарии
# входят в образы именованными контекстами, bind-mount'ов исходников нет.
services:
postgres:
image: registry.gglamer.ru/pub/postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: lct
# Пароль по умолчанию: БД не публикуется наружу, только loopback хоста.
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-lct}
POSTGRES_DB: lct
ports: ["127.0.0.1:${POSTGRES_PORT:-5432}:5432"]
volumes: ["pgdata:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lct"]
interval: 5s
timeout: 3s
retries: 10
backend:
image: registry.gglamer.ru/pub/lct-hack-backend:2026.09.29
build: &backend-build
context: ./backend
target: voice
additional_contexts:
models: ./backend/models
scenarios: ./scenarios
licenses: ./deploy/licenses
restart: unless-stopped
# Один воркер принципиально: состояние живой сессии и реестр наблюдателей
# живут в памяти процесса (docs/arch/STACK.md).
command: >-
sh -c '
if [ -z "$${SESSION_SECRET:-}" ]; then
if [ ! -s /run/lct/session-secret ]; then
python -c "import secrets; print(secrets.token_urlsafe(48))" > /run/lct/session-secret;
chmod 600 /run/lct/session-secret;
fi;
export SESSION_SECRET="$$(cat /run/lct/session-secret)";
fi;
alembic upgrade head && python scripts/seed.py &&
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 1'
environment:
DATABASE_URL: postgresql+asyncpg://lct:${POSTGRES_PASSWORD:-lct}@postgres:5432/lct
SESSION_SECRET: ${SESSION_SECRET:-}
BACKEND_NODE_ID: backend
OFFLINE: "true"
LDAP_ENABLED: ${LDAP_ENABLED:-false}
LDAP_URL: ${LDAP_URL:-}
LDAP_BASE_DN: ${LDAP_BASE_DN:-}
LDAP_BIND_DN: ${LDAP_BIND_DN:-}
LDAP_BIND_PASSWORD: ${LDAP_BIND_PASSWORD:-}
LDAP_USER_FILTER: ${LDAP_USER_FILTER:-'(objectClass=person)'}
LDAP_LOGIN_ATTRIBUTE: ${LDAP_LOGIN_ATTRIBUTE:-sAMAccountName}
LDAP_ROLE_GROUPS: "${LDAP_ROLE_GROUPS:-{}}"
LDAP_SERVICE_GROUPS: "${LDAP_SERVICE_GROUPS:-{}}"
LDAP_CA_CERTS_FILE: ${LDAP_CA_CERTS_FILE:-}
LDAP_CONNECT_TIMEOUT_SECONDS: ${LDAP_CONNECT_TIMEOUT_SECONDS:-5}
VOICE_ENABLED: "true"
# Единственный STT, проверенный вживую на CPU: ~0,3–0,4 с на 2 с звука.
STT_MODEL: gigaam-v3-rnnt
RECORD_CALLS: "true"
RECORDINGS_DIR: /recordings
LLM_PROVIDER: local
# Имена сервисов ниже; ALLOW_DOCKER_HOST_MODELS пускает к ним офлайн-проверку
# адреса (app/dialog/llm.py), внешний URL по-прежнему отвергается.
LLM_BASE_URL: http://llm-qwen:18080/v1
LLM_MODEL_CALLER: Qwen3-1.7B
LLM_CONTROL_BASE_URL: http://llm-vikhr:18081/v1
LLM_MODEL_CONTROL: Vikhr-1B
ALLOW_DOCKER_HOST_MODELS: "true"
GRAMMAR_LLM_ENABLED: "true"
BACKUP_INTERVAL_SECONDS: ${BACKUP_INTERVAL_SECONDS:-86400}
BACKUP_KEEP: ${BACKUP_KEEP:-14}
volumes:
- securitydata:/run/lct
- recordings:/recordings
- backups:/app/backups
ports: ["127.0.0.1:${BACKEND_PORT:-8000}:8000"]
depends_on:
postgres:
condition: service_healthy
llm-qwen:
condition: service_healthy
llm-vikhr:
condition: service_healthy
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=2).read()"]
interval: 5s
timeout: 3s
retries: 24
# Прогрев STT и TTS на старте занимает десятки секунд на слабом CPU.
start_period: 120s
backup:
image: registry.gglamer.ru/pub/lct-hack-backend:2026.09.29
build: *backend-build
restart: unless-stopped
command: python scripts/backup_loop.py
environment:
DATABASE_URL: postgresql+asyncpg://lct:${POSTGRES_PASSWORD:-lct}@postgres:5432/lct
BACKUP_INTERVAL_SECONDS: ${BACKUP_INTERVAL_SECONDS:-86400}
BACKUP_RETRY_SECONDS: ${BACKUP_RETRY_SECONDS:-300}
BACKUP_KEEP: ${BACKUP_KEEP:-14}
volumes:
- backups:/app/backups
depends_on:
postgres:
condition: service_healthy
frontend:
image: registry.gglamer.ru/pub/lct-hack-frontend:2026.09.29
build: ./frontend
restart: unless-stopped
environment:
BACKEND_HOST: backend
BACKEND_PORT: 8000
ports: ["127.0.0.1:${FRONTEND_PORT:-5173}:5173"]
depends_on:
backend:
condition: service_healthy
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 10s
timeout: 3s
retries: 3
# Asterisk для софтфонов и встроенного WebRTC-телефона. Браузер ходит на
# same-origin /sip-ws через nginx фронта; SIP и RTP для внешних софтфонов —
# только на loopback.
sip:
image: registry.gglamer.ru/pub/lct-hack-sip:2026.09.29
build: ./sip
restart: unless-stopped
environment:
SIP_6001_PASSWORD: ${SIP_6001_PASSWORD:-}
SIP_6002_PASSWORD: ${SIP_6002_PASSWORD:-}
SIP_6003_PASSWORD: ${SIP_6003_PASSWORD:-}
SIP_6101_PASSWORD: ${SIP_6101_PASSWORD:-}
SIP_6102_PASSWORD: ${SIP_6102_PASSWORD:-}
SIP_6103_PASSWORD: ${SIP_6103_PASSWORD:-}
SIP_EXTERNAL_MEDIA_ADDRESS: 127.0.0.1
SIP_RTP_START: "10000"
SIP_RTP_END: "10099"
ports:
- "127.0.0.1:${SIP_PORT:-5060}:5060/udp"
- "127.0.0.1:${SIP_PORT:-5060}:5060/tcp"
- "127.0.0.1:10000-10099:10000-10099/udp"
volumes:
- sipdata:/var/lib/lct-sip
- siprecordings:/recordings
# Qwen играет звонящего, пишет черновики сценариев и выводы по группе.
llm-qwen:
image: registry.gglamer.ru/pub/lct-hack-llm:2026.09.29
build: &llm-build
context: ./deploy/llm
additional_contexts:
models: ./backend/models
licenses: ./deploy/licenses
restart: unless-stopped
command:
[
"-m", "/models/qwen3-1.7b/Qwen3-1.7B-Q8_0.gguf",
"--alias", "Qwen3-1.7B",
"--host", "0.0.0.0", "--port", "18080",
"--ctx-size", "2048", "--threads", "${LLM_THREADS:-4}", "--parallel", "1",
"--reasoning-budget", "0",
]
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:18080/health"]
interval: 10s
timeout: 3s
retries: 30
start_period: 180s
# Vikhr — рекомендации к разбору и грамматика описания в упражнении 112.
llm-vikhr:
image: registry.gglamer.ru/pub/lct-hack-llm:2026.09.29
build: *llm-build
restart: unless-stopped
command:
[
"-m", "/models/vikhr-1b/Vikhr-Llama-3.2-1B-Q4_K_M.gguf",
"--alias", "Vikhr-1B",
"--host", "0.0.0.0", "--port", "18081",
"--ctx-size", "2048", "--threads", "${LLM_THREADS:-4}", "--parallel", "1",
]
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:18081/health"]
interval: 10s
timeout: 3s
retries: 30
start_period: 180s
volumes:
pgdata:
securitydata:
recordings:
backups:
sipdata:
siprecordings: