diff --git a/Makefile b/Makefile index 7ddd09b..6e39aa6 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,26 @@ help: @echo "Доступные команды:" + @echo "" + @echo "Development:" @echo " make dev-up - Запустить development окружение" @echo " make dev-down - Остановить development окружение" @echo " make dev-build - Пересобрать development контейнеры" @echo " make dev-logs - Показать логи development" + @echo "" + @echo "Production:" @echo " make prod-up - Запустить production окружение" @echo " make prod-down - Остановить production окружение" @echo " make prod-build - Пересобрать production контейнеры" @echo " make prod-logs - Показать логи production" + @echo "" + @echo "Celery (Production):" + @echo " make prod-worker-logs - Логи Celery worker" + @echo " make prod-beat-logs - Логи Celery beat" + @echo " make prod-celery-status - Статус Celery" + @echo " make prod-celery-test - Тест Celery подключения" + @echo "" + @echo "Django:" @echo " make shell - Открыть Django shell" @echo " make migrate - Выполнить миграции" @echo " make createsuperuser - Создать суперпользователя" @@ -97,3 +109,29 @@ status: prod-status: docker-compose -f docker-compose.prod.yaml ps + +# Celery команды для production +prod-worker-logs: + docker-compose -f docker-compose.prod.yaml logs -f worker + +prod-beat-logs: + docker-compose -f docker-compose.prod.yaml logs -f beat + +prod-celery-status: + docker-compose -f docker-compose.prod.yaml exec web uv run celery -A dbapp inspect active + +prod-celery-test: + docker-compose -f docker-compose.prod.yaml exec web uv run python test_celery.py + +prod-redis-test: + docker-compose -f docker-compose.prod.yaml exec web uv run python check_redis.py + +# Celery команды для development +celery-status: + cd dbapp && uv run celery -A dbapp inspect active + +celery-test: + cd dbapp && uv run python test_celery.py + +redis-test: + cd dbapp && uv run python check_redis.py diff --git a/dbapp/Dockerfile b/dbapp/Dockerfile index 1b4d6e9..f8d1fa9 100644 --- a/dbapp/Dockerfile +++ b/dbapp/Dockerfile @@ -44,8 +44,8 @@ COPY --from=builder /app /app ENV PYTHONUNBUFFERED=1 \ PATH="/usr/local/bin:$PATH" -# Делаем entrypoint.sh исполняемым -RUN chmod +x /app/entrypoint.sh +# Делаем entrypoint скрипты исполняемыми +RUN chmod +x /app/entrypoint.sh /app/entrypoint-celery.sh EXPOSE 8000 diff --git a/dbapp/check_redis.py b/dbapp/check_redis.py new file mode 100644 index 0000000..f772a72 --- /dev/null +++ b/dbapp/check_redis.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +""" +Скрипт для проверки подключения к Redis. +Запуск: python check_redis.py +""" +import os +import sys + +try: + import redis +except ImportError: + print("❌ Redis библиотека не установлена") + print("Установите: pip install redis") + sys.exit(1) + +def check_redis(): + """Проверка подключения к Redis""" + print("=" * 60) + print("ПРОВЕРКА REDIS") + print("=" * 60) + + # Получаем URL из переменных окружения + broker_url = os.getenv("CELERY_BROKER_URL", "redis://localhost:6379/0") + cache_url = os.getenv("REDIS_URL", "redis://localhost:6379/1") + + print(f"\n1. Broker URL: {broker_url}") + print(f"2. Cache URL: {cache_url}") + + # Проверка broker (database 0) + print("\n3. Проверка Celery Broker (db 0)...") + try: + r_broker = redis.from_url(broker_url) + r_broker.ping() + print(" ✓ Подключение успешно") + + # Проверка ключей + keys = r_broker.keys("*") + print(f" ✓ Ключей в базе: {len(keys)}") + + # Проверка очереди celery + queue_length = r_broker.llen("celery") + print(f" ✓ Задач в очереди 'celery': {queue_length}") + + except redis.ConnectionError as e: + print(f" ✗ Ошибка подключения: {e}") + return False + except Exception as e: + print(f" ✗ Ошибка: {e}") + return False + + # Проверка cache (database 1) + print("\n4. Проверка Django Cache (db 1)...") + try: + r_cache = redis.from_url(cache_url) + r_cache.ping() + print(" ✓ Подключение успешно") + + # Проверка ключей + keys = r_cache.keys("*") + print(f" ✓ Ключей в базе: {len(keys)}") + + except redis.ConnectionError as e: + print(f" ✗ Ошибка подключения: {e}") + return False + except Exception as e: + print(f" ✗ Ошибка: {e}") + return False + + # Тест записи/чтения + print("\n5. Тест записи/чтения...") + try: + test_key = "test:celery:connection" + test_value = "OK" + + r_broker.set(test_key, test_value, ex=10) # TTL 10 секунд + result = r_broker.get(test_key) + + if result and result.decode() == test_value: + print(f" ✓ Запись/чтение работает") + r_broker.delete(test_key) + else: + print(f" ✗ Ошибка: ожидалось '{test_value}', получено '{result}'") + return False + + except Exception as e: + print(f" ✗ Ошибка: {e}") + return False + + print("\n" + "=" * 60) + print("✓ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") + print("=" * 60) + return True + +if __name__ == "__main__": + success = check_redis() + sys.exit(0 if success else 1) diff --git a/dbapp/dbapp/settings/production.py b/dbapp/dbapp/settings/production.py index ee79320..efdec9e 100644 --- a/dbapp/dbapp/settings/production.py +++ b/dbapp/dbapp/settings/production.py @@ -160,5 +160,14 @@ LOGGING = { "level": "INFO", "propagate": False, }, + "celery.worker": { + "handlers": ["console", "celery_file"], + "level": "INFO", + "propagate": False, + }, }, } + +# Force Celery to log to stdout for Docker +CELERY_WORKER_REDIRECT_STDOUTS = True +CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO" diff --git a/dbapp/entrypoint-celery.sh b/dbapp/entrypoint-celery.sh new file mode 100644 index 0000000..240fffe --- /dev/null +++ b/dbapp/entrypoint-celery.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +echo "Starting Celery Worker..." + +# Ждем PostgreSQL +echo "Waiting for PostgreSQL..." +until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c '\q' 2>/dev/null; do + echo "PostgreSQL is unavailable - sleeping" + sleep 1 +done +echo "PostgreSQL started" + +# Ждем Redis +echo "Waiting for Redis..." +until redis-cli -h redis ping 2>/dev/null; do + echo "Redis is unavailable - sleeping" + sleep 1 +done +echo "Redis started" + +# Создаем директорию для логов +mkdir -p /app/logs + +# Запускаем команду (celery worker или beat) +exec "$@" diff --git a/dbapp/test_celery.py b/dbapp/test_celery.py new file mode 100644 index 0000000..fa6dc14 --- /dev/null +++ b/dbapp/test_celery.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +""" +Скрипт для тестирования Celery подключения и задач. +Запуск: python test_celery.py +""" +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dbapp.settings.production') +django.setup() + +from celery import current_app +from dbapp.celery import debug_task + +def test_celery_connection(): + """Проверка подключения к Celery""" + print("=" * 60) + print("ТЕСТ CELERY ПОДКЛЮЧЕНИЯ") + print("=" * 60) + + # Проверка конфигурации + print(f"\n1. Broker URL: {current_app.conf.broker_url}") + print(f"2. Result Backend: {current_app.conf.result_backend}") + + # Проверка подключения к брокеру + try: + inspect = current_app.control.inspect() + stats = inspect.stats() + + if stats: + print(f"\n3. ✓ Активные workers: {list(stats.keys())}") + for worker, info in stats.items(): + print(f" - {worker}: {info}") + else: + print("\n3. ✗ Нет активных workers!") + print(" Убедитесь, что Celery worker запущен:") + print(" docker-compose -f docker-compose.prod.yaml logs worker") + + except Exception as e: + print(f"\n3. ✗ Ошибка подключения к брокеру: {e}") + return False + + # Проверка зарегистрированных задач + registered_tasks = list(current_app.tasks.keys()) + print(f"\n4. Зарегистрированные задачи ({len(registered_tasks)}):") + for task in sorted(registered_tasks): + if not task.startswith('celery.'): + print(f" - {task}") + + # Тест простой задачи + print("\n5. Тестирование задачи...") + try: + result = debug_task.delay() + print(f" Task ID: {result.id}") + print(f" Waiting for result...") + output = result.get(timeout=10) + print(f" ✓ Результат: {output}") + except Exception as e: + print(f" ✗ Ошибка выполнения задачи: {e}") + return False + + print("\n" + "=" * 60) + print("✓ ВСЕ ТЕСТЫ ПРОЙДЕНЫ") + print("=" * 60) + return True + +if __name__ == "__main__": + test_celery_connection() diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index e6fe266..91ee352 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -21,14 +21,14 @@ services: image: https://registry.geraltserv.ru/geolocation:latest env_file: - .env.prod - #entrypoint: [] - command: ["uv", "run", "celery", "-A", "dbapp", "worker", "--loglevel=INFO"] + entrypoint: ["/app/entrypoint-celery.sh"] + command: ["uv", "run", "celery", "-A", "dbapp", "worker", "--loglevel=INFO", "--concurrency=2"] depends_on: - db - redis - - web volumes: - ./logs:/app/logs + restart: unless-stopped redis: image: redis:7-alpine