Установка
Развёртывание выполняется через 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. Выбор манифеста
- Создайте рабочую директорию и перейдите в неё:
mkdir savetest
cd savetest
- Выберите подходящий манифест и сохраните его в этой директории как
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 конфигурация для продакшн окружения
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 конфигурация для продакшн окружения БЕЗ ПЛАГИНОВ
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 | Имя базы данных | string | savetest_db | Нет | |
POSTGRES_USER | Пользователь БД | string | savetest_user | Нет | |
POSTGRES_PASSWORD | Пароль БД | string | savetest_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_URL | URL PostgreSQL | string | см. манифест | Нет | postgresql://user:password@host:port/database |
REDIS_URL | URL Redis | string | redis://redis:6379/0 | Нет | |
SECRET_KEY | Секрет JWT и шифрования | string | см. манифест | Да в проде | Минимум ~32 символа; python -c "import secrets; print(secrets.token_urlsafe(32))" |
DEBUG | Режим отладки | bool | False | Нет | В проде — False |
ALLOWED_ORIGINS | CORS | string | localhost* в манифесте | Нет | URL через запятую или JSON-массив |
UVICORN_WORKERS | Число workers | int | 4 | Нет | Зависит от нагрузки |
PLUGIN_URLS | URL плагинов через запятую | string | см. production | Нет | Или PLUGIN_1_URL, PLUGIN_2_URL, …; метаданные — GET /config |
PLUGIN_TIMEOUT | Таймаут к плагинам (сек) | int | 30 | Нет | |
LOG_FILE_BACKUP_COUNT | Хранение логов (дней) | int | 30 | Нет | |
LOG_FLUSH_BATCH_SIZE | Записей до flush | int | 10 | Нет | |
ALLURE_SERVICE_URL | URL Allure | string | http://allure-service:3001 | Нет | |
ALLURE_STORAGE_PATH | Путь в контейнере backend | string | /allure-storage | Нет | Согласовать с volume |
APP_HOST | Bind API | string | 0.0.0.0 | Нет | |
APP_PORT | Порт API в контейнере | int | 8000 | Нет | |
ACCESS_TOKEN_EXPIRE_MINUTES | Жизнь access-токена (мин) | int | 10080 | Нет | |
REFRESH_TOKEN_EXPIRE_DAYS | Жизнь refresh-токена (дни) | int | 14 | Нет |
Проброс портов (Compose)
| Переменная | Описание | По умолчанию |
|---|---|---|
FRONTEND_PORT | Порт веб-интерфейса на хосте | 8080 |
BACKEND_PORT | Порт API на хосте | 8001 |
ALLURE_SERVICE_PORT | Порт Allure на хосте | 3001 |
Allure Service
| Переменная | Описание | По умолчанию |
|---|---|---|
PORT | Порт в контейнере | 3001 |
STORAGE_PATH | Каталог отчётов | /allure-storage |
NODE_ENV | Окружение Node.js | production |
Webhook Worker
| Переменная | Описание | По умолчанию | Обязательно |
|---|---|---|---|
DATABASE_URL | PostgreSQL | — | Да |
REDIS_URL | Redis | redis://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 дополнительные переменные в типовой схеме не задаются.