← Назад к вопросам

Сколько может быть инструкций FROM в Dockerfile?

2.2 Middle🔥 221 комментариев
#Docker и контейнеризация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сколько инструкций FROM может быть в Dockerfile?

В классическом, одиночном процессе сборки (docker build), Dockerfile может содержать несколько инструкций FROM, но с критически важным ограничением: каждая инструкция FROM начинает новую стадию сборки (stage), и в итоговый образ попадает содержимое только последней стадии. Это фундаментальная концепция многостадийных сборок (multi-stage builds), появившаяся в Docker 17.05.

Таким образом, технически количество инструкций FROM не ограничено (пока хватает ресурсов), но каждая из них создает независимый контекст сборки.

Ключевые принципы и использование

  1. Цель многостадийных сборок: Резкое уменьшение размера итогового образа за счет разделения сборки (build stage) и финального рантайма (runtime stage).
    *   На ранних стадиях используются тяжелые образы с компиляторами, SDK, вспомогательными инструментами.
    *   На финальной стадии используется минимальный образ (часто `alpine` или `scratch`), куда копируются **только** скомпилированные артефакты из предыдущих стадий.

  1. Именование стадий: Стадиям можно (и нужно) давать имена с помощью ключевого слова AS. Это позволяет явно ссылаться на них позже.

    FROM golang:1.21-alpine AS builder
    WORKDIR /app
    COPY . .
    RUN go build -o myapp .
    
    FROM alpine:latest AS final
    COPY --from=builder /app/myapp /usr/local/bin/myapp
    CMD ["myapp"]
    
  2. Копирование между стадиями: Инструкция COPY --from=<stage_name> позволяет копировать файлы из любой предыдущей стадии или даже из внешнего образа (например, COPY --from=nginx:latest /etc/nginx/nginx.conf /config/).

Пример многостадийного Dockerfile для приложения на Go

# Стадия 1: Сборка приложения
FROM golang:1.21-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o /bin/myapp ./cmd/app

# Стадия 2: Сборка фронтенда (параллельная концептуальная стадия)
FROM node:18-alpine AS frontend-builder
WORKDIR /app
COPY frontend/ .
RUN npm ci && npm run build

# Стадия 3: Создание минимального финального образа
FROM alpine:latest AS final
RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime
WORKDIR /root/

# Копируем бинарник Go из стадии 'builder'
COPY --from=builder /bin/myapp .
# Копируем статику фронтенда из стадии 'frontend-builder'
COPY --from=frontend-builder /app/dist ./static

# Запускаем приложение
EXPOSE 8080
CMD ["./myapp"]

В этом примере три инструкции FROM, создающие три независимые стадии. В финальный образ final попадает только содержимое Alpine Linux и два скопированных артефакта, без всего инструментария Go и Node.js, что экономит сотни мегабайт.

Важные нюансы

  • Аргументы и переменные среды: Объявленные с помощью ARG или ENV в одной стадии не доступны в другой, если их явно не переобъявить. Каждая стадия — изолированный контекст.
  • История и кэш: Docker кэширует слои для каждой стадии отдельно. Изменения в одной стадии не инвалидируют кэш для другой.
  • Сборка конкретной стадии: Можно собрать только промежуточную стадию для отладки или извлечения артефактов с помощью флага --target.
    docker build --target builder -t myapp:builder .
    
  • Одиночная стадия: Если вам не нужна многостадийность, используйте только одну инструкцию FROM в начале файла.

Вывод

Инструкций FROM в Dockerfile может быть столько, сколько необходимо стадий сборки. Их множественное использование — это не ошибка, а мощный стандартный подход (best practice) для создания оптимизированных, безопасных и минималистичных образов контейнеров путем разделения обязанностей между стадиями сборки и выполнения. Главное — четко понимать, что каждая инструкция FROM начинает новый, изолированный контекст, и управлять переносом артефактов между ними с помощью COPY --from.