Що таке distroless образи і яку перевагу вони дають?
Distroless image це stripped-down container-image. Команда GoogleContainerTools Google ввела термін: «distribution-less», без повного Linux-distribution, лише голий мінімум для запуску застосунку. Результат значно менший і безпечніший за традиційні image.
Теорія
TL;DR
- Distroless image містить: мовний runtime + твій бінар + CA-certs. Усе.
- Без shell (без
bash, безsh). - Без package-manager (без
apt, безapk). - Без утиліт (без
curl, безvim, безps). - Підтримується Google:
gcr.io/distroless/<runtime>. - Варіанти:
static,base,cc,python3,nodejs22,java21тощо. Плюс:debugі:nonrootflavors. - Чому брати: менший image (часто 5-50 MB final), drastically менша attack-surface, швидший CVE-remediation (менше для сканування).
Distroless-варіанти
| Image | Вміст | Бери для |
|---|---|---|
gcr.io/distroless/static | нічого крім glibc + CA-certs | Static-бінарі (Go, Rust) |
gcr.io/distroless/base | static + glibc + busybox-static | Переважно static-бінарі з малими deps |
gcr.io/distroless/cc | base + libgcc | C/C++ бінарі з compiler-runtime |
gcr.io/distroless/python3 | Python-runtime | Python-app |
gcr.io/distroless/nodejs22 | Node 22-runtime | Node-app |
gcr.io/distroless/java21 | Java 21-runtime | JVM-app |
Кожен має :debug і :nonroot (краще) варіанти.
Швидкий приклад: Go-static-бінар
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/server ./cmd/server
FROM gcr.io/distroless/static:nonroot
COPY /out/server /server
ENTRYPOINT ["/server"]Фінальний image: ~10 MB. Лише Go-бінар + bare runtime-support файли. Жодного shell для drop-in, жодного apt-get, нічого, що не служить app напряму.
Node.js приклад
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
FROM gcr.io/distroless/nodejs22:nonroot
WORKDIR /app
COPY /app/dist /app/dist
COPY /app/node_modules /app/node_modules
CMD ["dist/server.js"]Фінальний image: ~150 MB (Node-runtime великий). Порівняй з node:22 (~1 GB) або навіть node:22-alpine (~200 MB), і alpine має shell і apk.
Чому distroless
Безпека
- Без shell = без shell-based експлойтів. Compromised-app не може exec
bash. - Без package-manager = attacker не може встановити tools для escalate.
- Менше файлів = менше CVE. Vulnerability-сканери репортять менше findings; remediation це просто «bump base-image».
Розмір
- 80-95% менший за повний Debian/Ubuntu.
- Менші pull, швидші cold-старти, менше storage.
- Cumulative-impact на масштабі: сотні сервісів × мільйони pulls = реальна економія bandwidth.
Передбачуваність
- Image містить точно те, що ти поклав. Без surprise-утиліт. Без drift між dev і прод.
Trade-off дебагу
Найбільше заперечення проти distroless: як docker exec sh?
Відповідь: ніяк. Trade-off реальний. Три обхідних шляхи:
1. :debug-варіант
docker run -it --entrypoint sh gcr.io/distroless/base:debugTag :debug додає busybox. Бери для розробки; deploy :nonroot у прод.
2. Sidecar debug-container
У Kubernetes:
kubectl debug -it pod/myapp --image=alpine --target=appКрутить alpine-shell, що ділить namespaces з твоїм distroless-container. Можеш копати у app filesystem і процеси з sister-container.
3. Збір окремого :debug-image
FROM gcr.io/distroless/nodejs22:debug AS debug
COPY /app /appДай два tag: myapp:1.0 (distroless) і myapp:1.0-debug (з shell). Deploy debug, коли треба.
Порівняння з Alpine
| Alpine | Distroless | |
|---|---|---|
| Має shell? | Так (busybox sh) | Ні |
| Має package-manager? | Так (apk) | Ні |
| Розмір base | ~7 MB | 2-50 MB залежно від варіанту |
| Легко debug? | Так | Ні (потрібен :debug-варіант) |
| Multi-stage friendly? | Так | Так |
| Glibc чи musl? | musl | glibc |
| Зрепортовані CVE | Деякі (APK-пакети) | Дуже мало |
Alpine це size-champion серед full-distros; distroless б'є його на attack-surface, але ціною debuggability. Багато проектів беруть alpine як builder + distroless як final.
Типові помилки
Спроба docker exec sh на distroless-image
$ docker exec -it myapp sh
OCI runtime exec failed: exec failed: ... "sh": executable file not found in $PATHShell нема. Для debug бери :debug-варіант або sidecar-патерн.
Healthcheck, що потребує curl
FROM gcr.io/distroless/nodejs22:nonroot
HEALTHCHECK CMD curl -f http://localhost:3000/health
# Падає: немає curl у distrolessРішення:
- Для Node:
HEALTHCHECK CMD node -e "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))" - Для інших: включи крихітний static health-checker-бінар; або бери external-healthcheck (Kubernetes-probe замість Docker-healthcheck).
Бери gcr.io/distroless/base для Go-бінаря, що потребує glibc
FROM gcr.io/distroless/static AS final # ← може не працювати для CGO-enabled бінарівЯкщо твій Go-бінар використовує CGO (будь-яка C-interop), бери cc або base-варіант. Для pure-Go бери static.
Забути :nonroot
FROM gcr.io/distroless/static # ← крутиться як root за замовчуваннямБери gcr.io/distroless/static:nonroot (UID 65532) за замовчуванням. Зменшує радіус ураження, якщо escape трапляється.
Real-world adoption
- Внутрішні сервіси Google: distroless це дефолт для прод-image (Google створив).
- Kubernetes-екосистема: багато CNCF-проектів шиппять distroless-image (Prometheus-компоненти, kube-state-metrics).
- Banking / регульовані індустрії: distroless зменшує auditor-біль і CVE-management overhead.
- Serverless: менші image = швидші cold-старти. AWS Lambda, Cloud Run виграють.
Питання для поглиблення
Q: Чи можу написати Dockerfile, що стартує FROM distroless напряму (без multi-stage)?
A: Технічно так, але не можеш нічого встановити (без shell, без apt). Distroless-base задумано як target multi-stage білду, не як builder.
Q: Яка різниця між static і base?
A: static має лише glibc і CA-certs. base додає busybox-static (кілька утиліт). Бери static для Go/Rust pure-static бінарів; base для C/C++, що потребують кількох утиліт.
Q: Чи distroless-image підписані?
A: Так, Google підписує через Cosign. Верифікуй через cosign verify gcr.io/distroless/base --certificate-identity=....
Q: Чи є Wolfi або Chainguard еквівалент?
A: Так, Chainguard Images це популярна альтернатива, що набирає ground. Wolfi-based, схожий мінімалізм, часто з ще швидшим CVE-patching. Варто оцінити для нових проектів.
Q: (Senior) Коли НЕ використовувати distroless?
A: Три кейси. (1) Твій app потребує shell у runtime (рідко; зазвичай smell). (2) Потребуєш багато tools у runtime, що потребуватимуть build кастомного minimal-image (краще: переосмисли app). (3) Команда ще не обладнана дебажити без docker exec sh — вкладай у tooling (sidecar-debug, observability) перш ніж форсувати distroless. Для greenfield Go/Rust сервісів distroless це no-brainer; для legacy-app з shell-dependent операціями мігруй поступово.
Приклади
Go у scratch (ще менший за distroless)
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/server ./cmd/server
FROM scratch
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY /out/server /server
USER 65532:65532
ENTRYPOINT ["/server"]scratch ще мінімальніший за distroless. Лише бінар + CA-certs. Фінальний image: приблизно розмір бінаря.
Distroless Node з healthcheck
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
FROM gcr.io/distroless/nodejs22:nonroot
WORKDIR /app
COPY /app/dist /app/dist
COPY /app/node_modules /app/node_modules
HEALTHCHECK \
CMD ["node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
CMD ["dist/server.js"]Без curl у distroless; беремо Node-runtime як healthcheck-бінар. Той самий ефект, без extra-tools.
Production-like Python
FROM python:3.13-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
COPY . .
FROM gcr.io/distroless/python3:nonroot
WORKDIR /app
COPY /install /usr/local
COPY /app /app
USER nonroot:nonroot
CMD ["app.py"]Python-runtime + твій код + залежності, нічого більше. Фінальний image близько 50-150 MB залежно від dep-розміру, набагато менше за python:3.13-slim ~150-300 MB.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів