У тебя образ 1.8 ГБ. Какие шаги предпримешь, чтобы уменьшить?
Применяй multi-stage build, минимальный базовый образ (alpine/distroless), объединяй RUN-команды и чисти кэш пакетного менеджера в одном слое, добавляй .dockerignore.
Диагностика: откуда 1.8 ГБ?
Первый шаг — понять, какие слои занимают место:
docker image history my-app:latest
docker buildx imagetools inspect my-app:latest
# или утилита dive
dive my-app:latest
После анализа применяю набор техник последовательно, от наибольшего эффекта к наименьшему.
1. Многоэтапная сборка (multi-stage build)
Самое мощное средство — финальный образ не содержит компилятора, тестовых зависимостей и кэша пакетного менеджера.
# Пример: Go-приложение
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app ./cmd/server
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app /app
ENTRYPOINT ["/app"]
Тот же приём работает для Python (builder ставит pip-зависимости, финальный образ копирует только site-packages), для Node (builder — npm ci + npm run build, финальный — только dist + prod-модули).
2. Выбор правильного базового образа
ubuntu:22.04— ~77 МБ, но тянет лишние утилиты.debian:bookworm-slim— ~75 МБ, хороший баланс.alpine:3.20— ~7 МБ, musl вместо glibc (может ломать C-расширения Python).gcr.io/distroless/python3-debian12— только рантайм, нет shell.scratch— только для статически слинкованных бинарей.
3. Чистка кэша пакетного менеджера в одном слое
RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq5 \
&& rm -rf /var/lib/apt/lists/*
Если RUN apt-get и rm разделены на два слоя — файлы останутся в нижнем слое и образ не уменьшится.
4. .dockerignore
Без него COPY . . затягивает node_modules, .git, __pycache__, .venv — часто сотни МБ.
.git
.venv
node_modules
__pycache__
*.pyc
dist
.pytest_cache
5. Python-специфика
RUN pip install --no-cache-dir -r requirements.txt
# --no-cache-dir убирает ~/.cache/pip
# при использовании multi-stage копируем только site-packages:
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
6. Сжатие финального образа
Docker BuildKit поддерживает сжатие при push: docker buildx build --push --compression=zstd. Утилита docker-slim автоматически убирает недостижимые файлы, уменьшая образ до минимума.
Подводные камни
- Alpine + Python: пакеты с C-расширениями (numpy, cryptography) требуют musl-совместимых wheel или сборки из исходников — это медленнее и иногда невозможно без флагов.
- Разбивка RUN на несколько строк ради читаемости увеличивает число слоёв и суммарный размер — всегда объединяй в один RUN через &&.
- COPY --from=builder может скопировать лишние файлы, если путь задан слишком широко (например, COPY --from=builder /usr/local /usr/local).
- distroless: нет shell, нет curl, нет apt — невозможно зайти exec /bin/sh для отладки. Для дебага используй отдельный debug-тег: gcr.io/distroless/python3-debian12:debug.
- docker-slim может обрезать файлы, необходимые в рантайме, которые не проявились при профилировании (например, редко используемые плагины).
- Старый builder (без BuildKit) не поддерживает multi-stage cache mount и inline cache — всегда используй DOCKER_BUILDKIT=1 или docker buildx.
- Удалять dev-зависимости вручную (npm prune --production) в одном образе менее эффективно, чем multi-stage, потому что npm создаёт промежуточные файлы в нижних слоях.
Common mistakes
- Начинать с замены base image на scratch без проверки runtime dependencies.
- Удалять большие файлы в отдельном позднем RUN и ждать уменьшения image.
- Забывать .dockerignore и случайно копировать .git, caches или dumps.
What the interviewer is testing
- Использует docker history или аналоги для анализа слоёв.
- Предлагает multi-stage и production-only dependencies.
- Балансирует размер, безопасность, debuggability и скорость CI.