PRO

Как оптимизировать размер Docker-образа (multi-stage build)?

Multi-stage build разделяет сборку и запуск приложения на несколько этапов Dockerfile. На этапе builder устанавливают компиляторы и build-зависимости, а в финальный image копируют только готовые артефакты и runtime-зависимости, что уменьшает размер образа и поверхность атаки.
Подробный ответ

Зачем уменьшать image

Маленький Docker image быстрее собирается, передаётся в registry и запускается на серверах. В нём меньше лишних библиотек и утилит, а значит ниже поверхность атаки и проще управлять зависимостями.

Что такое multi-stage build

Multi-stage build использует несколько инструкций FROM в одном Dockerfile. Первый stage собирает приложение, а второй — финальный runtime-образ — получает только нужные файлы через COPY --from.

Пример для Python

FROM python:3.12-slim AS builder

WORKDIR /build
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

FROM python:3.12-slim AS runtime

WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*

COPY . .
RUN useradd --create-home appuser
USER appuser

CMD ["python", "main.py"]

Пример для frontend

FROM node:22-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS runtime
COPY --from=builder /app/dist /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]

Другие способы оптимизации

  • использовать подходящий минимальный базовый image, учитывая совместимость библиотек;

  • настроить .dockerignore и не отправлять в build context лишние файлы;

  • копировать сначала файлы зависимостей, а затем код, чтобы эффективно использовать Docker layer cache;

  • не хранить в runtime image компиляторы, тесты, исходники зависимостей и секреты;

  • запускать приложение не от root-пользователя.

Как проверить размер

docker image ls
docker image history my-app:latest

Как ответить на собеседовании

Для уменьшения Docker image я использую multi-stage build: в builder-этапе устанавливаю компиляторы и собираю артефакты, а в финальный runtime-этап копирую только то, что нужно для запуска. Дополнительно настраиваю .dockerignore, порядок COPY для layer cache и запускаю контейнер не от root.

Оцени свой прогресс

Честно оцени своё понимание этого вопроса, чтобы мы могли построить твой учебный трек максимально эффективно.
Читать в блоге