#!/usr/bin/env bash # # ZBrain VM Bootstrap # ==================== # Подготавливает чистую Ubuntu 22.04 VM к работе как ZBrain хост. # Устанавливает: Postgres 16 + pgvector, Node 22 LTS, Bun, Docker, gbrain. # # Использование: # sudo bash scripts/bootstrap-vm.sh # # Предполагается: запуск от root на свежей Ubuntu 22.04 LTS. # Скрипт идемпотентный - можно перезапускать. set -euo pipefail # ============================================================ # Конфигурация - правь под себя ПЕРЕД запуском # ============================================================ ZBRAIN_USER="${ZBRAIN_USER:-zbrain}" ZBRAIN_HOME="/opt/zbrain" ZBRAIN_CONFIG_DIR="/etc/zbrain" ZBRAIN_DATA_DIR="/var/lib/zbrain" ZBRAIN_LOG_DIR="/var/log/zbrain" # FNA-прокси для исходящих запросов к OpenAI/Anthropic FNA_PROXY="${FNA_PROXY:-http://client_001:PASSWORD@fna.zetit.ru:3128}" POSTGRES_VERSION="16" NODE_VERSION="22" GBRAIN_VERSION="${GBRAIN_VERSION:-master}" # потом закрепим тег после первого деплоя GBRAIN_REPO="${GBRAIN_REPO:-https://git.zetit.ru/zuevav/gbrain-mirror.git}" # RAM в гигабайтах (для тюнинга Postgres) TOTAL_RAM_GB="${TOTAL_RAM_GB:-8}" # ============================================================ # Утилиты # ============================================================ log() { echo -e "\033[1;34m[$(date +'%H:%M:%S')] $*\033[0m" } err() { echo -e "\033[1;31m[ERROR] $*\033[0m" >&2 } require_root() { if [[ $EUID -ne 0 ]]; then err "Этот скрипт должен запускаться от root (sudo)." exit 1 fi } check_ubuntu() { if ! grep -q "Ubuntu 22.04" /etc/os-release 2>/dev/null; then err "Скрипт рассчитан на Ubuntu 22.04 LTS." err "Текущая система:" cat /etc/os-release | grep PRETTY_NAME >&2 || true read -p "Продолжить на свой страх и риск? [y/N] " -n 1 -r echo [[ $REPLY =~ ^[Yy]$ ]] || exit 1 fi } # ============================================================ # Шаг 1: Базовые пакеты и обновления # ============================================================ step_base_packages() { log "Шаг 1: Базовые пакеты" export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq \ curl wget gnupg lsb-release ca-certificates \ build-essential git \ ufw fail2ban \ htop iotop net-tools \ unzip jq \ software-properties-common \ apt-transport-https \ chrony # точное время для аудит-логов log "Шаг 1: ✓" } # ============================================================ # Шаг 2: Создание пользователя zbrain # ============================================================ step_create_user() { log "Шаг 2: Пользователь $ZBRAIN_USER" if ! id "$ZBRAIN_USER" &>/dev/null; then useradd --system --create-home --shell /bin/bash "$ZBRAIN_USER" log "Создан пользователь $ZBRAIN_USER" fi mkdir -p "$ZBRAIN_HOME" "$ZBRAIN_CONFIG_DIR" "$ZBRAIN_DATA_DIR" "$ZBRAIN_LOG_DIR" chown -R "$ZBRAIN_USER:$ZBRAIN_USER" "$ZBRAIN_HOME" "$ZBRAIN_DATA_DIR" "$ZBRAIN_LOG_DIR" chmod 750 "$ZBRAIN_CONFIG_DIR" # секреты - 750 log "Шаг 2: ✓" } # ============================================================ # Шаг 3: Системные параметры (sysctl, swap, файрвол) # ============================================================ step_system_tuning() { log "Шаг 3: Системные параметры" # Swap для защиты от OOM if [[ ! -f /swapfile ]]; then fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab log "Создан swap 4G" fi # sysctl для Postgres + сети cat > /etc/sysctl.d/99-zbrain.conf </dev/null # UFW firewall ufw --force reset >/dev/null ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp comment 'SSH' ufw allow 80/tcp comment 'HTTP (redirect to HTTPS)' ufw allow 443/tcp comment 'HTTPS' # Postgres - ТОЛЬКО localhost, никаких ufw allow 5432! ufw --force enable log "Шаг 3: ✓" } # ============================================================ # Шаг 4: PostgreSQL 16 + pgvector # ============================================================ step_postgres() { log "Шаг 4: PostgreSQL $POSTGRES_VERSION + pgvector" # Репозиторий PGDG if [[ ! -f /etc/apt/sources.list.d/pgdg.list ]]; then sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - apt-get update -qq fi apt-get install -y -qq \ postgresql-$POSTGRES_VERSION \ postgresql-contrib-$POSTGRES_VERSION \ postgresql-$POSTGRES_VERSION-pgvector \ postgresql-client-$POSTGRES_VERSION # Тюнинг postgresql.conf под объём RAM local pg_conf="/etc/postgresql/$POSTGRES_VERSION/main/postgresql.conf" local shared_buffers=$((TOTAL_RAM_GB * 1024 / 4)) # 25% от RAM в MB local effective_cache=$((TOTAL_RAM_GB * 1024 / 2)) # 50% от RAM в MB cat > /etc/postgresql/$POSTGRES_VERSION/main/conf.d/zbrain.conf < /etc/postgresql/$POSTGRES_VERSION/main/pg_hba.conf </dev/null || log "БД brainhub уже существует" sudo -u postgres psql -d brainhub -c "CREATE EXTENSION IF NOT EXISTS vector;" sudo -u postgres psql -d brainhub -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" sudo -u postgres psql -d brainhub -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;" log "Шаг 4: ✓ Postgres работает, BD brainhub готова" log " ВАЖНО: создай пароли для пользователей postgres и brainhub:" log " sudo -u postgres psql" log " ALTER USER postgres PASSWORD '...';" log " CREATE USER brainhub WITH PASSWORD '...';" log " GRANT ALL PRIVILEGES ON DATABASE brainhub TO brainhub;" } # ============================================================ # Шаг 5: Node.js 22 LTS + Bun # ============================================================ step_node_bun() { log "Шаг 5: Node.js $NODE_VERSION LTS + Bun" # Node через NodeSource if ! command -v node &>/dev/null || [[ "$(node -v)" != v${NODE_VERSION}* ]]; then curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - apt-get install -y -qq nodejs fi # Bun (для gbrain) if ! sudo -u "$ZBRAIN_USER" bash -c 'command -v bun' &>/dev/null; then sudo -u "$ZBRAIN_USER" bash -c 'curl -fsSL https://bun.sh/install | bash' # Добавим в PATH sudo -u "$ZBRAIN_USER" bash -c 'echo "export PATH=\$HOME/.bun/bin:\$PATH" >> ~/.bashrc' fi log " Node: $(node -v)" log " npm: $(npm -v)" log "Шаг 5: ✓" } # ============================================================ # Шаг 6: Docker + Compose # ============================================================ step_docker() { log "Шаг 6: Docker" if ! command -v docker &>/dev/null; then # Официальный репо Docker (не snap!) install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list apt-get update -qq apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin fi # zbrain в группу docker usermod -aG docker "$ZBRAIN_USER" systemctl enable docker systemctl start docker log " Docker: $(docker --version)" log " Compose: $(docker compose version)" log "Шаг 6: ✓" } # ============================================================ # Шаг 7: gbrain (клонирование из mirror, установка зависимостей) # ============================================================ step_gbrain() { log "Шаг 7: gbrain (из $GBRAIN_REPO)" local gbrain_dir="$ZBRAIN_DATA_DIR/gbrain" if [[ ! -d "$gbrain_dir" ]]; then sudo -u "$ZBRAIN_USER" git clone "$GBRAIN_REPO" "$gbrain_dir" else log " gbrain уже склонирован в $gbrain_dir" sudo -u "$ZBRAIN_USER" bash -c "cd '$gbrain_dir' && git fetch" fi sudo -u "$ZBRAIN_USER" bash -c "cd '$gbrain_dir' && git checkout '$GBRAIN_VERSION'" # Установка зависимостей через bun (используем FNA-прокси) log " Устанавливаю зависимости gbrain (через FNA proxy)..." sudo -u "$ZBRAIN_USER" bash -c " export HTTPS_PROXY='$FNA_PROXY' export HTTP_PROXY='$FNA_PROXY' export PATH=\$HOME/.bun/bin:\$PATH cd '$gbrain_dir' && bun install " log "Шаг 7: ✓" } # ============================================================ # Шаг 8: Конфиг-файлы и шаблоны # ============================================================ step_config_templates() { log "Шаг 8: Шаблоны конфигов" # .env шаблон cat > "$ZBRAIN_CONFIG_DIR/env.example" <<'EOF' # ZBrain Environment Configuration # Скопируй в .env и заполни значения # === База данных === DATABASE_URL=postgresql://brainhub:CHANGE_ME@localhost:5432/brainhub POSTGRES_ADMIN_URL=postgresql://postgres:CHANGE_ME@localhost:5432/postgres # === FNA Proxy (для исходящих к OpenAI/Anthropic) === HTTPS_PROXY=http://client_001:PASSWORD@fna.zetit.ru:3128 HTTP_PROXY=http://client_001:PASSWORD@fna.zetit.ru:3128 NO_PROXY=localhost,127.0.0.1,.zetit.ru,.zetit.local # === API ключи === OPENAI_API_KEY=sk-... ANTHROPIC_API_KEY=sk-ant-... # === ZBrain === ZBRAIN_BASE_URL=https://brain.zetit.ru ZBRAIN_INTERNAL_URL=http://brain.zetit.local SESSION_SECRET=GENERATE_RANDOM_64_CHARS JWT_SECRET=GENERATE_RANDOM_64_CHARS TOKEN_ENCRYPTION_KEY=GENERATE_RANDOM_32_BYTES_HEX # === OAuth === YANDEX_OAUTH_CLIENT_ID= YANDEX_OAUTH_CLIENT_SECRET= GITHUB_OAUTH_CLIENT_ID= GITHUB_OAUTH_CLIENT_SECRET= # === Порты === BRAINHUB_INTERNAL_PORT=3000 BRAINHUB_PUBLIC_PORT=3010 GBRAIN_PORT_RANGE_START=3001 GBRAIN_PORT_RANGE_END=3099 # === Логирование === LOG_LEVEL=info LOG_FILE=/var/log/zbrain/brainhub.log EOF chmod 640 "$ZBRAIN_CONFIG_DIR/env.example" chown root:"$ZBRAIN_USER" "$ZBRAIN_CONFIG_DIR/env.example" log " Шаблон env: $ZBRAIN_CONFIG_DIR/env.example" log " Не забудь: cp $ZBRAIN_CONFIG_DIR/env.example $ZBRAIN_CONFIG_DIR/.env && chmod 600 $ZBRAIN_CONFIG_DIR/.env" log "Шаг 8: ✓" } # ============================================================ # Шаг 9: Самопроверка # ============================================================ step_verify() { log "Шаг 9: Самопроверка" local errors=0 check() { local name="$1" local cmd="$2" if eval "$cmd" &>/dev/null; then log " ✓ $name" else err " ✗ $name (команда: $cmd)" errors=$((errors+1)) fi } check "PostgreSQL запущен" "systemctl is-active postgresql" check "Postgres доступен" "sudo -u postgres psql -c 'SELECT version()'" check "pgvector установлен" "sudo -u postgres psql -d brainhub -c 'SELECT * FROM pg_extension WHERE extname = '\''vector'\''' | grep -q vector" check "Node.js установлен" "node --version" check "Docker запущен" "systemctl is-active docker" check "Bun установлен" "sudo -u $ZBRAIN_USER bash -c 'PATH=\$HOME/.bun/bin:\$PATH bun --version'" check "gbrain склонирован" "test -d $ZBRAIN_DATA_DIR/gbrain" check "UFW активен" "ufw status | grep -q 'Status: active'" check "Swap включён" "swapon --show | grep -q swapfile" if [[ $errors -gt 0 ]]; then err "Обнаружено $errors ошибок. Проверь логи выше." exit 1 fi log "Шаг 9: ✓ Всё работает" } # ============================================================ # Итоговый отчёт # ============================================================ final_report() { cat <