SaveTest — ДокументацияSaveTest — Документация Сайт
Руководство пользователя
Руководство администратора
Установка и разработка
Руководство пользователя
Руководство администратора
Установка и разработка
  • Установка и разработка

    • Установка и разработка
    • Быстрый старт
    • Установка
    • Обновление
    • Домен и SSL — Nginx (опционально)
    • Разработка плагинов парсеров
Сайт

Установка

Развёртывание выполняется через Docker Compose. Логика сценария: выбрать манифест, создать .env с критичными переменными, запустить стек и выполнить первый вход. Для быстрого ознакомительного запуска без настройки .env см. Быстрый старт.

Требования

  • Установлены Docker Engine и Docker Compose (Docker 20.10+, Compose 2.0+).
  • CPU: от 4 (рекомендуется 8); RAM: от 4 ГБ (рекомендуется 8 ГБ); диск: от ~10 ГБ.
  • Свободны порты 8080 (веб), 8001 (API), 3001 (Allure) — либо измените проброс в файле ниже.

Шаг 1. Выбор манифеста

  1. Создайте рабочую директорию и перейдите в неё:
mkdir savetest
cd savetest
  1. Выберите подходящий манифест и сохраните его в этой директории как docker-compose.yml.
ФайлКогда использовать
docker-compose.ymlПродакшен: образы из registry, плагины Python и Gherkin, подстановка ${VAR:-default}
docker-compose.production.no-plugins.ymlПродакшен без парсеров-плагинов (меньше ресурсов)

Продакшен (с плагинами)

Готовые образы, Allure, webhook-worker, python-parser-plugin и gherkin-parser-plugin. Backend получает PLUGIN_URLS по умолчанию.

docker-compose.yml
# Docker Compose конфигурация для продакшн окружения

