Distroless и минимизация размера
Что нужно знать перед уроком
- M3.05 — Stapel-импорты — нужна базовая модель builder+import из M3, здесь она доводится до продакшен-уровня
- M8.05 — werf cleanup глубоко — продолжение серии продвинутых тем сборки
Что нужно знать перед уроком
M3 урок 5 — базовая модель импорта файлов между образами. Этот урок доводит паттерн builder+import до продакшен-уровня — полностью минимального финального образа без пакетного менеджера и shell.
Теория
Что такое distroless и зачем он нужен
Distroless-образ — это финальный образ контейнера, содержащий только сам бинарник приложения и минимально необходимый рантайм (например, CA-сертификаты для HTTPS-запросов, часовые пояса), но без пакетного менеджера (apt, apk), без shell (/bin/sh, /bin/bash), без стандартных Unix-утилит (ls, cat, curl) и вообще без дистрибутива Linux в привычном смысле. Классический пример базового образа для этого — gcr.io/distroless/* или scratch (полностью пустой образ).
Паттерн builder+import, доведённый до продакшен-уровня
| |
Обратите внимание: раньше метаданные образа (ENTRYPOINT/CMD/EXPOSE и т.д.) в Stapel задавались через директиву docker: — она устарела и будет удалена в werf v3, актуальный способ — imageSpec.config (тот же механизм, что используется для expose/healthcheck в werf.yaml сайта курса, см. M3 урок 2).
Ключевое отличие от промежуточного (не полностью distroless) варианта из M3 урока 5: базовый образ финального agent — не alpine/debian-slim (у которых есть shell и минимальный набор утилит), а gcr.io/distroless/static-debian13 — образ, который физически не содержит shell и пакетного менеджера. Поскольку Beszel — единственный статически слинкованный Go-бинарник без внешних runtime-зависимостей (в отличие от, например, Python-приложений, требующих интерпретатор), это идеальный кандидат для полностью distroless-финала: единственное, что нужно скопировать в финальный образ — сам бинарник.
Trade-off’ы: сложность отладки vs размер и площадь атаки
| Аспект | Обычный образ (alpine/debian) | Distroless |
|---|---|---|
| Размер финального образа | Десятки-сотни МБ | Единицы-десятки МБ (только бинарник + минимальный рантайм) |
kubectl exec -it ... sh для отладки внутри контейнера | Работает | Не работает — нет shell |
| Площадь атаки (attack surface) | Больше (пакетный менеджер, shell, утилиты — потенциальные векторы) | Минимальна |
| Наличие известных CVE в базовом образе | Выше (больше установленных пакетов = больше потенциальных уязвимостей) | Ниже |
| Удобство отладки в продакшене | Высокое | Низкое — требует альтернативных методов (эфемерные debug-контейнеры через kubectl debug) |
Ключевой практический trade-off: distroless-образ нельзя отладить привычным kubectl exec -it <pod> -- sh, потому что в контейнере физически нет shell. Альтернатива в современном Kubernetes — эфемерные debug-контейнеры (kubectl debug -it <pod> --image=busybox --target=<container>), которые подключаются к тому же namespace процессов пода, не изменяя сам оригинальный distroless-контейнер.
Когда distroless оправдан, а когда нет
Distroless имеет смысл для скомпилированных бинарников без интерпретируемого рантайма (Go, Rust, статически слинкованный C/C++) — именно такие приложения не нуждаются в shell/пакетном менеджере внутри финального контейнера вообще. Для приложений на интерпретируемых языках (Python, PHP, Node.js без bundling в единый бинарник) distroless либо невозможен напрямую, либо требует значительно более сложной сборки (например, PyInstaller для Python) — в таких случаях минимизация обычно ограничивается использованием -slim/alpine вариантов базовых образов, а не полным distroless.
Частые ошибки и подводные камни
- Пытаться применить distroless к приложению, требующему интерпретатор или shell в рантайме. Например, приложение, вызывающее
sh -c "..."внутри своей логики (например, для выполнения shell-хуков), сломается в distroless-образе — нет/bin/shдля выполнения команды. - Забывать про CA-сертификаты для HTTPS-запросов. Полностью пустой
scratch-образ не содержит системных CA-сертификатов — если приложение делает исходящие HTTPS-запросы, нуженgcr.io/distroless/static(с сертификатами) вместоscratch, либо явное копирование/etc/ssl/certs/ca-certificates.crtиз builder-образа. - Не иметь плана отладки production-инцидентов в distroless-контейнерах. Команда должна заранее знать про
kubectl debugс эфемерными контейнерами — попытка “просто зайти в под” по привычке в момент инцидента только потеряет время.
Практика в кластере
| |
Убедитесь на практике, что обычный exec в distroless-контейнер невозможен, и освойте альтернативный способ отладки через эфемерный debug-контейнер.
Практика разработки
Соберите werf.yaml для Beszel по паттерну builder+distroless из теории и сравните размер итогового образа с промежуточным вариантом на alpine:
| |
Шпаргалка команд урока
| |
Полная сводная таблица — 📋 werf .
Вопросы для самопроверки
Это ключевой практический trade-off distroless-подхода.
Источник: Рекомендуемая литература
Debug-контейнер подключается к namespace процессов пода без изменения оригинального контейнера.
Источник: Рекомендуемая литература
Такие приложения не нуждаются в shell/пакетном менеджере внутри финального контейнера вообще.
Источник: Рекомендуемая литература
Без CA-сертификатов TLS-рукопожатие для внешних HTTPS-запросов не сможет проверить сертификат сервера.
Источник: Рекомендуемая литература
Рекомендуемая литература
Официальная документация
- GitHub — henrygd/beszel (пример Go-приложения для distroless-сборки)
- Google distroless — GitHub
- docs.docker.com — Multi-stage builds
Статьи и блоги
- Вышла werf 2.0: новый движок развёртывания Nelm — Flant на Habr.
- werf v1.2 — Stapel и инкрементальная сборка — Flant на Habr.
- GitLab + K8s + Werf — Habr.
Книги
- Nigel Poulton. Docker Deep Dive. 2023. ISBN 978-1916585206.
Связанные материалы
- Предыдущий урок: M8.05 — werf cleanup глубоко.
- Следующий урок: M8.07 — werf converge как единая точка входа.
✓ Урок пройден — все вопросы самопроверки отвечены верно