Как и для чего использовать несколько инструкций FROM в одном Dockerfile

Ответ

Использование нескольких инструкций FROM в одном Dockerfile активирует механизм мультистадийной сборки (multi-stage builds). Каждая инструкция FROM начинает новый этап сборки, который может иметь свою собственную базовую среду.

Основная цель — уменьшить размер финального production-образа, исключив из него инструменты сборки, зависимости для разработки и промежуточные файлы.

Преимущества:

  • Минимальный размер образа: В итоговый образ копируются только необходимые артефакты (например, скомпилированный бинарный файл или node_modules).
  • Повышение безопасности: Исключаются компиляторы, исходный код и утилиты, которые не нужны для работы приложения и могут содержать уязвимости.
  • Упрощение CI/CD: Весь процесс сборки и упаковки описывается в одном Dockerfile.

Пример (сборка Go-приложения):

# --- Этап 1: Сборка (builder) ---
# Используем полный образ Go для компиляции
FROM golang:1.19 as builder

WORKDIR /app
COPY . .

# Собираем приложение. Флаги отключают CGO и создают статичный бинарник
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

# --- Этап 2: Финальный образ (final) ---
# Используем минималистичный образ без лишних компонентов
FROM alpine:latest

WORKDIR /root/

# Копируем только скомпилированный бинарный файл из предыдущего этапа
COPY --from=builder /app/myapp .

# Указываем команду для запуска приложения
CMD ["./myapp"]

В этом примере итоговый образ будет основан на alpine и содержать только один исполняемый файл myapp, а не всю среду разработки Go.

Ответ 18+ 🔞

Да ты посмотри, какая хитрая штука, блядь! Эти несколько FROM в одном докерфайле — это ж мультистадийная сборка, ёпта! Каждый новый FROM — это как новый заход, новый этап, своя собственная песочница, блядь.

А смысл-то какой, спросишь? А смысл, сука, в том, чтобы на выходе получить образ, который весит не как паровоз, а как пушинка! Чтобы в продакшене тащить за собой весь этот строительный хлам — компиляторы, исходники, дев-зависимости — это ж пидарас шерстяной, нахуй не нужно!

Что это даёт, блядь:

  • Размер, мать его, микроскопический! В финал попадает только готовый артефакт — бинарник или node_modules, а не вся кухня, где его готовили.
  • Безопасность, ёбана! Нету в финале компиляторов, исходного кода и прочего говна, через которое могут взломать. Чистота, блядь!
  • Простота, в рот меня чих-пых! Вся эта волокита со сборкой и упаковкой — в одном файле. Красота!

Вот смотри, как это на Go выглядит, блядь:

# --- Этап 1: Стройплощадка (builder) ---
# Берём полную банду Go-разработчиков, чтобы собрать наше приложение
FROM golang:1.19 as builder

WORKDIR /app
COPY . .

# Компилим наше детище. Выключаем всякую хуйню (CGO) и делаем статичный бинарник
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

# --- Этап 2: Фронт-офис, продакшен ---
# А тут уже минимализм, блядь! Alpine — лёгкий, как пёрышко.
FROM alpine:latest

WORKDIR /root/

# И вот, магия, сука! Только бинарник тащим со стройплощадки.
# Всё остальное говно остаётся там!
COPY --from=builder /app/myapp .

# И поехали работать!
CMD ["./myapp"]

И что получается, а? Получается образ на основе alpine с одним-единственным файликом myapp внутри. А не целый грузовик с инструментами Go, которые в продакшене только место занимают! Вот это оптимизация, блядь!