services:
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-savetest_db}
      POSTGRES_USER: ${POSTGRES_USER:-savetest_user}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-savetest_password}
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-savetest_user} -d ${POSTGRES_DB:-savetest_db}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - savetest-network

  python-parser-plugin:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/python-parser-plugin:main
    restart: unless-stopped
    volumes:
      - git_repos:/app/git_repos:ro
    networks:
      - savetest-network
    environment:
      - PLUGIN_NAME=python-parser
      - LOG_LEVEL=${PLUGIN_LOG_LEVEL:-INFO}
    healthcheck:
      test: ["CMD-SHELL", "python -c 'import urllib.request, json; r=urllib.request.urlopen(\"http://localhost:8000/config\", timeout=5); data=json.loads(r.read()); assert data.get(\"language\"), \"no language in config\"'"]
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 30s

  gherkin-parser-plugin:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/gherkin-parser-plugin:main
    restart: unless-stopped
    volumes:
      - git_repos:/app/git_repos:ro
    networks:
      - savetest-network
    environment:
      - PLUGIN_NAME=gherkin-parser
      - LOG_LEVEL=${PLUGIN_LOG_LEVEL:-INFO}
    healthcheck:
      test: ["CMD-SHELL", "python -c 'import urllib.request, json; r=urllib.request.urlopen(\"http://localhost:8000/config\", timeout=5); data=json.loads(r.read()); assert data.get(\"language\"), \"no language in config\"'"]
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 30s

  allure-service:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/allure-service:main
    container_name: allure-service
    restart: unless-stopped
    ports:
      - "${ALLURE_SERVICE_PORT:-3001}:3001"
    volumes:
      - allure_storage:/allure-storage
    environment:
      - PORT=3001
      - STORAGE_PATH=/allure-storage
      - NODE_ENV=production
      - REDIS_URL=redis://redis:6379/0
      - BACKEND_URL=http://backend:8000
    depends_on:
      redis:
        condition: service_healthy
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  backend:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/backend:main
    restart: unless-stopped
    ports:
      - "${BACKEND_PORT:-8001}:8000"
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-savetest_user}:${POSTGRES_PASSWORD:-savetest_password}@postgres:5432/${POSTGRES_DB:-savetest_db}
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=${SECRET_KEY:-your-super-secret-key-change-in-production}
      - DEBUG=${DEBUG:-False}
      - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000,http://localhost:8080,http://127.0.0.1:8080}
      - UVICORN_WORKERS=${UVICORN_WORKERS:-4}
      - PLUGIN_URLS=${PLUGIN_URLS:-http://python-parser-plugin:8000,http://gherkin-parser-plugin:8000}
      - PLUGIN_TIMEOUT=${PLUGIN_TIMEOUT:-30}
      - LOG_FILE_BACKUP_COUNT=${LOG_FILE_BACKUP_COUNT:-30}
      - LOG_FLUSH_BATCH_SIZE=${LOG_FLUSH_BATCH_SIZE:-10}
      - ALLURE_SERVICE_URL=${ALLURE_SERVICE_URL:-http://allure-service:3001}
      - ALLURE_STORAGE_PATH=/allure-storage
    volumes:
      - git_repos:/app/git_repos
      - avatars_data:/app/avatars
      - project_avatars_data:/app/project_avatars
      - reports_data:/app/reports
      - result_attachments_data:/app/result_attachments
      - wiki_sites_data:/app/wiki_sites
      - logs_data:/app/logs
      - app_data:/app/app/__data__
      - allure_storage:/allure-storage
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      python-parser-plugin:
        condition: service_healthy
      gherkin-parser-plugin:
        condition: service_healthy
      allure-service:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - savetest-network

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  webhook-worker:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/webhook-worker:main
    container_name: webhook-worker
    restart: unless-stopped
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-savetest_user}:${POSTGRES_PASSWORD:-savetest_password}@postgres:5432/${POSTGRES_DB:-savetest_db}
      - REDIS_URL=redis://redis:6379/0
      - WORKER_TIMEOUT=${WORKER_TIMEOUT:-30}
      - WORKER_RETRY_COUNT=${WORKER_RETRY_COUNT:-2}
      - WORKER_RETRY_DELAY=${WORKER_RETRY_DELAY:-5}
      - LOG_LEVEL=${WORKER_LOG_LEVEL:-INFO}
      - LOGS_DIR=/app/logs
    volumes:
      - logs_data:/app/logs
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  frontend:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/frontend:main
    restart: unless-stopped
    ports:
      - "${FRONTEND_PORT:-8080}:80"
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - savetest-network
    stop_grace_period: 15s
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

volumes:
  git_repos:
    driver: local
  redis_data:
    driver: local
  avatars_data:
    driver: local
  project_avatars_data:
    driver: local
  reports_data:
    driver: local
  result_attachments_data:
    driver: local
  wiki_sites_data:
    driver: local
  logs_data:
    driver: local
  app_data:
    driver: local
  postgres_data:
  allure_storage:
    driver: local

networks:
  savetest-network:
    driver: bridge

Продакшен без плагинов

Те же основные сервисы, без контейнеров парсеров; PLUGIN_URLS в backend не задаётся.

docker-compose.production.no-plugins.yml
# Docker Compose конфигурация для продакшн окружения БЕЗ ПЛАГИНОВ

services:
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-savetest_db}
      POSTGRES_USER: ${POSTGRES_USER:-savetest_user}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-savetest_password}
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-savetest_user} -d ${POSTGRES_DB:-savetest_db}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - savetest-network

  allure-service:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/allure-service:main
    container_name: allure-service
    restart: unless-stopped
    ports:
      - "${ALLURE_SERVICE_PORT:-3001}:3001"
    volumes:
      - allure_storage:/allure-storage
    environment:
      - PORT=3001
      - STORAGE_PATH=/allure-storage
      - NODE_ENV=production
      - REDIS_URL=redis://redis:6379/0
      - BACKEND_URL=http://backend:8000
    depends_on:
      redis:
        condition: service_healthy
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  backend:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/backend:main
    restart: unless-stopped
    ports:
      - "${BACKEND_PORT:-8001}:8000"
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-savetest_user}:${POSTGRES_PASSWORD:-savetest_password}@postgres:5432/${POSTGRES_DB:-savetest_db}
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=${SECRET_KEY:-your-super-secret-key-change-in-production}
      - DEBUG=${DEBUG:-False}
      - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000,http://localhost:8080,http://127.0.0.1:8080}
      - UVICORN_WORKERS=${UVICORN_WORKERS:-4}
      # PLUGIN_URLS не указан - плагины не будут загружены
      - PLUGIN_TIMEOUT=${PLUGIN_TIMEOUT:-30}
      - LOG_FILE_BACKUP_COUNT=${LOG_FILE_BACKUP_COUNT:-30}
      - LOG_FLUSH_BATCH_SIZE=${LOG_FLUSH_BATCH_SIZE:-10}
      - ALLURE_SERVICE_URL=${ALLURE_SERVICE_URL:-http://allure-service:3001}
      - ALLURE_STORAGE_PATH=/allure-storage
    volumes:
      - git_repos:/app/git_repos
      - avatars_data:/app/avatars
      - project_avatars_data:/app/project_avatars
      - reports_data:/app/reports
      - result_attachments_data:/app/result_attachments
      - wiki_sites_data:/app/wiki_sites
      - logs_data:/app/logs
      - app_data:/app/app/__data__
      - allure_storage:/allure-storage
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      allure-service:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - savetest-network

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  webhook-worker:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/webhook-worker:main
    container_name: webhook-worker
    restart: unless-stopped
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-savetest_user}:${POSTGRES_PASSWORD:-savetest_password}@postgres:5432/${POSTGRES_DB:-savetest_db}
      - REDIS_URL=redis://redis:6379/0
      - WORKER_TIMEOUT=${WORKER_TIMEOUT:-30}
      - WORKER_RETRY_COUNT=${WORKER_RETRY_COUNT:-2}
      - WORKER_RETRY_DELAY=${WORKER_RETRY_DELAY:-5}
      - LOG_LEVEL=${WORKER_LOG_LEVEL:-INFO}
      - LOGS_DIR=/app/logs
    volumes:
      - logs_data:/app/logs
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - savetest-network
    healthcheck:
      test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  frontend:
    image: cr.yandex/crp8hln0vgikkl8djavt/save-test/frontend:main
    restart: unless-stopped
    ports:
      - "${FRONTEND_PORT:-8080}:80"
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - savetest-network
    stop_grace_period: 15s
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

