Что такое multistage сборка Docker?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое Multistage сборка Docker (Multi-stage build)?
Multistage сборка (мультистейдж, многоэтапная сборка) — это мощная техника в Docker, позволяющая использовать несколько этапов (образов FROM) в одном Dockerfile для оптимизации итогового образа. Ключевая цель — отделить среду сборки (build environment) от финального рантайм-образа (runtime image), чтобы итоговый контейнер содержал только необходимые для работы артефакты (бинарники, зависимости), без инструментов компиляции, исходного кода или промежуточных файлов.
Как это работает?
В одном Dockerfile вы можете определить несколько этапов с помощью директивы FROM. Каждый этап начинается с нового базового образа и может копировать артефакты из предыдущих этапов. В итоговый образ попадает только содержимое последнего этапа.
Базовый пример для Go-приложения:
Представьте типичный сценарий: вам нужно скомпилировать Go-приложение (требуется Go SDK), а запустить — в минимальном образе (например, alpine).
Dockerfile без multistage (проблемный подход):
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o myapp main.go
# Финальный образ
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
В этом примере есть два этапа:
- Этап
builder: Использует полный образgolang:1.21-alpine(~350 МБ) для компиляции. Здесь выполняется загрузка зависимостей и сборка бинарникаmyapp. - Финальный этап: Начинается с чистого
alpine:latest(~5 МБ). С помощьюCOPY --from=builderкопируется только скомпилированный бинарник из этапаbuilder. Инструменты Go, исходный код, промежуточные объектные файлы — всё это остаётся в первом этапе и не попадает в финальный образ.
Итоговый образ будет весить ~10 МБ (Alpine + бинарник) вместо ~350 МБ, что критично для скорости деплоя, безопасности и эффективности использования реестров.
Преимущества multistage сборки
- Резкое уменьшение размера образа: Как показано выше, из образа удаляются все инструменты сборки, исходный код, временные файлы.
- Улучшенная безопасность: В финальном образе меньше компонентов → меньше поверхность для атак (меньше CVEs, уязвимых пакетов).
- Оптимизация кеширования слоёв: Каждый этап имеет собственный кеш. Изменения в исходном коде не инвалидируют кеш для этапа установки зависимостей, если
go.modне менялся. - Упрощение Dockerfile: Нет необходимости поддерживать отдельные скрипты для чистки или сложные цепочки команд.
- Гибкость: Можно использовать разные базовые образы для разных этапов (например,
golangдля сборки,distrolessилиscratchдля рантайма).
Продвинутые приёмы
Использование именованных этапов
Этапам можно давать имена ( AS <name>), чтобы явно ссылаться на них при копировании.
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app .
FROM gcr.io/distroless/static-debian12 AS final
COPY --from=builder /app/app /
CMD ["/app"]
Несколько этапов для разных артефактов
Иногда нужно подготовить несколько артефактов (бинарник, миграции, статика).
FROM node:18 AS frontend-builder
WORKDIR /app/frontend
COPY frontend/ .
RUN npm ci && npm run build
FROM golang:1.21 AS backend-builder
WORKDIR /app/backend
COPY backend/ .
RUN go build -o api .
FROM alpine:latest
WORKDIR /app
COPY --from=frontend-builder /app/frontend/dist ./static
COPY --from=backend-builder /app/backend/api .
CMD ["./api"]
Этап только для зависимостей (для улучшения кеширования)
Отдельный этап для скачивания зависимостей ускоряет повторные сборки.
FROM golang:1.21 AS deps
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
FROM deps AS builder
COPY . .
RUN go build -o myapp .
FROM alpine:latest
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Заключение
Multistage сборка — это must-have практика для создания production-образов. Для Go-разработчика это особенно актуально, учитывая статическую линковку и возможность создавать самодостаточные бинарники. Результат — минимальные, безопасные и быстрые в развёртывании контейнеры. Все современные CI/CD системы и Docker Engine (версии > 17.05) поддерживают эту функциональность. Игнорирование этого подхода ведёт к раздутым образам, повышенным рискам безопасности и неэффективному использованию ресурсов.