mod: двухэтапная сборка для более компактного образа

This commit is contained in:
2026-03-22 10:02:51 +03:00
parent 30b554fce4
commit 23b111ec22
3 changed files with 62 additions and 45 deletions

View File

@@ -45,3 +45,8 @@ jobs:
# Используем теги, сгенерированные шагом meta (v1.0.0 и latest) # Используем теги, сгенерированные шагом meta (v1.0.0 и latest)
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
# ДОБАВЛЕНО:
cache-from: type=gha
cache-to: type=gha,mode=max
# И это для медленного интернета:
timeout: 900 # 15 минут на всю сборку

View File

@@ -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 # Устанавливаем переменные окружения для Poetry
# Копируем файлы зависимостей
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
ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 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 WORKDIR /app
# Установка Poetry # Копируем только файлы зависимостей.
RUN pip install --no-cache-dir poetry # Docker кэширует этот слой. Если эти файлы не меняются, Docker не будет
# переустанавливать все зависимости при каждой сборке.
COPY poetry.lock pyproject.toml ./
# Копируем файлы зависимостей # Устанавливаем зависимости с помощью Poetry.
COPY pyproject.toml poetry.lock* /app/ # --no-root: не устанавливать сам проект (etpgrf-site) как пакет.
# --only main: устанавливать только основные зависимости (не dev).
RUN poetry install --no-interaction --no-ansi --no-root --only main
# Настройка Poetry: не создавать venv и установка зависимостей (без dev-зависимостей для продакшена) # Очищаем кеш Poetry, чтобы он не попал в финальный образ.
RUN poetry config virtualenvs.create false \ RUN poetry cache clear --all -n
&& poetry install --no-interaction --no-ansi --no-root --only main
# Создаем непривилегированного пользователя
# -----------------------------------------------------------------------------
# --- Этап 2: Финальный образ ---
# -----------------------------------------------------------------------------
# Начинаем с такого же чистого и легкого образа Python.
FROM python:3.13-slim
# Устанавливаем рабочую директорию
WORKDIR /app
# Создаем непривилегированного пользователя для запуска приложения
RUN useradd -m -r appuser 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 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 USER appuser
# Порт # Сообщаем Docker, что наше приложение будет работать на порту 8000.
# Это нужно для `docker-compose`.
EXPOSE 8000 EXPOSE 8000
# Команда запуска через Gunicorn # ENTRYPOINT и CMD не указываем, так как команда запуска
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--chdir", "/app/etpgrf_site", "etpgrf_site.wsgi"] # будет передана из docker-compose.prod.yml (docker-compose.yml).

View File

@@ -13,6 +13,8 @@ services:
etpgrf-backend: etpgrf-backend:
# Используем готовый образ из Gitea Registry # Используем готовый образ из Gitea Registry
image: git.cube2.ru/erjemin/2026-etpgrf-site:latest image: git.cube2.ru/erjemin/2026-etpgrf-site:latest
# Если нужно, собрать образ из локального Dockerfile, а не скачивать готовый
# build: .
# Перезапускать всегда (если упал или сервер перезагрузился) # Перезапускать всегда (если упал или сервер перезагрузился)
restart: always restart: always
# Метка для Watchtower, чтобы он обновлял только этот контейнер # Метка для Watchtower, чтобы он обновлял только этот контейнер
@@ -84,7 +86,6 @@ services:
# Внешний порт. Если у тебя на хосте уже есть Nginx (прокси), # Внешний порт. Если у тебя на хосте уже есть Nginx (прокси),
# то можно пробросить на 127.0.0.1:8000 или использовать внутреннюю сеть. # то можно пробросить на 127.0.0.1:8000 или использовать внутреннюю сеть.
# Но пока оставим так:
ports: ports:
- "127.0.0.1:8080:80" # Используем 8080, чтобы не конфликтовать с Portainer (8000) или основным Nginx (80) - "127.0.0.1:8080:80" # Используем 8080, чтобы не конфликтовать с Portainer (8000) или основным Nginx (80)