volumes:
  git_repos:
    driver: local
  redis_data:
    driver: local
  avatars_data:
    driver: local
  project_avatars_data:
    driver: local
  reports_data:
    driver: local
  result_attachments_data:
    driver: local
  wiki_sites_data:
    driver: local
  logs_data:
    driver: local
  app_data:
    driver: local
  postgres_data:
  allure_storage:
    driver: local

networks:
  savetest-network:
    driver: bridge

Шаг 2. Создание .env (критичные переменные)

Создайте файл .env в каталоге с docker-compose.yml и задайте минимум критичных параметров:

POSTGRES_PASSWORD=your-secure-password
SECRET_KEY=your-super-secret-key

Минимум требований для продакшена:

  • POSTGRES_PASSWORD — надёжный пароль БД.
  • SECRET_KEY — уникальный секрет приложения (рекомендуется не короче ~32 символов).

При необходимости расширьте .env дополнительными параметрами из раздела Параметры окружения.

Шаг 3. Запуск и доступ

Запустите контейнеры:

docker compose up -d
docker compose ps

Интерфейс по умолчанию: http://localhost:8080 (порт задаётся пробросом у frontend, см. манифест).

Шаг 4. Первый вход

При первом входе создайте суперадминистратора:

Создание суперадминистратора при первом входе (светлая тема)Создание суперадминистратора при первом входе (тёмная тема)

Важно: Для внешнего доступа по домену и HTTPS используйте отдельный материал: Домен и SSL — Nginx (опционально).

Параметры окружения

PostgreSQL

ПеременнаяОписаниеТипПо умолчаниюОбязательноПримечания
POSTGRES_DBИмя базы данныхstringsavetest_dbНет
POSTGRES_USERПользователь БДstringsavetest_userНет
POSTGRES_PASSWORDПароль БДstringsavetest_passwordНетВ продакшене — надёжный пароль
POSTGRES_INITDB_ARGSАргументы инициализации БДstring"--encoding=UTF8 --locale=en_US.UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8"НетОбычно не меняют

Backend

ПеременнаяОписаниеТипПо умолчаниюОбязательноПримечания
DATABASE_URLURL PostgreSQLstringсм. манифестНетpostgresql://user:password@host:port/database
REDIS_URLURL Redisstringredis://redis:6379/0Нет
SECRET_KEYСекрет JWT и шифрованияstringсм. манифестДа в продеМинимум ~32 символа; python -c "import secrets; print(secrets.token_urlsafe(32))"
DEBUGРежим отладкиboolFalseНетВ проде — False
ALLOWED_ORIGINSCORSstringlocalhost* в манифестеНетURL через запятую или JSON-массив
UVICORN_WORKERSЧисло workersint4НетЗависит от нагрузки
PLUGIN_URLSURL плагинов через запятуюstringсм. productionНетИли PLUGIN_1_URL, PLUGIN_2_URL, …; метаданные — GET /config
PLUGIN_TIMEOUTТаймаут к плагинам (сек)int30Нет
LOG_FILE_BACKUP_COUNTХранение логов (дней)int30Нет
LOG_FLUSH_BATCH_SIZEЗаписей до flushint10Нет
ALLURE_SERVICE_URLURL Allurestringhttp://allure-service:3001Нет
ALLURE_STORAGE_PATHПуть в контейнере backendstring/allure-storageНетСогласовать с volume
APP_HOSTBind APIstring0.0.0.0Нет
APP_PORTПорт API в контейнереint8000Нет
ACCESS_TOKEN_EXPIRE_MINUTESЖизнь access-токена (мин)int10080Нет
REFRESH_TOKEN_EXPIRE_DAYSЖизнь refresh-токена (дни)int14Нет

Проброс портов (Compose)

ПеременнаяОписаниеПо умолчанию
FRONTEND_PORTПорт веб-интерфейса на хосте8080
BACKEND_PORTПорт API на хосте8001
ALLURE_SERVICE_PORTПорт Allure на хосте3001

Allure Service

ПеременнаяОписаниеПо умолчанию
PORTПорт в контейнере3001
STORAGE_PATHКаталог отчётов/allure-storage
NODE_ENVОкружение Node.jsproduction

Webhook Worker

ПеременнаяОписаниеПо умолчаниюОбязательно
DATABASE_URLPostgreSQL—Да
REDIS_URLRedisredis://redis:6379/0Нет
WORKER_TIMEOUTТаймаут задачи (сек)30Нет
WORKER_RETRY_COUNTПовторы2Нет
WORKER_RETRY_DELAYПауза (сек)5Нет
LOG_LEVEL / WORKER_LOG_LEVELУровень логовINFOНет
LOGS_DIRКаталог логов/app/logsНет

Контейнеры плагинов

ПеременнаяОписаниеПо умолчанию
PLUGIN_NAMEИмя в логахиз образа
PLUGIN_LOG_LEVELУровень логовINFO

Redis

Для образа redis:7-alpine дополнительные переменные в типовой схеме не задаются.

Назад
Быстрый старт
Далее
Обновление