diff --git a/.env.example b/.env.example index 03ea2a8..5928542 100644 --- a/.env.example +++ b/.env.example @@ -15,18 +15,6 @@ ADMIN_URL=hidden-admin-panel/ # ======================================== DB_NAME=database/db.sqlite3 -# ======================================== -# Пути для файлов автоматически вычисляются в settings.py -# на основе PROJECT_ROOT (корень проекта) -# ======================================== -# STATIC_ROOT вычисляется как: PROJECT_ROOT / 'public' / 'static' -# MEDIA_ROOT вычисляется как: PROJECT_ROOT / 'public' / 'media' -# Настройки почты (опционально) -# ======================================== -# EMAIL_HOST=smtp.gmail.com -# EMAIL_PORT=587 -# EMAIL_HOST_USER=your-email@gmail.com -# EMAIL_HOST_PASSWORD=your-app-password -# EMAIL_USE_TLS=True -# EMAIL_FROM=noreply@example.com - +# Настройки достпа к пакетам в репозитории, чтобы wathtower мог проверять их свежесть и скачивать +REPO_USER=xxxxx +REPO_PASS=xxxxx diff --git a/Dockerfile b/Dockerfile index b64233e..9453d0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,14 +39,29 @@ ENV PATH="/opt/venv/bin:$PATH" # Устанавливаем рабочую директорию WORKDIR /app +# Устанавливаем пользователя для запуска приложения (из соображений безопасности) +RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app +USER appuser + # Копируем виртуальное окружение из builder COPY --from=builder /opt/venv /opt/venv # Копируем содержимое проекта -COPY . . +COPY --chown=appuser:appuser . . -# Создаём необходимые директории -RUN mkdir -p /app/public/media /app/public/static /app/database /app/staticfiles +# Создаём директорию для собранной статики и даём права пользователю app +RUN mkdir -p /app/staticfiles && chown -R appuser:appuser /app/staticfiles + +# Создаём директорию для media +RUN mkdir -p /app/public/media && chown -R appuser:appuser /app/public/media + +# Создаём директорию для БД и даём права пользователю app +# Это важно когда БД монтируется как том с хоста +RUN mkdir -p /app/database && chown -R appuser:appuser /app/database + +# Копируем внешний nginx конфиг для экспорта на хост (через volume) +# Это нужно для настройки reverse-proxy на хосте +COPY config/nginx/pet-clones--external-nginx.conf /tmp/pet-clones--external-nginx.conf.source # Собираем статику при сборке образа (для production) # Это гарантирует что статика готова и не нужно запускать collectstatic при старте @@ -68,21 +83,17 @@ RUN find /app/staticfiles/webfonts -type f \( -name "*.ttf" -o -name "*.eot" -o # bootstrap.css, all.css, fontawesome.css используем только .min версии RUN find /app/staticfiles/css -maxdepth 1 -name "*.css" -not -name "*.min.css" -not -name "rosmorport.css" -delete -# Устанавливаем пользователя для запуска приложения (из соображений безопасности) -RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app -USER appuser - # Открываем порт EXPOSE 8000 # CMD - выполняет миграции и запускает Gunicorn # Миграции выполняются при каждом запуске для гибкости # Статика уже собрана при сборке образа -# Параметры оптимизированы для dev с минимальной нагрузкой: +# Параметры оптимизированы для минимальной нагрузкой (ветер качает, но не падает): # - bind 0.0.0.0:8000 (все интерфейсы контейнера, необходимо для Docker проброса портов) -# - workers=1 (один worker - достаточно для dev без нагрузок) -# - timeout=30 (сокращенный таймаут - запросы должны быть быстрыми) -# - max-requests=100 (перезагрузка воркера после 100 запросов - экономия памяти) +# - workers=1 (один worker - достаточно если не будет нагрузок) +# - timeout=120 (сокращенный таймаут - запросы должны быть быстрыми) +# - max-requests=200 (перезагрузка воркера после 200 запросов - экономия памяти) # - access-logfile/error-logfile '-' (логи в stdout для docker) -CMD ["sh", "-c", "cd /app/rosmorport_tsts && echo '>>> Applying database migrations...' && python manage.py migrate --noinput && echo '>>> Starting Gunicorn...' && gunicorn --bind 0.0.0.0:8000 --workers 1 --worker-class sync --worker-tmp-dir /dev/shm --max-requests 100 --timeout 30 --access-logfile - --error-logfile - rosmorport_tsts.wsgi:application"] +CMD ["sh", "-c", "cd /app/rosmorport_tsts && echo '>>> Applying database migrations...' && python manage.py migrate --noinput && echo '>>> Starting Gunicorn...' && gunicorn --bind 0.0.0.0:8000 --workers 1 --worker-class sync --worker-tmp-dir /dev/shm --max-requests 200 --timeout 120 --access-logfile - --error-logfile - rosmorport_tsts.wsgi:application"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 0023de0..0dac6a6 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -12,118 +12,99 @@ services: dockerfile: Dockerfile # Имя контейнера - container_name: rosmorport_web_prod + container_name: petclones-site--backend # Переменные окружения для production environment: - DOCKER_ENV=1 - DEBUG=False - - ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0,web,pet-clones.cocorico.ru - - DB_ENGINE=django.db.backends.postgresql - - DB_HOST=db - - DB_PORT=5432 - - DB_NAME=${POSTGRES_DB:-rosmorport_db} - - DB_USER=${POSTGRES_USER:-postgres} - - DB_PASSWORD=${POSTGRES_PASSWORD:-postgres} - PYTHONUNBUFFERED=1 # Тома для медиа, статики и БД volumes: - - public_media:/app/public/media - - public_static:/app/public/static - - database:/app/database - + # Volume для media (загруженные пользователями файлы) + - media:/app/public/media:rw + # Volume для базы данных (SQLite файл) + - database:/app/database:rw + # Volume для экспорта конфигов из контейнера на хост + # Контейнер копирует nginx конфиг при запуске для настройки reverse-proxy на хосте + - ./config/nginx:/tmp/nginx_configs:rw # контейнер пишет сюда конфиги + + # Command: скрипт для копирования конфигов из контейнера на хост при запуске + # Встроенный entrypoint nginx:alpine (/docker-entrypoint.sh) запустит это как команду + command: > + sh -c " + echo '📋 Копирование внешнего nginx конфига на хост...' && + cp /tmp/pet-clones--external-nginx.conf.source /tmp/nginx_configs/pet-clones--external-nginx.conf.sample && + echo '✅ Пример nginx-конфига создан в ./config/nginx/pet-clones--external-nginx.conf.sample (свежий из контейнера)' && + if [ ! -f /tmp/nginx_configs/pet-clones--external-nginx.conf ]; then + cp /tmp/pet-clones--external-nginx.conf.source /tmp/nginx_configs/pet-clones--external-nginx.conf && + echo '✅ Боевой nginx-конфиг создан в ./config/nginx/pet-clones--external-nginx.conf (свежий из контейнера)' + else + echo '⏭️ Боевой nginx-конфиг оставлен без изменений.' + fi + gunicorn --bind 0.0.0.0:8000 --workers 1 --worker-class sync --worker-tmp-dir /dev/shm --max-requests 200 --timeout 120 --access-logfile - --error-logfile - rosmorport_tsts.wsgi:application + " + # Перенаправляем порты ports: - - "8000:8000" - - # Зависимость от базы данных - depends_on: - db: - condition: service_healthy - - # Политика перезапуска - restart: unless-stopped - - # Ограничения ресурсов - deploy: - resources: - limits: - cpus: '1' - memory: 512M - reservations: - cpus: '0.5' - memory: 256M - - # PostgreSQL база данных для production - db: - # Используем официальный образ PostgreSQL - image: postgres:16-alpine - container_name: rosmorport_db_prod - - # Переменные окружения - environment: - - POSTGRES_DB=${POSTGRES_DB:-rosmorport_db} - - POSTGRES_USER=${POSTGRES_USER:-postgres} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} - - # Том для хранения данных БД - volumes: - - postgres_data:/var/lib/postgresql/data - - # Портов не открываем - доступ только изнутри контейнеров - # ports: - # - "5432:5432" - - # Проверка здоровья сервиса - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] - interval: 10s - timeout: 5s - retries: 5 - - # Политика перезапуска - restart: unless-stopped - - # Ограничения ресурсов - deploy: - resources: - limits: - cpus: '1' - memory: 1G - reservations: - cpus: '0.5' - memory: 512M - - # Nginx для reverse proxy (опционально) - # nginx: - # image: nginx:alpine - # container_name: rosmorport_nginx - # volumes: - # - ./config/nginx.conf:/etc/nginx/nginx.conf:ro - # - django_staticfiles:/app/static:ro - # - django_mediafiles:/app/media:ro - # ports: - # - "80:80" - # - "443:443" - # depends_on: - # - web - # restart: unless-stopped + - "127.0.0.1:8040:8000" -# Именованные тома для хранения данных -volumes: - postgres_data: - driver: local - public_media: - driver: local - public_static: - driver: local - database: - driver: local + # Политика перезапуска + restart: unless-stopped + + # Метки для Watchtower (авто-обновление) + labels: + - "com.centurylinklabs.watchtower.scope=petclones-site--scope" + + # Ограничения ресурсов + deploy: + resources: + limits: + cpus: '0.25' + memory: 512M + + # Логирование в JSON-файлы (для сбора логов через docker logs) + logging: + driver: "json-file" + options: + max-size: "5m" # больше лимит в продакшене для логирования + max-file: "1" # храним 1 файл лога + + networks: + - petclones-site--network + + # WATCHTOWER ДЛЯ АВТОМАТИЧЕСКОГО ОБНОВЛЕНИЯ ОБРАЗОВ ИЗ РЕЕСТРА + watchtower: + image: containrrr/watchtower + container_name: petclones-site--watchtower + restart: always + volumes: + - /var/run/docker.sock:/var/run/docker.sock + # Переменные окружения + env_file: + - .env + environment: + - DOCKER_API_VERSION=1.44 + # Берем учетные данные из .env файла + - REPO_USER=${REPO_USER} + - REPO_PASS=${REPO_PASS} + # Ограничиваем область видимости только этим проектом + - WATCHTOWER_SCOPE=petclones-site--scope + # Если нужно указать реестр явно (обычно watchtower сам понимает из имени образа) + # - WATCHTOWER_REGISTRY_URL=git.cube2.ru + command: --interval 1800 --cleanup # Проверять каждые 30 минут + logging: + driver: "json-file" + options: + max-size: "1m" + max-file: "1" + networks: + - petclones-site--network # Сеть для сервисов networks: default: - name: rosmorport_network + name: petclones-site--network driver: bridge