lenec ru

← все посты

Docker slim images: Alpine vs Distroless vs scratch — что выбрать

18K

Выбор базового образа — одно из первых решений при контейнеризации. От него зависит размер итогового image, количество CVE в сканере, время cold start и возможность дебага в проде. Три главных кандидата на минимальный образ — Alpine, Distroless и scratch. Разберём каждый.

Зачем минимальные образы

Стандартный node:18 или python:3.12 на Debian весит 800-1000 MB. Внутри — сотни пакетов, которые вашему приложению не нужны, но каждый из них может содержать уязвимость. Минимальный образ даёт:

  • Безопасность — меньше пакетов = меньше CVE. Trivy на debian-based находит 200+ уязвимостей, на Alpine — 5-10, на distroless — 0-3
  • Размер — быстрее pull из registry, дешевле хранение, быстрее деплой
  • Cold start — критично для serverless и scale-to-zero: разница между 1 GB и 50 MB образом — это секунды на старте

Alpine: компактный Linux

Alpine Linux — минималистичный дистрибутив на musl libc и BusyBox. Базовый образ alpine:3.19 весит ~7 MB.

Плюсы:

  • Полноценный shell, пакетный менеджер apk
  • Можно дебажить: зайти в контейнер, поставить curl, strace
  • Официальные -alpine варианты для большинства рантаймов

Подводные камни:

  • musl vs glibc — некоторые библиотеки (особенно с native-расширениями) собраны под glibc и падают на musl. Python-пакеты с C-extensions, Node.js native addons — зона риска
  • DNS — musl резолвит DNS иначе: не поддерживает search и ndots из resolv.conf так же, как glibc. В Kubernetes это может вызвать проблемы с service discovery
  • Python wheels — многие PyPI-пакеты не имеют musl-совместимых wheels, pip собирает из исходников (долго, нужны build-deps)
FROM node:18-alpine
WORKDIR /app
RUN apk add --no-cache dumb-init
COPY --from=build /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/main.js"]

Distroless от Google

Образы gcr.io/distroless/* содержат только рантайм языка и минимальные системные библиотеки. Нет shell, нет пакетного менеджера, нет даже ls.

Доступные варианты:

  • gcr.io/distroless/static-debian12 — для статических бинарников (Go, Rust)
  • gcr.io/distroless/base-debian12 — glibc + libssl + ca-certificates
  • gcr.io/distroless/nodejs18-debian12 — Node.js 18 рантайм
  • gcr.io/distroless/python3-debian12 — Python 3 рантайм

Плюсы: минимальная CVE-поверхность, glibc (нет проблем Alpine), Google поддерживает. Минусы: невозможно зайти в контейнер для дебага (нет shell), сложнее troubleshooting.

FROM gcr.io/distroless/nodejs18-debian12
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
CMD ["dist/main.js"]

Для дебага есть :debug теги с busybox shell: gcr.io/distroless/nodejs18-debian12:debug.

scratch: пустой образ

scratch — это буквально ничего. Ноль слоёв, ноль файлов. Подходит только для статически скомпилированных бинарников:

FROM golang:1.22-alpine AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o server .

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

Не забудьте скопировать CA-сертификаты, если приложение делает HTTPS-запросы. Также нет /tmp, нет /etc/passwd — если нужен non-root user, создайте его в build-стадии и скопируйте /etc/passwd.

Сравнительная таблица

Образ                  Размер    CVE*   Shell   Debug   glibc
─────────────────────────────────────────────────────────────
debian:12-slim         ~80 MB    50+    да      да      да
alpine:3.19            ~7 MB     5-10   да      да      нет (musl)
distroless/base        ~20 MB    0-3    нет     нет**   да
distroless/static      ~2 MB     0      нет     нет     нет
scratch                0 MB      0      нет     нет     нет

* Типичное количество CVE при сканировании Trivy. ** Есть :debug-теги.

Один сервис — три базы

Go HTTP-сервер на каждом из вариантов:

$ docker images myserver
REPOSITORY   TAG          SIZE
myserver     alpine       12 MB
myserver     distroless   8.2 MB
myserver     scratch      6.1 MB

Для Go разница между distroless и scratch минимальна — выбирайте scratch, если не нужны CA-сертификаты из системы (или копируйте их вручную). Для Node.js и Python scratch не подходит — нужен рантайм, поэтому выбор между Alpine и Distroless.

Что выбрать

  • Go / Rust (static binary) → scratch или distroless/static
  • Node.js / Python в проде → distroless (если не нужен shell) или Alpine (если нужен дебаг)
  • Нужен shell и apk для troubleshooting → Alpine
  • Максимальная безопасность, compliance → Distroless

Универсального ответа нет — выбирайте под свой стек и требования к безопасности.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.