From 23b111ec22e8be0c6409e3130ddfb28dd88fc3dc Mon Sep 17 00:00:00 2001 From: erjemin Date: Sun, 22 Mar 2026 10:02:51 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20=D0=B4=D0=B2=D1=83=D1=85=D1=8D=D1=82?= =?UTF-8?q?=D0=B0=D0=BF=D0=BD=D0=B0=D1=8F=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=B0=D0=BA=D1=82=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/docker-publish.yaml | 5 ++ Dockerfile | 99 +++++++++++++++------------- docker-compose.prod.yml | 3 +- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/.gitea/workflows/docker-publish.yaml b/.gitea/workflows/docker-publish.yaml index 420db61..fee36cc 100644 --- a/.gitea/workflows/docker-publish.yaml +++ b/.gitea/workflows/docker-publish.yaml @@ -45,3 +45,8 @@ jobs: # Используем теги, сгенерированные шагом meta (v1.0.0 и latest) tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + # ДОБАВЛЕНО: + cache-from: type=gha + cache-to: type=gha,mode=max + # И это для медленного интернета: + timeout: 900 # 15 минут на всю сборку diff --git a/Dockerfile b/Dockerfile index 9e7779b..492a5ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,60 +1,71 @@ -# --- Stage 1: Сборка фронтенда (CodeMirror) --- -FROM node:20-slim as frontend-builder +# ----------------------------------------------------------------------------- +# --- Этап 1: Сборщик (Builder) --- +# ----------------------------------------------------------------------------- +# Используем официальный, но компактный образ Python как "строительную площадку". +# На этом этапе мы установим все зависимости, а потом скопируем только результат. +FROM python:3.13-slim as builder -WORKDIR /app/frontend - -# Копируем файлы зависимостей -COPY frontend-assembly/package.json frontend-assembly/package-lock.json ./ - -# Устанавливаем зависимости (включая devDependencies для сборки) -RUN npm ci - -# Копируем исходники -COPY frontend-assembly/ ./ - -# Собираем бандл через npm script -RUN npm run build - - -# --- Stage 2: Сборка бэкенда (Django) --- -FROM python:3.13-slim - -# Настройки Python +# Устанавливаем переменные окружения для Poetry ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 +# Эти настройки говорят Poetry создать виртуальное окружение прямо в папке проекта (/app/.venv) +ENV POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_IN_PROJECT=1 \ + POETRY_VIRTUALENVS_CREATE=1 \ + POETRY_CACHE_DIR=/tmp/poetry_cache +# Устанавливаем саму Poetry +RUN pip install poetry + +# Устанавливаем рабочую директорию внутри контейнера WORKDIR /app -# Установка Poetry -RUN pip install --no-cache-dir poetry +# Копируем только файлы зависимостей. +# Docker кэширует этот слой. Если эти файлы не меняются, Docker не будет +# переустанавливать все зависимости при каждой сборке. +COPY poetry.lock pyproject.toml ./ -# Копируем файлы зависимостей -COPY pyproject.toml poetry.lock* /app/ +# Устанавливаем зависимости с помощью Poetry. +# --no-root: не устанавливать сам проект (etpgrf-site) как пакет. +# --only main: устанавливать только основные зависимости (не dev). +RUN poetry install --no-interaction --no-ansi --no-root --only main -# Настройка Poetry: не создавать venv и установка зависимостей (без dev-зависимостей для продакшена) -RUN poetry config virtualenvs.create false \ - && poetry install --no-interaction --no-ansi --no-root --only main +# Очищаем кеш Poetry, чтобы он не попал в финальный образ. +RUN poetry cache clear --all -n -# Создаем непривилегированного пользователя + +# ----------------------------------------------------------------------------- +# --- Этап 2: Финальный образ --- +# ----------------------------------------------------------------------------- +# Начинаем с такого же чистого и легкого образа Python. +FROM python:3.13-slim + +# Устанавливаем рабочую директорию +WORKDIR /app + +# Создаем непривилегированного пользователя для запуска приложения RUN useradd -m -r appuser - -# Копируем код проекта -COPY . /app/ - -# Создаем папку для данных и статики, чтобы у appuser были права -RUN mkdir -p /app/data /app/public/static_collected - -# Копируем собранный фронтенд из первого стейджа -COPY --from=frontend-builder /app/frontend/dist/editor.js /app/public/static/codemirror/editor.js - -# Меняем владельца папки +# Устанавливаем владельца рабочей директории RUN chown -R appuser:appuser /app -# Переключаемся на пользователя +# Копируем готовое виртуальное окружение из сборщика +COPY --from=builder /app/.venv ./.venv + +# Устанавливаем PATH, чтобы использовать python из .venv +ENV PATH="/app/.venv/bin:$PATH" + +# Копируем весь код нашего приложения в рабочую директорию. +COPY . . + +# Устанавливаем владельца для скопированных файлов +RUN chown -R appuser:appuser /app + +# Переключаемся на непривилегированного пользователя USER appuser -# Порт +# Сообщаем Docker, что наше приложение будет работать на порту 8000. +# Это нужно для `docker-compose`. EXPOSE 8000 -# Команда запуска через Gunicorn -CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--chdir", "/app/etpgrf_site", "etpgrf_site.wsgi"] +# ENTRYPOINT и CMD не указываем, так как команда запуска +# будет передана из docker-compose.prod.yml (docker-compose.yml). diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 14f1459..52ec81c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -13,6 +13,8 @@ services: etpgrf-backend: # Используем готовый образ из Gitea Registry image: git.cube2.ru/erjemin/2026-etpgrf-site:latest + # Если нужно, собрать образ из локального Dockerfile, а не скачивать готовый + # build: . # Перезапускать всегда (если упал или сервер перезагрузился) restart: always # Метка для Watchtower, чтобы он обновлял только этот контейнер @@ -84,7 +86,6 @@ services: # Внешний порт. Если у тебя на хосте уже есть Nginx (прокси), # то можно пробросить на 127.0.0.1:8000 или использовать внутреннюю сеть. - # Но пока оставим так: ports: - "127.0.0.1:8080:80" # Используем 8080, чтобы не конфликтовать с Portainer (8000) или основным Nginx (80)