Как создать минимальный Docker-образ?

«Как создать минимальный Docker-образ?» — вопрос из категории Docker, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для создания минимальных Docker-образов я использую несколько проверенных техник:

1. Многоступенчатая сборка (multi-stage build):

# Этап сборки
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .

# Финальный образ
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /app/app .
CMD ["./app"]

2. Использование scratch-образов для статических бинарников:

FROM scratch
COPY --from=builder /app/app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/app"]

3. Ключевые оптимизации, которые я применяю:

  • .dockerignore для исключения ненужных файлов:

    .git
    node_modules
    *.log
    Dockerfile
    README.md
  • Объединение RUN-команд в Alpine:

    RUN apk add --no-cache python3 py3-pip && 
    pip3 install --no-cache-dir flask && 
    apk del py3-pip
  • Использование distroless-образов от Google для Java/Python:

    FROM gcr.io/distroless/python3-debian12
    COPY app.py .
    CMD ["app.py"]

Результаты:

  • Go-приложение: ~5-10 МБ (Alpine) или ~2-5 МБ (scratch)
  • Python-приложение: ~80-100 МБ (slim) vs 1+ ГБ (полный образ)
  • Java-приложение: ~150 МБ (JRE slim) vs 500+ МБ (полный JDK)

Я всегда проверяю размер образа командой docker images и анализирую слои через docker history <image>.