Skip to content

Latest commit

 

History

History
294 lines (221 loc) · 10.2 KB

File metadata and controls

294 lines (221 loc) · 10.2 KB

Развертывание SpawnDock MCP на Hetzner VPS

Операторский чеклист по Docker Compose, QWEN_MODE=container, Docker socket и Caddy (80/443) — в OPERATOR.md. В firewall Hetzner открыты порты 22, 80, 443 (80/443 — публичный HTTP(S) и Let’s Encrypt). Порт 3000 наружу не публикуется: API доступен через Caddy.

Что разворачивается

Одна VPS на Hetzner (CX22: 2 vCPU, 4 GB RAM, 40 GB диск) с двумя сервисами:

  • Caddy (порты 80/443) — reverse proxy на MCP-сервер
  • MCP Server (порт 3000 только внутри Docker-сети) — MCP бекенд + control plane + WebSocket tunnel
  • Telegram Bot — polling-бот для создания проектов и preview

Qwen-инференс идёт через Hugging Face Inference API (не локально — нет GPU).

Предварительные требования

1. Установи OpenTofu

# macOS
brew install opentofu

# Linux
curl -fsSL https://get.opentofu.org/install-opentofu.sh | bash

# Windows (через scoop)
scoop install opentofu

2. Получи токены

Токен Где взять
Hetzner API Token console.hetzner.cloud → Project → Security → API Tokens → Generate
HuggingFace Token huggingface.co/settings/tokens → New token (Read)
Telegram Bot Token @BotFather → /newbot → скопировать токен
Telegram Bot Username Username бота, например rustgpt_bot
Telegram Mini App Short Name Short name Mini App из BotFather, например test

3. Убедись что есть SSH-ключ

# Проверь
ls ~/.ssh/id_ed25519.pub

# Если нет — создай
ssh-keygen -t ed25519

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

Шаг 1: Создай файл секретов

cd infra
cp terraform.tfvars.example terraform.tfvars

Отредактируй terraform.tfvars:

hcloud_token        = "ВСТАВЬ_HETZNER_API_TOKEN"
hf_token            = "hf_ВСТАВЬ_HUGGINGFACE_TOKEN"
ssh_public_key_path = "~/.ssh/id_ed25519.pub"
telegram_bot_token  = "123456:ABC-ВСТАВЬ_TELEGRAM_BOT_TOKEN"
telegram_bot_username = "rustgpt_bot"
telegram_mini_app_short_name = "test"
public_origin       = "http://PLACEHOLDER"
public_host         = "spawn-dock.w3voice.net"

Для production по умолчанию используем https://spawn-dock.w3voice.net.

Шаг 2: Инициализация и проверка

cd infra
tofu init
tofu validate

Ожидаемый вывод: Success! The configuration is valid.

Шаг 3: Посмотри план

tofu plan

Должно показать создание 3 ресурсов:

  • hcloud_ssh_key.default
  • hcloud_firewall.mcp
  • hcloud_server.mcp

Шаг 4: Разверни

tofu apply

Набери yes для подтверждения. Через 1-2 минуты получишь вывод:

server_ip   = "1.2.3.4"
ssh_command = "ssh root@1.2.3.4"
mcp_url     = "https://spawn-dock.w3voice.net"

Шаг 5: Убедись, что домен указывает на VPS

Отредактируй terraform.tfvars:

public_origin = "https://spawn-dock.w3voice.net"
public_host   = "spawn-dock.w3voice.net"

Если домен ещё не указывает на VPS, сначала обнови DNS A/AAAA записи. После этого примени изменение:

tofu apply

Это пересоздаст VPS с правильным PUBLIC_ORIGIN и доменным PUBLIC_HOST. Всё автоматически.

Шаг 6: Дождись запуска (3-5 минут)

Cloud-init устанавливает Docker, клонирует репо, собирает и запускает контейнеры. Проверь:

# Подключись к серверу
ssh root@1.2.3.4

# Проверь статус cloud-init
cloud-init status --wait

# Проверь контейнеры
cd /opt/mcp && docker compose -f docker-compose.prod.yml ps

# Логи MCP сервера
docker compose -f docker-compose.prod.yml logs mcp-server

# Логи бота
docker compose -f docker-compose.prod.yml logs bot

Шаг 7: Проверь что всё работает

# Health check
curl https://spawn-dock.w3voice.net/health
# Ожидаемо: {"status":"ok"}

# Проверь бота — отправь /help в Telegram

Что происходит внутри

tofu apply
    ↓
