Ответ
Multi-stage сборка в Docker — это техника, позволяющая использовать несколько промежуточных этапов (stages) в одном Dockerfile для сборки приложения. Каждый этап начинается с инструкции FROM и может использовать свой базовый образ.
Основная цель — значительно уменьшить размер итогового Docker-образа, исключив из него всё ненужное для запуска приложения: инструменты сборки (компилятор Go, SDK), исходный код, временные файлы и зависимости.
Пример для Go-приложения:
# --- Этап 1: Сборка (Builder) ---
# Используем полный образ Go для компиляции
FROM golang:1.21-alpine as builder
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем файлы зависимостей и загружаем их
COPY go.mod go.sum ./
RUN go mod download
# Копируем исходный код и собираем бинарный файл
COPY . .
# Флаги -ldflags="-s -w" убирают отладочную информацию, уменьшая размер
RUN go build -ldflags="-s -w" -o myapp
# --- Этап 2: Финальный образ (Final) ---
# Используем минималистичный образ, не содержащий ничего лишнего
FROM alpine:latest
WORKDIR /root/
# Копируем только скомпилированный бинарник из предыдущего этапа (builder)
COPY --from=builder /app/myapp .
# Указываем команду для запуска приложения
CMD ["./myapp"]
Ключевые преимущества:
- Минимальный размер образа: Образ
builderможет весить >500 МБ (включая Go SDK), а финальный образ на основеalpine— всего 10-15 МБ. - Повышение безопасности: Меньше компонентов в итоговом образе означает меньшую поверхность для атак.
- Чистота и порядок: В финальном образе содержатся только артефакты, необходимые для работы приложения.
Ответ 18+ 🔞
Да ты посмотри, какая хитрая жопа придумана! Multi-stage сборка в Docker — это, блядь, как двухходовочка для лохов. Берёшь один Dockerfile, а внутри него — несколько этапов, как в плохом спектакле. Каждый этап стартует с этой своей FROM и живёт своей жизнью.
Смысл всей этой ёблы — на выходе получить образ, который весит не как чугунный мост, а как пустое ведро. Выкинуть нахуй всё лишнее: компиляторы, исходники, временные файлы — всё, что нужно только для постройки, но не для жизни.
Вот, смотри, как для Go-проги делают:
# --- Этап 1: Стройка (Builder) ---
# Берём здоровенный образ Go, чтоб всё скомпилить
FROM golang:1.21-alpine as builder
WORKDIR /app
# Тащим файлики с зависимостями и качаем их
COPY go.mod go.sum ./
RUN go mod download
# Тащим весь исходный код и лепим из него бинарник
COPY . .
# Эти флаги `-ldflags="-s -w"` вырезают отладочную хуйню, чтоб меньше весило
RUN go build -ldflags="-s -w" -o myapp
# --- Этап 2: Результат (Final) ---
# А теперь берём образ-голыш, alpine, в нём нихуя лишнего нет
FROM alpine:latest
WORKDIR /root/
# И вот тут магия: копируем ТОЛЬКО готовый бинарник из этапа `builder`
COPY --from=builder /app/myapp .
# И говорим, что запускать
CMD ["./myapp"]
И что мы, блядь, выигрываем?
- Размер — овердохуища меньше: Образ
builderможет быть полгига, потому что там весь Go-мир. А финальныйalpine— 10-15 мегов, кот наплакал. - Безопасность повыше: Чем меньше в образе всякого говна, тем меньше дырок для взлома.
- Чистота, блядь, и порядок: В итоговом контейнере лежит только то, что приложению реально нужно для работы. Никакого мусора. Красота!