# Deployment Guide Полная инструкция по развёртыванию ZBrain с нуля. ## Стадия 1: Подготовка VM ### Спецификация | Параметр | Значение | |----------|----------| | ОС | Ubuntu 22.04 LTS Server | | CPU | 4 vCPU (минимум 2) | | RAM | 8 GB (минимум 4) | | Disk | 80 GB SSD (минимум 40) | | FS | ext4 | | Network | статический IP в сети ZETIT | ### Создание 1. На гипервизоре создать VM с указанными параметрами 2. Установить Ubuntu 22.04 LTS Server (minimal) 3. Создать пользователя `admin` с sudo правами 4. Скопировать SSH-ключи: `ssh-copy-id admin@` 5. Отключить password auth: `sudo passwd -l root` и в `/etc/ssh/sshd_config` `PasswordAuthentication no` ### Базовая конфигурация ```bash # Hostname sudo hostnamectl set-hostname zbrain echo "127.0.1.1 zbrain.zetit.local zbrain" | sudo tee -a /etc/hosts # Часовой пояс sudo timedatectl set-timezone Europe/Moscow # Обновления sudo apt-get update && sudo apt-get upgrade -y sudo apt-get install -y unattended-upgrades ``` ### DNS В твоей внутренней DNS зоне (zetit.local или эквивалент): ``` brain.zetit.local. A ``` На внешнем DNS (если используешь публичный контур): ``` brain.zetit.ru. A ``` Edge-сервер (где живёт nginx с публичным контуром) проксирует на внутренний IP VM. ## Стадия 2: Bootstrap ```bash # Склонируй ZBrain repo sudo mkdir -p /opt/zbrain && sudo chown $USER:$USER /opt/zbrain git clone git@git.zetit.ru:zuevav/ZBrain.git /opt/zbrain cd /opt/zbrain # Зеркало gbrain (один раз, не на каждой VM) # Это делается единожды на твоей рабочей машине: # # git clone --mirror https://github.com/garrytan/gbrain.git # cd gbrain.git # git push --mirror git@git.zetit.ru:zuevav/gbrain-mirror.git # Запусти bootstrap sudo TOTAL_RAM_GB=8 FNA_PROXY="http://client_001:PASSWORD@fna.zetit.ru:3128" bash scripts/bootstrap-vm.sh ``` После завершения скрипт покажет следующие шаги. ## Стадия 3: Postgres secrets ```bash sudo -u postgres psql ``` ```sql -- Пароль root postgres юзера (сохрани в KeePass) ALTER USER postgres PASSWORD 'STRONG_RANDOM_PASSWORD_24_CHARS'; -- Пользователь для brainhub CREATE USER brainhub WITH PASSWORD 'ANOTHER_STRONG_PASSWORD'; GRANT ALL PRIVILEGES ON DATABASE brainhub TO brainhub; -- Проверка \du \l \q ``` ## Стадия 4: Заполнение .env ```bash sudo cp /etc/zbrain/env.example /etc/zbrain/.env sudo chmod 600 /etc/zbrain/.env sudo chown zbrain:zbrain /etc/zbrain/.env sudo nano /etc/zbrain/.env ``` Сгенерируй секреты: ```bash # SESSION_SECRET openssl rand -hex 32 # JWT_SECRET openssl rand -hex 32 # TOKEN_ENCRYPTION_KEY (для шифрования TOTP секретов) openssl rand -hex 16 ``` Заполни: - `DATABASE_URL` — с паролем brainhub - Секреты выше - `OPENAI_API_KEY` и `ANTHROPIC_API_KEY` - FNA-прокси с реальными credentials - `INITIAL_OWNER_EMAIL` и `INITIAL_OWNER_PASSWORD` (временный, сменишь после первого логина) ## Стадия 5: Первый брейн ```bash sudo bash /opt/zbrain/scripts/create-brain.sh zetit "ZETIT MSP" 3001 ``` Скрипт: 1. Создаст пользователя `gbrain_zetit` и БД `gbrain_zetit` в Postgres 2. Положит конфиг в `/var/lib/zbrain/brains/zetit/` 3. Запустит миграции gbrain 4. Создаст и запустит systemd unit `zbrain-gbrain-zetit` Проверка: ```bash systemctl status zbrain-gbrain-zetit journalctl -u zbrain-gbrain-zetit -f # в отдельном окне curl http://localhost:3001/health ``` ## Стадия 6: Импорт первых данных ```bash sudo -u zbrain bash -c ' export PATH=$HOME/.bun/bin:$PATH source /etc/zbrain/.env cd /var/lib/zbrain/gbrain bun run gbrain import /path/to/your/markdown/files ' ``` Проверка: ```bash sudo -u postgres psql -d gbrain_zetit -c ' SELECT (SELECT count(*) FROM pages) AS pages, (SELECT count(*) FROM content_chunks) AS chunks, (SELECT count(*) FROM content_chunks WHERE embedding IS NOT NULL) AS embedded; ' ``` ## Стадия 7: Brainhub API + Web > Эта часть требует, чтобы код Sprint 2-5 был написан. До этого пропускаем. ### Build ```bash cd /opt/zbrain sudo -u zbrain bun install sudo -u zbrain bun run build ``` ### Миграции ```bash sudo -u zbrain bun run --cwd apps/api db:migrate ``` ### Запуск через docker-compose (рекомендуется в prod) ```bash cd /opt/zbrain/deploy/docker sudo docker compose up -d sudo docker compose ps ``` ### Альтернатива: systemd Если не хочешь docker - использовать `deploy/systemd/zbrain-brainhub.service`. ## Стадия 8: Nginx (внутренний контур) ```bash sudo apt-get install -y nginx # Скопируй конфиг sudo cp /opt/zbrain/deploy/nginx/zbrain.conf /etc/nginx/sites-available/zbrain # Закомментируй временно public server-блок (TLS пока нет) sudo ln -s /etc/nginx/sites-available/zbrain /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default # Проверь sudo nginx -t # Запусти sudo systemctl reload nginx ``` Тест: ```bash curl -H "Host: brain.zetit.local" http://localhost/ ``` ## Стадия 9: TLS для публичного контура > Только если у тебя есть edge-сервер с внешним IP и DNS-запись brain.zetit.ru указывает на него. На edge-сервере (или на самой VM, если она имеет внешний IP): ```bash sudo apt-get install -y certbot python3-certbot-nginx sudo certbot --nginx -d brain.zetit.ru ``` Раскомментируй public server-блок в nginx и перезагрузи. ## Стадия 10: Первый логин 1. Открой http://brain.zetit.local в браузере 2. Залогинься как `INITIAL_OWNER_EMAIL` / `INITIAL_OWNER_PASSWORD` 3. **Сразу смени пароль** в Profile 4. Включи 2FA (когда Sprint 7 готов) 5. Удали `INITIAL_OWNER_*` из `.env` (необязательно, но гигиенично) ## Стадия 11: Подключение Claude Code 1. В UI: создай токен с scope `mcp:write:zetit` 2. Скопируй токен 3. На своей рабочей машине добавь в `~/.claude/mcp.json` или в `.claude/mcp.json` проекта: ```json { "mcpServers": { "zbrain-zetit": { "transport": "http", "url": "http://brain.zetit.local/mcp/zetit", "headers": { "Authorization": "Bearer brain_pat_" } } } } ``` 4. Перезапусти Claude Code 5. Проверь что MCP-инструменты доступны ## Стадия 12: Backup ```bash # Создай age ключ для шифрования бэкапов (только один раз!) age-keygen -o /tmp/backup.age cat /tmp/backup.age # сохрани оба ключа в KeePass! # Положи публичную часть на VM sudo head -1 /tmp/backup.age | grep public | awk '{print $NF}' > /tmp/pub sudo mv /tmp/pub /etc/zbrain/backup.age.pub sudo chmod 644 /etc/zbrain/backup.age.pub # Удали /tmp/backup.age с VM! shred -u /tmp/backup.age # Тест бэкапа sudo bash /opt/zbrain/scripts/backup.sh # Добавь в cron sudo crontab -e # Добавь строку: # 0 3 * * * /opt/zbrain/scripts/backup.sh >> /var/log/zbrain/backup.log 2>&1 ``` ## Чек-лист готовности - [ ] VM создана с правильными параметрами - [ ] Bootstrap отработал без ошибок - [ ] Postgres работает, пароли установлены - [ ] `.env` заполнен и защищён 600 - [ ] Первый брейн создан и работает - [ ] Данные импортированы - [ ] Brainhub запущен и доступен через nginx - [ ] OAuth настроен (если нужно) - [ ] TLS работает (если публичный контур) - [ ] Owner логин работает, пароль сменён - [ ] Claude Code успешно подключается через токен - [ ] Backup делается и шифруется - [ ] Восстановление из бэкапа протестировано (на отдельной VM) - [ ] Мониторинг настроен (опционально) - [ ] Документация в `docs/INFRASTRUCTURE.md` обновлена ## Типовые проблемы ### gbrain не стартует ```bash journalctl -u zbrain-gbrain-zetit -n 50 --no-pager ``` Частые причины: - Пароль в DATABASE_URL не совпадает - FNA-прокси недоступен - Bun не в PATH (проверь Environment="PATH=..." в unit-файле) ### Embeddings не создаются ```bash # Проверь подключение к OpenAI через FNA sudo -u zbrain bash -c ' source /etc/zbrain/.env curl --proxy "$HTTPS_PROXY" https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY" ' ``` ### Postgres падает ```bash # Проверь логи sudo tail -100 /var/log/postgresql/postgresql-16-main.log # Проверь disk space df -h /var/lib/postgresql/ # Проверь shared_buffers vs RAM sudo -u postgres psql -c 'SHOW shared_buffers; SHOW effective_cache_size;' ``` ### Брейн занимает слишком много места ```bash # Размер по таблицам sudo -u postgres psql -d gbrain_zetit -c " SELECT schemaname || '.' || tablename AS table, pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC; " ``` Если эмбеддинги занимают слишком много — можно перейти на `text-embedding-3-small` (вместо `large`), это даст 1536 → 1024 dim и -33% места.