Skip to content

Latest commit

 

History

History
182 lines (129 loc) · 12.2 KB

File metadata and controls

182 lines (129 loc) · 12.2 KB

Инструкция оператора: SpawnDock API (Docker Compose)

Документ описывает продакшен-разворот одним файлом docker-compose.prod.yml: MCP-сервер и бот внутри Docker-сети, наружу выставлены только порты 80 и 443 через Caddy. Режимы Qwen, монтирование знаний и QWEN_MODE=container (Docker socket) описаны ниже.

1. Подготовка на хосте

  1. Клонируйте репозиторий API и перейдите в его корень (каталог, где лежат Dockerfile и docker-compose.prod.yml).
  2. Создайте файл .env в этом же каталоге — он используется так:
    • Подстановка в Compose (${VAR} в docker-compose.prod.yml, в т.ч. PUBLIC_HOST для сервиса caddy) читается из .env в корне проекта автоматически.
    • Переменные внутри контейнеров приложения задаются директивой env_file: .env у сервисов mcp-server и bot (caddy получает только PUBLIC_HOST, без остальных секретов).
  3. Скопируйте шаблон и заполните значения:
cp .env.example .env
chmod 600 .env
  1. Создайте каталог состояния и при необходимости наполните knowledge/:
mkdir -p data/state knowledge
  1. Запуск и проверка:
docker compose -f docker-compose.prod.yml up -d --build
curl -fsS http://127.0.0.1/health

Для схемы HTTPS с доменом см. раздел 4; до тех пор по умолчанию в .env задаётся PUBLIC_HOST=:80 (только HTTP на порту 80).

2. Состав стека

Сервис Назначение
qwen-search Одноразовый job: собирает образ spawndock/qwen-search:prod для вызовов docker run из MCP.
mcp-server Control plane, MCP (/sse), туннель preview, поиск по базе знаний. Порт 3000 доступен только внутри сети Compose.
bot Telegram polling-бот; CONTROL_PLANE_URL=http://mcp-server:3000 (внутренняя сеть).
caddy Публикует 80/tcp и 443/tcp, проксирует на mcp-server:3000. Адрес сайта задаётся PUBLIC_HOST в .env.

3. Переменные в .env (справочник)

Ниже — переменные из .env.example с пояснениями. Все чувствительные значения храните только в .env, не коммитьте его.

Compose и Caddy

Переменная Обязательность Описание
PUBLIC_HOST Рекомендуется Адрес «сайта» для Caddy: :80 — HTTP на всех интерфейсах (порт 80); api.example.com — то же имя в DNS на эту машину → Caddy запросит Let’s Encrypt и поднимет HTTPS.
QWEN_KNOWLEDGE_HOST_PATH Если QWEN_MODE=container Абсолютный путь на хосте к каталогу знаний (тот же физический каталог, что монтируется как ./knowledge:/app/knowledge:ro). Нужен для docker run -v со стороны демона Docker. Пример: /srv/spawndock-api/knowledge.

Приложение (mcp-server)

Переменная Описание
PORT Порт HTTP внутри контейнера (обычно 3000); снаружи доступ через Caddy.
RATE_LIMIT_RPS Лимит запросов в секунду.
PUBLIC_ORIGIN Публичный URL без завершающего / (как клиенты открывают API), например https://api.example.com или http://203.0.113.10. Должен совпадать со схемой и хостом, которые реально используются за Caddy.
STATE_FILE Путь к JSON состояния внутри контейнера; в Compose обычно /app/.spawndock/state.json (том ./data/state).
SPAWNDOCK_BOT_SECRET Секрет для внутренних вызовов control plane (сгенерируйте случайную строку).

Qwen / поиск

Переменная Описание
QWEN_MODE http — внешний API; container — изолированный docker run образа поиска; cli — бинарь внутри образа API (в prod обычно не используется).
QWEN_API_URL Базовый URL API при QWEN_MODE=http.
HF_TOKEN Токен Hugging Face (если провайдер им требуется).
QWEN_TIMEOUT_MS Таймаут запроса к модели.
QWEN_CODE_COMMAND, QWEN_CODE_AUTH_TYPE Настройки CLI в режиме container/cli.
QWEN_CONTAINER_IMAGE Образ для QWEN_MODE=container (в Compose по умолчанию переопределяется на spawndock/qwen-search:prod).
QWEN_CONTAINER_RUNTIME docker или podman.
QWEN_CONTAINER_CORPUS_PATH Путь монтирования корпуса внутри одноразового контейнера поиска.
QWEN_CONTAINER_MAX_STDOUT_BYTES, QWEN_CONTAINER_MAX_STDERR_BYTES Лимиты вывода CLI.

Telegram (бот)

