From 3feb2f431c632e5d4842774e72b3ed17010ce790 Mon Sep 17 00:00:00 2001 From: nineap Date: Thu, 9 Jul 2026 17:17:20 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B1=D0=B8=D1=80=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D1=81=D0=B2=D0=BE=D0=B9=20Postgres=20=E2=80=94=20=D0=91=D0=94?= =?UTF-8?q?=20=D1=84=D0=B8=D0=B7=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B5=D0=B7=D0=B6=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D0=BD=D0=B0=20VPS=20=D1=81=20Vaultwarden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backend будет жить на отдельном, ещё не арендованном VPS — свой контейнер `db` и бэкапы (scripts/backup.sh) здесь больше не нужны и переехали в netrunner-data, где Postgres теперь физически стоит. DATABASE_URL собирается из DB_HOST/DB_PORT (внешний IP того VPS) с обязательным sslmode=require — трафик пересекает публичную сеть между двумя разными серверами. Co-Authored-By: Claude Sonnet 5 --- .env.example | 36 ++++++++++------------ docker-compose.prod.yml | 67 +++++++---------------------------------- rclone.conf.example | 16 ---------- scripts/backup.sh | 62 -------------------------------------- 4 files changed, 27 insertions(+), 154 deletions(-) delete mode 100644 rclone.conf.example delete mode 100755 scripts/backup.sh diff --git a/.env.example b/.env.example index 2cdc5eb..e13470a 100644 --- a/.env.example +++ b/.env.example @@ -6,17 +6,22 @@ # (например `openssl rand -base64 24`) и никогда не коммитить .env с настоящим # значением (см. .gitignore — .env/.env.* уже исключены). # -# ДЛЯ ЛОКАЛЬНЫХ ТЕСТОВ НА ТВОЕМ ПК: -# DATABASE_URL=postgres://netrunner_admin:CHANGE_ME_DB_PASSWORD@:5432/netrunner -# -# ДЛЯ ЗАПУСКА НА САМОЙ ВПС (Через Docker Compose): -# DATABASE_URL=postgres://netrunner_admin:CHANGE_ME_DB_PASSWORD@db:5432/netrunner - -DATABASE_URL=postgres://netrunner_admin:CHANGE_ME_DB_PASSWORD@db:5432/netrunner +# Postgres физически стоит НЕ на этом VPS, а на отдельном сервере вместе с +# Vaultwarden (см. netrunner-data/docker-compose.yml, сервис `db`) — этот +# бэкенд подключается к нему по сети. Полный DATABASE_URL собирает сам +# docker-compose.prod.yml из DB_USER/DB_PASSWORD/DB_HOST/DB_PORT/DB_NAME ниже +# (не пишите его здесь отдельной строкой — env_file не подставляет ${...}, +# в отличие от самого docker-compose, так что отдельная строка DATABASE_URL +# тут просто не сработает). sslmode=require обязателен: без него подключение +# к hostssl-правилу в pg_hba.conf того сервера будет отклонено. DB_USER=netrunner_admin DB_NAME=netrunner DB_PASSWORD=CHANGE_ME_DB_PASSWORD +# IP VPS, где физически стоит Postgres (netrunner-data). Совпадает с DATA_VPS_IP +# в CI-переменных netrunner-data. +DB_HOST=CHANGE_ME_DB_HOST_IP +DB_PORT=5432 # Кеш курса TON (не источник истины — если недоступен, приложение просто # ходит в TonAPI напрямую на каждый инвойс, как раньше). @@ -30,18 +35,9 @@ ARGON_SALT=CHANGE_ME_ARGON_SALT_MIN_16_CHARS GRAFANA_PASSWORD=CHANGE_ME_GRAFANA_PASSWORD TON_MASTER_WALLET=UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKK8y -# ===================================================================== -# === БЭКАПЫ POSTGRES (см. scripts/backup.sh, сервис `backup` в -# === docker-compose.prod.yml) === -# ===================================================================== -# Без RCLONE_REMOTE/RCLONE_CONFIG_CONTENT бэкапы всё равно снимаются каждые -# BACKUP_INTERVAL_SEC (по умолчанию 6ч) и хранятся локально в volume -# netrunner_db_backups — но только на этом же VPS. Для реальной защиты от -# падения диска/потери сервера нужна офсайт-копия: -# BACKUP_INTERVAL_SEC=21600 -# BACKUP_RETENTION_DAYS=14 -# RCLONE_REMOTE=b2:netrunner-backups -# RCLONE_CONFIG_CONTENT содержимое rclone.conf одной переменной, см. rclone.conf.example +# Бэкапы Postgres (pg_dump + опциональный rclone) теперь настраиваются в +# netrunner-data/.env (там же, где физически стоит БД) — см. +# netrunner-data/scripts/backup.sh. Здесь их больше нет. # Опционально: без него оплата через @CryptoBot вернёт "provider not # supported", остальные провайдеры продолжают работать. Токен — у @CryptoBot: @@ -98,7 +94,7 @@ COOKIE_SECURE=true # === DEPLOYMENT & REPOSITORY SETTINGS === # ===================================================================== GHCR_TOKEN=ghp_ТВОЙ_ТОКЕН_ОТ_ГИТХАБА_ДЛЯ_ДОСТУПА_К_РЕЕСТРУ -VPS_IP=77.110.106.113 +VPS_IP=CHANGE_ME_VPS_IP DEPLOY_DIR=/root/netrunner-core BINARY_NAME=netrunner-control-plane # Образ, который тянет прод-стек (docker-compose.prod.yml). diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index a913674..424a9dc 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,6 +1,15 @@ # ===================================================================== # PRODUCTION — VPS только тянет готовый образ из GHCR и запускает его. # Никакой компиляции Rust на сервере. Деплой = pull + migrate + up. +# +# ВАЖНО: своего Postgres здесь больше нет. БД физически стоит на отдельном +# VPS (см. netrunner-data/docker-compose.yml — там же Vaultwarden, тот же +# Postgres-процесс, вторая база) и слушает по сети, не в одном +# Docker-стеке с этим compose. DATABASE_URL ниже указывает на DB_HOST:DB_PORT +# (внешний IP того VPS), не на "db" как раньше — и требует sslmode=require, +# т.к. трафик идёт по публичной сети между двумя разными серверами. Бэкапы +# тоже переехали туда же (netrunner-data/scripts/backup.sh) — дампить +# логичнее там, где физически лежат данные. # ===================================================================== services: # One-shot: применяет миграции и завершается. Требует только DATABASE_URL. @@ -9,10 +18,7 @@ services: container_name: netrunner-migrate command: ["./netrunner-control-plane", "--migrate"] environment: - - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} - depends_on: - db: - condition: service_healthy + - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT:-5432}/${DB_NAME}?sslmode=require restart: "no" networks: - netrunner_grid @@ -28,12 +34,10 @@ services: environment: - APP_ENV=production - FRONTEND_DIR=/app/frontend/dist - - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} + - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT:-5432}/${DB_NAME}?sslmode=require volumes: - ./keys:/app/keys:ro depends_on: - db: - condition: service_healthy redis: condition: service_healthy migrate: @@ -47,24 +51,6 @@ services: networks: - netrunner_grid - db: - image: postgres:16-alpine - container_name: netrunner-db - restart: always - environment: - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_USER: ${DB_USER} - POSTGRES_DB: ${DB_NAME} - volumes: - - netrunner_db_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - netrunner_grid - redis: image: redis:7-alpine container_name: netrunner-redis @@ -75,31 +61,6 @@ services: networks: - netrunner_grid - # Периодический pg_dump + опциональная офсайт-копия через rclone (см. - # scripts/backup.sh). RCLONE_REMOTE/RCLONE_CONFIG — задать в .env, если нужна - # копия за пределы этого VPS (настоятельно рекомендуется — единственная - # локальная копия на volume ничем не защищена от падения диска этой машины). - backup: - image: postgres:16-alpine - container_name: netrunner-backup - restart: always - entrypoint: ["/bin/sh", "/scripts/backup.sh"] - environment: - - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} - - BACKUP_DIR=/backups - - BACKUP_INTERVAL_SEC=${BACKUP_INTERVAL_SEC:-21600} - - BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-14} - - RCLONE_REMOTE=${RCLONE_REMOTE:-} - - RCLONE_CONFIG_CONTENT=${RCLONE_CONFIG_CONTENT:-} - volumes: - - ./scripts/backup.sh:/scripts/backup.sh:ro - - netrunner_db_backups:/backups - depends_on: - db: - condition: service_healthy - networks: - - netrunner_grid - # ========================================== # МОНИТОРИНГ # ========================================== @@ -161,13 +122,7 @@ services: networks: netrunner_grid: - # Явное имя (не дефолтный _netrunner_grid) — на этот же bridge - # снаружи подключается netrunner-data/docker-compose.yml (Vaultwarden) как - # external-сеть, ссылаясь по этому фиксированному имени. - name: netrunner_grid driver: bridge volumes: - netrunner_db_data: - netrunner_db_backups: grafana_data: diff --git a/rclone.conf.example b/rclone.conf.example deleted file mode 100644 index a29dbe6..0000000 --- a/rclone.conf.example +++ /dev/null @@ -1,16 +0,0 @@ -# Конфиг rclone для офсайт-копии бэкапов Postgres (см. scripts/backup.sh). -# -# 1. Скопировать этот файл в rclone.conf (рядом с docker-compose.prod.yml на VPS) -# и заполнить реальными данными удалённого хранилища (Backblaze B2, S3, -# другой VPS по SFTP — что угодно, что поддерживает rclone). -# 2. Прописать в .env: RCLONE_REMOTE=b2:netrunner-backups (имя секции ниже -# + путь/бакет), совпадающее с именем секции [remote-name] тут. -# 3. Пустой/незаполненный rclone.conf — не ошибка: офсайт-копия просто не -# выполнится (см. лог "не удалось скопировать"), локальный дамп всё равно -# создаётся каждый цикл. -# -# Пример для Backblaze B2: -# [b2] -# type = b2 -# account = YOUR_B2_ACCOUNT_ID -# key = YOUR_B2_APPLICATION_KEY diff --git a/scripts/backup.sh b/scripts/backup.sh deleted file mode 100755 index e2584c1..0000000 --- a/scripts/backup.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -# Периодический pg_dump с опциональной офсайт-копией. Запускается как отдельный -# долгоживущий контейнер (см. docker-compose.prod.yml/docker-compose.dev-remote.yml, -# сервис `backup`) — сам себе крон через бесконечный цикл + sleep, без внешнего -# cron-демона в образе postgres:16-alpine. -# -# Обязательные переменные окружения: DATABASE_URL, BACKUP_DIR (примонтированный volume). -# Опциональные: BACKUP_INTERVAL_SEC (по умолчанию 21600 = 6ч), BACKUP_RETENTION_DAYS -# (по умолчанию 14), RCLONE_REMOTE (например "b2:netrunner-backups" — если не задан, -# офсайт-копия просто пропускается, бэкапы остаются только локально на VPS) и -# RCLONE_CONFIG_CONTENT (содержимое rclone.conf целиком, одной строкой/переменной -# окружения — без bind-mount'а файла, чтобы не требовать его существования на диске -# при первом деплое). -set -eu - -BACKUP_DIR="${BACKUP_DIR:-/backups}" -INTERVAL="${BACKUP_INTERVAL_SEC:-21600}" -RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-14}" - -mkdir -p "$BACKUP_DIR" - -if [ -n "${RCLONE_REMOTE:-}" ] && ! command -v rclone >/dev/null 2>&1; then - echo "[backup] Устанавливаю rclone для офсайт-копий..." - apk add --no-cache rclone >/dev/null -fi - -if [ -n "${RCLONE_CONFIG_CONTENT:-}" ]; then - mkdir -p /root/.config/rclone - printf '%s\n' "$RCLONE_CONFIG_CONTENT" > /root/.config/rclone/rclone.conf -fi - -echo "[backup] Запущен. Интервал: ${INTERVAL}с, хранение: ${RETENTION_DAYS}д, офсайт: ${RCLONE_REMOTE:-<не настроен>}" - -while true; do - timestamp=$(date -u +%Y%m%dT%H%M%SZ) - dump_file="$BACKUP_DIR/netrunner_${timestamp}.sql.gz" - - echo "[backup] $(date -u +%FT%TZ) Снимаю дамп -> $dump_file" - if pg_dump "$DATABASE_URL" | gzip > "$dump_file.tmp"; then - mv "$dump_file.tmp" "$dump_file" - echo "[backup] Готово: $(du -h "$dump_file" | cut -f1)" - else - echo "[backup] ОШИБКА: pg_dump упал, файл не сохранён" >&2 - rm -f "$dump_file.tmp" - sleep "$INTERVAL" - continue - fi - - if [ -n "${RCLONE_REMOTE:-}" ]; then - if rclone copy "$dump_file" "$RCLONE_REMOTE" 2>&1; then - echo "[backup] Скопировано на $RCLONE_REMOTE" - else - echo "[backup] ОШИБКА: не удалось скопировать на $RCLONE_REMOTE (локальная копия сохранена)" >&2 - fi - fi - - # Локальная ротация — офсайт-копия (если настроена) хранится по правилам - # самого remote, это не отменяет отдельную политику хранения там. - find "$BACKUP_DIR" -name 'netrunner_*.sql.gz' -mtime "+${RETENTION_DAYS}" -delete - - sleep "$INTERVAL" -done