diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..351f1bb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,39 @@ +# Игнорируемые файлы для Docker сборки +# Позволяют уменьшить размер контекста сборки и не тащить мусор в контейнер + +# Git +.git +.gitignore + +# Python / Poetry +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env/ +venv/ +.venv/ +# poetry.lock - ВАЖНО: Мы НЕ игнорируем lock-файл! Он нужен для воспроизводимой сборки. + +# Django +*.log +local_settings.py +.env # Секреты не должны попадать в образ! +.env.local +db.sqlite3 # Не копируем локальную базу на этапе сборки, она должна быть в volume! +db.sqlite3-journal +database/ # Исключаем папку с базой из образа. В продакшене она монтируется как volume. + +# Static / Media +# public/static/ # Исходники статики нужны collectstatic +public/media # Медиа файлы НЕ нужны в образе, они монтируются как volume + +# IDE +.idea +.vscode +*.swp +*.swo + +# Mac OS и Synology +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9a528db --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# ========================================== +# Dockerfile для Django + Gunicorn + WhiteNoise +# ========================================== + +# 1. Базовый образ: Python 3.12 (Slim версия для меньшего размера) +FROM python:3.12-slim + +# 2. Переменные окружения для Python +# PYTHONDONTWRITEBYTECODE: Запрещает Python писать .pyc файлы +# PYTHONUNBUFFERED: Гарантирует, что вывод консоли (logs) виден сразу (не буферизуется) +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +# Poetry настройки: не создавать виртуальное окружение внутри контейнера (ставим системно). +# Дублирует `poetry config virtualenvs.create false` в пп.7 (на всякий случай). +ENV POETRY_VIRTUALENVS_CREATE=false +# Путь настройки Django (по умолчанию для production) на случай если контейнер будет запущен не через docker-compose. +ENV DJANGO_SETTINGS_MODULE=dicquo.settings + +# 3. Рабочая директория внутри контейнера +WORKDIR /app + +# 4. Установка системных зависимостей +# - libjpeg-dev zlib1g-dev: библиотеки для работы с изображениями (Pillow) +RUN apt-get update && apt-get install -y --no-install-recommends \ + libjpeg-dev \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +# 5. Установка Poetry через pip (быстро и надежно) +RUN pip install --no-cache-dir poetry + +# 6. Копируем файлы зависимостей (pyproject.toml и poetry.lock) +# Делаем это ДО копирования всего кода, чтобы использовать кэш Docker layers. +COPY pyproject.toml poetry.lock /app/ + +# 7. Установка зависимостей проекта +# --no-interaction: не будет спрашивать подтверждения +# --no-ansi: уберваем цветные символы из логов сборки (они иногда мусорят) +# --no-root: не устанавливать сам проект как пакет (мы просто копируем код) +# --only main: не ставить dev-зависимости (тесты, линтеры и т.п.) для продакшена +# RUN poetry install --no-root --only main +# Настройка Poetry: не создавать venv и установка зависимостей (без dev-зависимостей для продакшена) +RUN poetry config virtualenvs.create false \ + && poetry install --no-interaction --no-ansi --no-root --only main + +# 8. Копируем весь исходный код проекта в контейнер +COPY . /app/ + +# 9. Сборка статики (CSS, JS) +# Важно: Запускаем collectstatic с фейковым SECRET_KEY, так как на этапе сборки env файла может не быть. +RUN SECRET_KEY=dummy_build_key python dicquo/manage.py collectstatic --noinput --clear + +# 10. Открываем порт 8000 +EXPOSE 8000 + +# 11. Команда запуска +# Переходим в подпапку dicquo, где лежит код Django проекта +WORKDIR /app/dicquo + +# Запускаем Gunicorn (по умолчанию, если не переопределено в docker-compose) на три воркера, привязывая его к +# порту 8000 и указывая на точку входа приложения (wsgi.py). +CMD ["gunicorn", "--workers", "3", "--bind", "0.0.0.0:8000", "dicquo.wsgi:application"] + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..a3ddc41 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,114 @@ +# ============================================================================== +# Docker Compose для PRODUCTION +# Этот файл запускается на боевом сервере. +# Вариант 1 (если переименовали в docker-compose.yml): docker compose up -d +# Вариант 2 (если оставили имя): docker compose -f docker-compose.prod.yml up -d +# ============================================================================== + +version: '3.8' + +services: + # --- ОCНОВНОЙ СЕРВИС: DJANGO + GUNICORN + WHITENOISE --- + web: + # Имя контейнера + container_name: dq_app + + # 1. ОБРАЗ + # В продакшене мы используем готовый, собранный образ из реестра (Gitea) + # image: git.cube2.ru/e-serg/dicquo:latest + # Но пока, для первого деплоя или если реестра нет, можно собирать локально: + build: . + + restart: always + + # 2. Метки для Watchtower (авто-обновление) + labels: + - "com.centurylinklabs.watchtower.scope=dq-scope" + + # 3. КОМАНДА ЗАПУСКА (Замена entrypoint.sh) + # Выполняем цепочку команд внутри контейнера при запуске: + # 1. Миграции + # 2. Collectstatic + # 3. Создаем папку nginx в примонтированном томе конфигов (если нет) + # 4. Копирование конфига Nginx с авто-заменой путей через sed (замену реального пути на хосте получаем через переменную окружения HOST_PROJECT_PATH) + # 5. Инициализация боевого конфига (если нет) + # 6. Создаем папку для ошибок и копируем туда статические страницы 404/500 + # 7. Запуск Gunicorn + command: > + sh -c "python manage.py migrate --noinput && + python manage.py collectstatic --noinput && + mkdir -p /nginx_configs_host/nginx && + sed \"s|/home/user/app/dq-site|${HOST_PROJECT_PATH:-/home/default_user/projects/dq-site}|g\" /app/configs/nginx/dq-app--external-nginx.conf > /nginx_configs_host/nginx/nginx_dq.conf.example && + if [ ! -f /nginx_configs_host/nginx/dq-app--external-nginx.conf ]; then + cp /nginx_configs_host/nginx/nginx_dq.conf.example /nginx_configs_host/nginx/dq-app--external-nginx.conf; + echo 'INIT: Created new nginx config with correct paths'; + fi && + mkdir -p /app/public/media/errors && + cp /app/dicquo/templates/static_404.html /app/public/media/errors/404.html && + cp /app/dicquo/templates/static_500.html /app/public/media/errors/500.html && + gunicorn --workers 3 --bind 0.0.0.0:8000 dicquo.wsgi:application" + + # 4. Проброс портов (Внешний Nginx -> localhost:8000) + ports: + - "8000:8000" + + # 5. Тома (Volumes) + volumes: + # База данных + # Монтируем папку database с хоста в папку с базой внутри контейнера. + # Путь в контейнере: /app/database (так как Django ищет базу в BASE_DIR.parent/database) + - ./database:/app/database + + # Медиа (папка media должна быть рядом с docker-compose.yml) + - ./media:/app/public/media + + # Конфиги (Монтируем папку ./config с хоста в /nginx_configs_host внутри контейнера) + # Это нужно, чтобы скрипт запуска мог положить туда .example конфиг и прочитать боевой конфиг. + - ./config:/nginx_configs_host + + # 6. Переменные окружения + env_file: + - .env + environment: + - DJANGO_SETTINGS_MODULE=dicquo.settings + - PYTHONUNBUFFERED=1 + # Передаем переменную с путем на хосте внутрь контейнера, чтобы sed мог её использовать + - HOST_PROJECT_PATH=${HOST_PROJECT_PATH:-/home/default_user/projects/dq-site} + + # 7. Логирование (Ротация) + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # 8. Ресурсы + deploy: + resources: + limits: + cpus: '0.40' + memory: 256M + mem_limit: 256m + + # --- WATCHTOWER: АВТО-ОБНОВЛЕНИЕ ОБРАЗОВ --- + # Следит за реестром Gitea и обновляет контейнер web, если появился новый image + watchtower: + image: containrrr/watchtower + container_name: dq_watchtower + restart: always + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + # Токен/Логин для вашего приватного реестра (нужно добавить в .env!) + # REPO_USER и REPO_PASS должны быть в .env файле на сервере + - REPO_USER=${REPO_USER} + - REPO_PASS=${REPO_PASS} + - WATCHTOWER_SCOPE=dq-scope + - WATCHTOWER_CLEANUP=true # Удалять старые образы после обновления + - WATCHTOWER_POLL_INTERVAL=1800 # Проверять каждые 30 минут + command: --scope dq-scope + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a9a435f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +# ============================================================================== +# Docker Compose для РАЗРАБОТКИ (Local Development) +# Этот файл содержит настройки для локальной работы (live reload, debug). +# Запуск: docker compose up --build +# ============================================================================== + +services: + web: + # Имя контейнера для удобства + container_name: dq_app_dev + + # Сборка из текущей директории + build: . + + # Проброс портов (чтобы сайт был доступен на localhost:8000) + ports: + - "8000:8000" + + # 1. КОМАНДА ЗАПУСКА (Dev режим) + # Используем --reload для авто-перезагрузки при изменении кода. + # Уменьшаем число воркеров до 2 (экономия ресурсов dev-машины). + # Убираем collectstatic (в dev Django сам может отдавать статику или она нам не так важна сжатой) + # Но миграции оставляем, чтобы база была актуальной. + command: > + sh -c "python manage.py migrate --noinput && + gunicorn --workers 2 --bind 0.0.0.0:8000 --reload dicquo.wsgi:application" + + # 2. МОНТИРОВАНИЕ КОДА (Live Reload) + # Подключаем локальные папки внутрь контейнера, чтобы Gunicorn видел изменения без пересборки образа. + volumes: + # Монтируем основной код проекта. + # Так как web, templates и manage.py лежат внутри dicquo/, одного этого маунта достаточно. + - ./dicquo:/app/dicquo + + # Монтируем всю папку public (Static + Media) + # Это нужно, чтобы: + # 1. Изменения в CSS/JS (public/static) сразу были видны (Live Reload). + # 2. Загруженные картинки (public/media) сохранялись на диске. + - ./public:/app/public + + # Монтируем базу данных (чтобы данные сохранялись при пересоздании контейнера) + # Используем ту же папку database, что и на проде, для единообразия. + # ВАЖНО: Django ищет базу в BASE_DIR.parent / 'database/db.sqlite3' + # В контейнере BASE_DIR=/app/dicquo, значит путь к базе: /app/database/db.sqlite3 + - ./database:/app/database + + # 3. ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ + environment: + - DEBUG=True + - DJANGO_LOG_LEVEL=DEBUG + # В dev нам не нужно ограничивать буферизацию так строго, но не помешает. + + # 4. РЕСУРСЫ (Без лимитов) + # Удаляем секцию ограничений, чтобы локально использовать все доступные ресурсы хоста. + # deploy: + # resources: + # limits: + # cpus: ... + # memory: ... + # mem_limit: ... +