Переменная Описание
TELEGRAM_BOT_TOKEN Токен от BotFather.
TELEGRAM_BOT_USERNAME Username бота (без @).
TELEGRAM_MINI_APP_SHORT_NAME Short name Mini App.
CONTROL_PLANE_URL В Compose для сервиса bot задаётся в docker-compose.prod.yml как http://mcp-server:3000; в .env.example значение для локального запуска без Compose.
BOT_POLL_TIMEOUT Таймаут long polling.

Ужесточение Docker (опционально)

Переменная Описание
DOCKER_GID GID группы docker на хосте (getent group docker | cut -d: -f3), если убираете user: "0:0" у mcp-server и добавляете group_add в compose (см. раздел 7).

Полный перечень ключей с плейсхолдерами см. в .env.example.

4. HTTP и HTTPS (один docker-compose.prod.yml)

  • Только HTTP (по IP или без TLS): в .env укажите PUBLIC_HOST=:80 и PUBLIC_ORIGIN=http://<ваш-хост-или-IP> (без :3000). Проверка: curl -fsS http://<IP>/health.
  • HTTPS (Let’s Encrypt): запись DNS A/AAAA для вашего имени должна указывать на эту машину; откройте 80 и 443 на firewall. В .env:
PUBLIC_HOST=api.example.com
PUBLIC_ORIGIN=https://api.example.com

Перезапуск:

docker compose -f docker-compose.prod.yml up -d --build

Проверка:

curl -fsS https://api.example.com/health

Сертификаты хранятся в томе caddy_data. Для экспериментов с ACME staging можно временно добавить в Caddyfile глобальный блок с acme_ca (не оставляйте в проде без необходимости).

5. Режимы Qwen (QWEN_MODE)

Значение Где выполняется LLM Требования в Compose
http (по умолчанию) Внешний API (QWEN_API_URL, HF и т.д.) Только сеть и секреты в .env.
cli Бинарь qwen внутри образа mcp-server Обычно не используется в slim prod-образе.
container Каждый запрос: docker run --rm образа QWEN_CONTAINER_IMAGE Смонтированный docker.sock, собранный qwen-search, QWEN_KNOWLEDGE_HOST_PATH на абсолютный путь к тому же knowledge/, что ./knowledge на хосте.

Каталог ./knowledge с хоста монтируется в контейнер API как /app/knowledge:ro. Для QWEN_MODE=container переменная QWEN_KNOWLEDGE_HOST_PATH обязательна и должна указывать на тот же каталог на хосте, что вы используете как ./knowledge в compose (например /opt/mcp/knowledge), потому что docker run -v выполняется демоном Docker на хосте.

Пример фрагмента .env для container-режима:

QWEN_MODE=container
QWEN_CONTAINER_IMAGE=spawndock/qwen-search:prod
QWEN_CONTAINER_RUNTIME=docker
QWEN_KNOWLEDGE_HOST_PATH=/srv/spawndock-api/knowledge

6. Безопасность Docker socket

mcp-server в штатном docker-compose.prod.yml идёт от root (user: "0:0"), чтобы стабильно использовать /var/run/docker.sock. Это даёт контроль над Docker на хосте с точки зрения возможностей процесса API. Ограничьте доступ к публичному API (firewall, TLS, секреты MCP). Вариант усиления — раздел 7.

7. Ужесточение (опционально)

  • Убрать user: "0:0" у mcp-server, добавить в compose:
group_add:
  - "${DOCKER_GID}"

На хосте: getent group docker | cut -d: -f3 → в .env: DOCKER_GID=….

  • Ограничить доступ к docker.sock и использовать только read-only монтирование корпуса в образе поиска (текущий qwen-search уже монтирует корпус :ro).

8. Обновление документации (knowledge/)

  • Каталог ./knowledge примонтирован с хоста: обновите файлы на диске; пересборка образа API для смены текста не обязательна.
  • При QWEN_MODE=container после смены пути к знаниям на диске проверьте QWEN_KNOWLEDGE_HOST_PATH в .env.
  • После правок docker-compose.prod.yml или Caddyfile: docker compose -f docker-compose.prod.yml up -d --build.

9. Полезные команды

docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml logs -f mcp-server
docker compose -f docker-compose.prod.yml logs -f bot
docker compose -f docker-compose.prod.yml logs -f caddy
docker images spawndock/qwen-search:prod

Healthchecks: в docker compose ps смотрите колонку STATUS (healthy / unhealthy / starting). Если сервис unhealthy, сначала логи этого сервиса, затем детали проверки:

docker inspect "$(docker compose -f docker-compose.prod.yml ps -q mcp-server)" --format '{{json .State.Health}}' | python3 -m json.tool

(замените mcp-server на caddy или bot). mcp-server проверяется запросом к GET /health внутри контейнера; bot — доступностью http://mcp-server:3000/health; caddy — HTTP /health через прокси (для PUBLIC_HOST=:80 — на порт из значения, для FQDN — с заголовком Host).

10. Связанные документы