This commit is contained in:
zuevav
2026-05-20 19:33:02 +03:00
commit f4bca8449e
30 changed files with 4152 additions and 0 deletions
+362
View File
@@ -0,0 +1,362 @@
# 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@<ip>`
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 <internal-ip>
```
На внешнем DNS (если используешь публичный контур):
```
brain.zetit.ru. A <external-ip-of-edge-server>
```
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_<your-token>"
}
}
}
}
```
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% места.