Hetzner создаёт VPS (Ubuntu 24.04)
    ↓
cloud-init:
    1. apt install docker
    2. git clone SpawnDock/MCP → /opt/mcp
    3. Создаёт .env с токенами
    4. docker compose up --build
        ├── caddy (80/443 → mcp-server:3000)
        ├── mcp-server (внутренний :3000)
        └── bot (polling Telegram)
    5. Smoke test: curl localhost/health

Архитектура на VPS

┌─────────────────────────────────────┐
│         Hetzner CX22 VPS            │
│                                     │
│  ┌───────────────────────────────┐  │
│  │ Docker Compose                │  │
│  │                               │  │
│  │  ┌─────────────────────────┐  │  │
│  │  │ caddy (:80, :443)     │  │  │
│  │  └───────────┬─────────────┘  │  │
│  │              │ reverse_proxy   │  │
│  │  ┌───────────▼─────────────┐  │  │
│  │  │ mcp-server (:3000)      │  │  │
│  │  │ ├─ MCP бекенд (/sse)   │  │  │
│  │  │ ├─ Control Plane API    │  │  │
│  │  │ ├─ WebSocket Tunnel     │  │  │
│  │  │ └─ Preview Proxy        │  │  │
│  │  └─────────────────────────┘  │  │
│  │                               │  │
│  │  ┌─────────────────────────┐  │  │
│  │  │ bot                     │  │  │
│  │  │ └─ Telegram polling     │  │  │
│  │  └─────────────────────────┘  │  │
│  └───────────────────────────────┘  │
│                                     │
│  Firewall: 22 (SSH), 80 (HTTP), 443 (HTTPS)      │
└─────────────────────────────────────┘
         │
         │  Qwen запросы
         ↓
  HuggingFace Inference API

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

# Подключиться к серверу
ssh root@$(cd infra && tofu output -raw server_ip)

# Перезапустить сервисы
ssh root@IP "cd /opt/mcp && docker compose -f docker-compose.prod.yml restart"

# Обновить код на сервере (без пересоздания VPS)
ssh root@IP "cd /opt/mcp && git pull && docker compose -f docker-compose.prod.yml up -d --build"

# Посмотреть логи в реальном времени
ssh root@IP "cd /opt/mcp && docker compose -f docker-compose.prod.yml logs -f"

# Проверить состояние проектов
ssh root@IP "cat /opt/mcp/data/state/state.json | python3 -m json.tool"

Удаление инфраструктуры

cd infra
tofu destroy

Набери yes. VPS и все ресурсы Hetzner будут удалены. Ничего не останется.

Устранение неполадок

cloud-init не завершился

ssh root@IP
cat /var/log/cloud-init-output.log | tail -50

Контейнеры не запустились

ssh root@IP
cd /opt/mcp
docker compose -f docker-compose.prod.yml logs

Бот не отвечает

# Проверь что TELEGRAM_* переменные и short name на месте
ssh root@IP "grep TELEGRAM /opt/mcp/.env"

# Проверь логи бота
ssh root@IP "cd /opt/mcp && docker compose -f docker-compose.prod.yml logs bot"

MCP клиент не подключается

# Проверь что сервер отвечает (порт 80, Caddy)
curl http://IP/health

# Проверь SSE endpoint
curl -N http://IP/sse

Переменные окружения (справка)

Переменная Описание Значение по умолчанию
PORT Порт сервера 3000
QWEN_API_URL URL Qwen/HF API http://localhost:8080
HF_TOKEN HuggingFace токен
QWEN_MODEL Модель для инференса qwen3-coder
QWEN_TIMEOUT_MS Таймаут запроса к Qwen 60000
RATE_LIMIT_RPS Лимит запросов в секунду 10
PUBLIC_HOST Адрес Caddy (:80 или FQDN для HTTPS) :80
PUBLIC_ORIGIN Публичный URL сервера (как у клиентов) http://localhost
TELEGRAM_BOT_TOKEN Токен Telegram бота
TELEGRAM_BOT_USERNAME Username бота для deep link
TELEGRAM_MINI_APP_SHORT_NAME Short name Mini App для deep link
STATE_FILE Путь к файлу состояния .spawndock/state.json
TUNNEL_PATH Путь WebSocket tunnel /tunnel/connect
PREVIEW_PREFIX Префикс preview URL /preview
REQUEST_TIMEOUT_MS Таймаут tunnel proxy 15000
CONTROL_PLANE_URL URL control plane (для бота) http://localhost:3000