Что такое многоэтапная сборка (Multi-stage builds) в Docker?

«Что такое многоэтапная сборка (Multi-stage builds) в Docker?» — вопрос из категории DevOps, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Многоэтапная сборка (Multi-stage builds) — это функция Docker, позволяющая использовать несколько инструкций FROM в одном Dockerfile. Каждая инструкция FROM начинает новый этап сборки, и можно копировать артефакты (например, скомпилированные бинарники, зависимости) из одного этапа в другой. Основная цель — создать минимальный и безопасный итоговый образ, исключив из него инструменты сборки, промежуточные файлы и ненужные зависимости.

Типичная проблема, которую решает Multi-stage: Без нее в финальном образе остаются компиляторы, заголовочные файлы, пакеты для разработки (например, gcc, python3-dev), которые нужны только для сборки приложения, но не для его запуска. Это увеличивает размер образа и поверхность для атак.

Пример Dockerfile для Go-приложения:

# ЭТАП 1: Сборка (Builder Stage)
# Используем полный образ с компилятором
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./ 
RUN go mod download          # Загружаем зависимости
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /myapp ./cmd/app  # Компилируем статический бинарник

# ЭТАП 2: Финальный образ (Runtime Stage)
# Используем минимальный образ без компилятора
FROM alpine:latest  
RUN apk --no-cache add ca-certificates  # Добавляем только необходимые для работы пакеты
WORKDIR /root/
COPY --from=builder /myapp .            # Копируем ТОЛЬКО бинарник из этапа сборки
CMD ["./myapp"]

Пример для Python-приложения:

# Этап сборки зависимостей
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt  # Устанавливаем зависимости в домашнюю директорию пользователя

# Финальный этап
FROM python:3.11-slim
WORKDIR /app
# Копируем только установленные пакеты из домашней директории пользователя этапа builder
COPY --from=builder /root/.local /root/.local
# Копируем исходный код приложения
COPY src/ .
# Убедимся, что скрипты могут найти пакеты из /root/.local
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]

Ключевые преимущества:

  • Значительное уменьшение размера образа: Финальный образ содержит только рантайм-зависимости и артефакты.
  • Повышение безопасности: Уменьшается количество установленных пакетов и, следовательно, потенциальных уязвимостей.
  • Улучшение структуры и читаемости: Весь процесс сборки и финальной упаковки описан в одном файле, что упрощает CI/CD.