diff --git a/DOCKER_COMMANDS.md b/DOCKER_COMMANDS.md new file mode 100644 index 0000000..33f0dcc --- /dev/null +++ b/DOCKER_COMMANDS.md @@ -0,0 +1,309 @@ +# Docker Commands and Usage + +Этот документ содержит все доступные команды для работы с Docker в проекте Queue Management System. + +## Быстрый старт + +### Разработка +```bash +# Использование Makefile (рекомендуется) +make up + +# Или напрямую через docker-compose +docker-compose up -d db redis +docker-compose up backend +``` + +### Продакшн +```bash +make prod +# или +docker-compose --profile production up -d +``` + +## Доступные команды через docker-entrypoint.sh + +### Основные команды + +#### Запуск сервера разработки +```bash +docker-compose run backend dev +# или +make up +``` + +#### Запуск продакшн сервера +```bash +docker-compose run backend prod +# или +make prod +``` + +#### Миграции базы данных +```bash +# Применить миграции +docker-compose run backend migrate +# или +make migrate + +# Создать новую миграцию +docker-compose run backend migrate-create "add user table" +# или +make migrate-create MSG="add user table" +``` + +#### Python shell с загруженными моделями +```bash +docker-compose run backend shell +# или +make shell +``` + +#### Bash shell в контейнере +```bash +docker-compose run backend bash +# или +make bash +``` + +### Тестирование и качество кода + +#### Запуск тестов +```bash +docker-compose --profile test run backend-test +# или +make test + +# Запуск конкретных тестов +docker-compose run backend test tests/test_auth.py +``` + +#### Линтинг кода +```bash +docker-compose run backend lint +# или +make lint +``` + +#### Форматирование кода +```bash +# Проверка форматирования +docker-compose run backend format +# или +make format + +# Исправление форматирования +docker-compose run backend format-fix +# или +make format-fix +``` + +### Утилиты базы данных + +#### Заполнение тестовыми данными +```bash +docker-compose run backend seed +# или +make seed +``` + +#### Сброс базы данных +```bash +docker-compose run backend reset-db +# или +make reset-db +``` + +#### Установка зависимостей +```bash +docker-compose run backend install-deps +``` + +## Профили Docker Compose + +### Базовый профиль (по умолчанию) +Включает сервисы: `db`, `redis`, `backend` +```bash +docker-compose up +``` + +### Профиль production +Включает сервисы: `db`, `redis`, `backend-prod`, `nginx` +```bash +docker-compose --profile production up +``` + +### Профиль test +Включает сервисы для тестирования: `db`, `redis`, `backend-test` +```bash +docker-compose --profile test up +``` + +### Профиль tools +Включает утилиты: `migrator` +```bash +docker-compose --profile tools up +``` + +## Примеры использования + +### Первый запуск проекта +```bash +# 1. Сборка образов +make build + +# 2. Запуск инфраструктуры +make up + +# 3. Применение миграций (если необходимо) +make migrate + +# 4. Заполнение тестовыми данными +make seed +``` + +### Ежедневная разработка +```bash +# Запуск для разработки +make up + +# Просмотр логов +make logs + +# Остановка +make down +``` + +### Создание и применение миграций +```bash +# Создать новую миграцию +make migrate-create MSG="add new field to user table" + +# Применить миграции +make migrate + +# Если нужно сбросить БД +make reset-db +``` + +### Тестирование +```bash +# Запуск всех тестов +make test + +# Запуск конкретного теста +docker-compose run backend test tests/test_specific.py::test_function + +# Линтинг и форматирование +make lint +make format-fix +``` + +### Продакшн деплой +```bash +# Сборка продакшн образов +docker-compose --profile production build + +# Запуск в продакшн режиме +make prod + +# Просмотр статуса сервисов +make status + +# Остановка продакшн окружения +make prod-down +``` + +### Отладка и диагностика +```bash +# Интерактивный Python shell +make shell + +# Bash в контейнере +make bash + +# Просмотр логов конкретного сервиса +make logs-backend +make logs-db +make logs-redis + +# Проверка статуса сервисов +make status +``` + +### Очистка +```bash +# Остановка всех сервисов +make down + +# Полная очистка (включая volumes) +make clean + +# Удаление Docker образов +make clean-images +``` + +## Переменные окружения + +### Backend +- `DATABASE_URL` - URL подключения к PostgreSQL +- `REDIS_URL` - URL подключения к Redis +- `ENVIRONMENT` - Режим работы (development/production/test) + +### Database +- `POSTGRES_DB` - Имя базы данных +- `POSTGRES_USER` - Пользователь PostgreSQL +- `POSTGRES_PASSWORD` - Пароль PostgreSQL + +## Порты + +- **8000** - Backend (development) +- **8001** - Backend (production) +- **5432** - PostgreSQL +- **6379** - Redis +- **80** - Nginx (production) +- **443** - Nginx HTTPS (production) + +## Volumes + +- `postgres_data` - Данные PostgreSQL +- `./backend:/app` - Код приложения (для разработки) + +## Сети + +- `queue_network` - Внутренняя сеть Docker для всех сервисов + +## Health Checks + +Все сервисы имеют health checks для корректного порядка запуска: +- PostgreSQL: `pg_isready` +- Redis: `redis-cli ping` + +## Troubleshooting + +### Проблемы с подключением к БД +```bash +# Проверить статус сервисов +make status + +# Посмотреть логи БД +make logs-db + +# Перезапустить сервисы +make restart +``` + +### Проблемы с миграциями +```bash +# Сбросить БД и применить миграции заново +make reset-db +``` + +### Проблемы с зависимостями +```bash +# Пересобрать образы +make build + +# Переустановить зависимости +docker-compose run backend install-deps +``` \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..56011a2 --- /dev/null +++ b/Makefile @@ -0,0 +1,124 @@ +.PHONY: help build up down logs shell migrate test lint format clean + +# Default target +help: + @echo "Available commands:" + @echo "" + @echo "Development:" + @echo " make up - Start development environment" + @echo " make down - Stop all services" + @echo " make build - Build Docker images" + @echo " make logs - View logs" + @echo " make restart - Restart all services" + @echo "" + @echo "Backend operations:" + @echo " make shell - Start Python shell in backend container" + @echo " make bash - Start bash shell in backend container" + @echo " make migrate - Run database migrations" + @echo " make migrate-create - Create new migration (requires MSG='migration name')" + @echo " make seed - Seed database with initial data" + @echo " make reset-db - Reset database" + @echo "" + @echo "Testing and Quality:" + @echo " make test - Run tests" + @echo " make lint - Run code linting" + @echo " make format - Check code formatting" + @echo " make format-fix - Fix code formatting" + @echo "" + @echo "Production:" + @echo " make prod - Start production environment" + @echo " make prod-down - Stop production environment" + @echo "" + @echo "Cleanup:" + @echo " make clean - Remove all containers and volumes" + @echo " make clean-images - Remove Docker images" + +# Development commands +build: + docker-compose build + +up: + docker-compose up -d db redis + @echo "Waiting for database and redis to be ready..." + @sleep 5 + docker-compose up backend + +down: + docker-compose down + +restart: down up + +logs: + docker-compose logs -f + +# Backend operations +shell: + docker-compose run --rm backend shell + +bash: + docker-compose run --rm backend bash + +migrate: + docker-compose run --rm backend migrate + +migrate-create: + @if [ -z "$(MSG)" ]; then \ + echo "Usage: make migrate-create MSG='migration description'"; \ + exit 1; \ + fi + docker-compose run --rm backend migrate-create "$(MSG)" + +seed: + docker-compose run --rm backend seed + +reset-db: + docker-compose run --rm backend reset-db + +# Testing and quality +test: + docker-compose --profile test run --rm backend-test + +lint: + docker-compose run --rm backend lint + +format: + docker-compose run --rm backend format + +format-fix: + docker-compose run --rm backend format-fix + +# Production +prod: + docker-compose --profile production up -d db redis + @echo "Waiting for database and redis to be ready..." + @sleep 5 + docker-compose --profile production up -d backend-prod nginx + +prod-down: + docker-compose --profile production down + +# Cleanup +clean: + docker-compose down -v --remove-orphans + docker system prune -f + +clean-images: + docker-compose down --rmi all + +# Install development dependencies locally (optional) +install: + pip install -r backend/requirements.txt + +# View service status +status: + docker-compose ps + +# Follow logs for specific service +logs-backend: + docker-compose logs -f backend + +logs-db: + docker-compose logs -f db + +logs-redis: + docker-compose logs -f redis \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index d89794b..1e55854 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -14,8 +14,15 @@ RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . +# Copy and make entrypoint script executable +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + # Expose port EXPOSE 8000 -# Command to run the application -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +# Set entrypoint +ENTRYPOINT ["docker-entrypoint.sh"] + +# Default command +CMD ["dev"] \ No newline at end of file diff --git a/backend/docker-entrypoint.sh b/backend/docker-entrypoint.sh new file mode 100755 index 0000000..853a8a0 --- /dev/null +++ b/backend/docker-entrypoint.sh @@ -0,0 +1,184 @@ +#!/bin/bash +set -e + +# Функция для ожидания доступности базы данных +wait_for_db() { + echo "Waiting for database to be ready..." + while ! python -c " +import asyncpg +import asyncio +import os + +async def check_db(): + try: + conn = await asyncpg.connect(os.getenv('DATABASE_URL')) + await conn.close() + print('Database is ready!') + return True + except Exception as e: + print(f'Database not ready: {e}') + return False + +if not asyncio.run(check_db()): + exit(1) +" 2>/dev/null; do + echo "Database is unavailable - sleeping" + sleep 2 + done +} + +# Функция для ожидания доступности Redis +wait_for_redis() { + echo "Waiting for Redis to be ready..." + while ! python -c " +import redis +import os + +try: + r = redis.Redis.from_url(os.getenv('REDIS_URL')) + r.ping() + print('Redis is ready!') +except Exception as e: + print(f'Redis not ready: {e}') + exit(1) +" 2>/dev/null; do + echo "Redis is unavailable - sleeping" + sleep 2 + done +} + +# Основная логика выбора команды +case "$1" in + "dev") + echo "Starting development server..." + wait_for_db + wait_for_redis + # Применяем миграции + alembic upgrade head + # Запускаем сервер в режиме разработки с автоперезагрузкой + exec uvicorn main:app --host 0.0.0.0 --port 8000 --reload + ;; + "prod") + echo "Starting production server..." + wait_for_db + wait_for_redis + # Применяем миграции + alembic upgrade head + # Запускаем сервер в продакшн режиме + exec uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 + ;; + "migrate") + echo "Running database migrations..." + wait_for_db + exec alembic upgrade head + ;; + "migrate-create") + echo "Creating new migration..." + if [ -z "$2" ]; then + echo "Usage: docker-compose run backend migrate-create " + exit 1 + fi + wait_for_db + exec alembic revision --autogenerate -m "$2" + ;; + "shell") + echo "Starting Python shell..." + exec python -i -c " +import asyncio +from sqlalchemy import create_engine +from app.core.database import SessionLocal +from app.models import * +print('Database models imported. Use SessionLocal() for database session.') +" + ;; + "test") + echo "Running tests..." + wait_for_db + wait_for_redis + # Устанавливаем тестовую базу данных + export DATABASE_URL="${DATABASE_URL}_test" + # Применяем миграции для тестовой БД + alembic upgrade head + # Запускаем тесты + exec python -m pytest "${@:2}" + ;; + "lint") + echo "Running code linting..." + exec python -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + ;; + "format") + echo "Formatting code..." + exec python -m black . --check + ;; + "format-fix") + echo "Fixing code formatting..." + exec python -m black . + ;; + "seed") + echo "Seeding database with initial data..." + wait_for_db + wait_for_redis + # Применяем миграции + alembic upgrade head + # Запускаем скрипт для наполнения БД данными + exec python -c " +import asyncio +from app.core.database import SessionLocal +from app.services.seed_data import seed_database + +async def main(): + db = SessionLocal() + try: + await seed_database(db) + print('Database seeded successfully!') + finally: + await db.close() + +asyncio.run(main()) +" + ;; + "reset-db") + echo "Resetting database..." + wait_for_db + # Сбрасываем все миграции + alembic downgrade base + # Применяем заново + alembic upgrade head + echo "Database reset completed!" + ;; + "install-deps") + echo "Installing dependencies..." + exec pip install -r requirements.txt + ;; + "bash") + echo "Starting bash shell..." + exec /bin/bash + ;; + *) + echo "Available commands:" + echo " dev - Start development server with auto-reload" + echo " prod - Start production server" + echo " migrate - Run database migrations" + echo " migrate-create - Create new migration (requires migration name)" + echo " shell - Start Python shell with models loaded" + echo " test - Run tests" + echo " lint - Run code linting" + echo " format - Check code formatting" + echo " format-fix - Fix code formatting" + echo " seed - Seed database with initial data" + echo " reset-db - Reset database (drop and recreate)" + echo " install-deps - Install Python dependencies" + echo " bash - Start bash shell" + echo "" + echo "Usage: docker-compose run backend " + echo "Example: docker-compose run backend dev" + + if [ $# -eq 0 ]; then + echo "No command provided, starting development server by default..." + exec "$0" dev + else + echo "Unknown command: $1" + exit 1 + fi + ;; +esac \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index 52b82c4..9c14b14 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -17,4 +17,10 @@ google-auth==2.24.0 google-auth-oauthlib==1.1.0 google-auth-httplib2==0.1.1 authlib==1.2.1 -itsdangerous==2.1.2 \ No newline at end of file +itsdangerous==2.1.2 + +# Development and testing dependencies +pytest==7.4.3 +pytest-asyncio==0.21.1 +black==23.11.0 +flake8==6.1.0 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e712704..c0f417b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,11 @@ services: - postgres_data:/var/lib/postgresql/data networks: - queue_network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 redis: image: redis:7-alpine @@ -22,6 +27,11 @@ services: - "6379:6379" networks: - queue_network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 backend: build: @@ -35,13 +45,98 @@ services: ports: - "8000:8000" depends_on: - - db - - redis + db: + condition: service_healthy + redis: + condition: service_healthy volumes: - ./backend:/app networks: - queue_network - command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload + # Команда по умолчанию - dev сервер + command: ["dev"] + + # Отдельный сервис для продакшн режима + backend-prod: + build: + context: ./backend + dockerfile: Dockerfile + container_name: queue_app_backend_prod + environment: + - DATABASE_URL=postgresql://postgres:postgres@db:5432/queue_app + - REDIS_URL=redis://redis:6379 + - ENVIRONMENT=production + ports: + - "8001:8000" + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - ./backend:/app + networks: + - queue_network + command: ["prod"] + profiles: + - production + + # Сервис для миграций + migrator: + build: + context: ./backend + dockerfile: Dockerfile + environment: + - DATABASE_URL=postgresql://postgres:postgres@db:5432/queue_app + - REDIS_URL=redis://redis:6379 + depends_on: + db: + condition: service_healthy + volumes: + - ./backend:/app + networks: + - queue_network + command: ["migrate"] + profiles: + - tools + + # Сервис для тестов + backend-test: + build: + context: ./backend + dockerfile: Dockerfile + environment: + - DATABASE_URL=postgresql://postgres:postgres@db:5432/queue_app + - REDIS_URL=redis://redis:6379 + - ENVIRONMENT=test + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - ./backend:/app + networks: + - queue_network + command: ["test"] + profiles: + - test + + # Nginx для продакшн (опционально) + nginx: + image: nginx:alpine + container_name: queue_app_nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - backend-prod + networks: + - queue_network + profiles: + - production volumes: postgres_data: diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..5f6bc38 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,76 @@ +events { + worker_connections 1024; +} + +http { + upstream backend { + server backend-prod:8000; + } + + server { + listen 80; + server_name localhost; + + # Увеличиваем размер буфера для больших запросов + client_max_body_size 50M; + + # Основной API + location /api/ { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket поддержка + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Документация API + location /docs { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # OpenAPI схема + location /openapi.json { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Статические файлы (если есть) + location /static/ { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Проверка здоровья + location /health { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Корневой маршрут + location / { + proxy_pass http://backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file