Зачем нужно указывать несколько операций from в одном Dockerfile?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Несколько операций FROM в Dockerfile (Multi-stage builds)
Много операций FROM в одном Dockerfile — это техника multi-stage build (многоэтапная сборка). Это позволяет оптимизировать размер финального образа и улучшить безопасность.
Проблема: обычный Dockerfile
Обычный Dockerfile содержит одну операцию FROM и создает один образ со всеми файлами и зависимостями:
FROM python:3.11-slim
WORKDIR /app
# Копируем requirements и устанавливаем зависимости
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копируем исходный код
COPY . .
# Запускаем приложение
CMD ["python", "app.py"]
Проблемы:
- Финальный образ содержит все промежуточные файлы
- pip кэш, временные файлы, инструменты сборки — все в образе
- Образ становится большим (500+ МБ вместо 50 МБ)
- Медленнее загружать и развертывать
- Уязвимость — все инструменты и библиотеки в продакшене
Решение: Multi-stage build
Много операций FROM позволяет использовать несколько промежуточных этапов (stage) и копировать только нужное в финальный образ:
# Этап 1: Сборка (builder)
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Компилируем Python в bytecode
RUN python -m py_compile app.py
# Этап 2: Продакшн (runtime)
FROM python:3.11-slim-distroless
WORKDIR /app
# Копируем ТОЛЬКО нужные файлы из builder-а
COPY --from=builder /app .
CMD ["python", "app.py"]
В этом примере:
- Stage 1 (builder): содержит pip, requirements.txt, исходные файлы
- Stage 2 (runtime): содержит только скомпилированные файлы и Python
Финальный образ гораздо меньше, так как не содержит pip и других инструментов сборки.
Практический пример: Python приложение
Плохой вариант (один FROM):
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt # pip остается в образе
COPY . .
CMD ["python", "app.py"]
# docker build . -t myapp
# Размер образа: ~900 МБ (python + pip + все библиотеки)
Хороший вариант (multi-stage):
# Этап 1: Сборка виртуального окружения
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
# Устанавливаем в /opt/venv
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Этап 2: Минимальный runtime образ
FROM python:3.11-slim
WORKDIR /app
# Копируем виртуальное окружение из builder
COPY --from=builder /opt/venv /opt/venv
# Копируем только исходный код
COPY --from=builder /app/app.py .
ENV PATH="/opt/venv/bin:$PATH"
CMD ["python", "app.py"]
# docker build . -t myapp
# Размер образа: ~300 МБ (50% от обычного)
Еще лучший вариант: distroless образ
# Этап 1: Сборка
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Этап 2: Минимальный runtime (без shell, без пакетного менеджера)
FROM python:3.11-slim-distroless
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app/app.py .
CMD ["python", "app.py"]
# docker build . -t myapp
# Размер образа: ~150 МБ (slim distroless очень легкий)
Преимущества distroless:
- Нет shell (sh, bash)
- Нет пакетного менеджера (apt, yum)
- Нет лишних утилит
- Намного меньше уязвимостей
Пример: Node.js приложение
# Этап 1: Сборка
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY src ./src
RUN npm run build # Компилируем TypeScript или minify JavaScript
# Этап 2: Runtime
FROM node:18-alpine
WORKDIR /app
# Копируем compiled приложение
COPY --from=builder /app/dist ./dist
# Копируем node_modules
COPY --from=builder /app/node_modules ./node_modules
COPY package.json .
EXPOSE 3000
CMD ["node", "dist/index.js"]
# Builder (с компилятором): 500+ МБ
# Runtime (с compiled кодом): 150 МБ
Пример: Java приложение
# Этап 1: Компиляция
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# Этап 2: Runtime
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# Копируем только JAR файл
COPY --from=builder /app/target/app.jar .
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
# Builder (с Maven + JDK): 800+ МБ
# Runtime (с JRE): 200+ МБ
Преимущества multi-stage build
-
Меньше размер образа
- Без инструментов сборки (pip, npm, maven, gcc)
- Без исходного кода
- До 80% экономии место
-
Быстрее развертывание
- Меньший образ загружается быстрее
- Меньше времени на push/pull
-
Безопаснее
- Нет инструментов для взлома в продакшене
- Меньше уязвимостей
- Меньше зависимостей для сканирования
-
Быстрее сборка
- Docker кэширует этапы
- Если код не изменился, builder этап пересчитывается из кэша
Пример с именованными этапами
# Этап 1: dependencies
FROM python:3.11-slim AS dependencies
WORKDIR /app
COPY requirements.txt .
RUN pip install --target=/opt/deps -r requirements.txt
# Этап 2: test
FROM dependencies AS test
COPY . .
RUN pip install pytest
RUN pytest
# Этап 3: production
FROM python:3.11-slim-distroless
COPY --from=dependencies /opt/deps /usr/local/lib/python3.11/site-packages
COPY --from=test /app /app
WORKDIR /app
CMD ["python", "app.py"]
# Можно собрать только test этап: docker build --target test .
# Или только production: docker build --target production .
Когда использовать multiple FROM?
Используй multi-stage build когда:
- Нужны инструменты для сборки (pip, npm, maven, gcc)
- Есть шаги компиляции или минификации
- Нужно оптимизировать размер финального образа
- Работаешь с production deployment
Можешь не использовать когда:
- Это простой скрипт, размер не критичен
- Разработка локально (размер образа не важен)
- Используешь serverless (размер = стоимость)
Multi-stage build — это лучшая практика для production Docker образов.