Docker FROM scratch — как создать образ с нуля

Быстрые ссылки
- Что такое образ?
- Образ “scratch”
- Что такое “scratch”?
- Что можно добавить в образ, основанный на scratch?
- Когда использовать scratch?
- Практическое руководство и чеклисты
- Критерии приёмки
- Резюме
Что такое образ?
Docker-образ — это набор слоёв файловой системы и метаданных, определённых в Dockerfile и используемых Docker Engine для создания контейнера. Обычно в Dockerfile вы указываете базовый образ, например:
FROM ubuntu:latestили
FROM debian:latestили
FROM alpine:latestТакие образы дают готовую среду: утилиты, менеджеры пакетов и общие системные библиотеки. Это удобнее, но увеличивает размер итогового образа. Если цель — максимально минимальный образ, имеет смысл сконструировать файловую систему самостоятельно — от самого нуля.
Образ “scratch”
Docker предоставляет специальный «образ» scratch, который фактически обозначает пустую, нижнюю файловую систему. В Dockerfile это выглядит так:
FROM scratchЭто не загружаемый образ и не контейнер для запуска: scratch — зарезервированное ключевое слово, означающее «начать с пустой файловой системы». После этой строки вы добавляете только те файлы и библиотеки, которые действительно необходимы вашему приложению.
Важно: нельзя выполнить docker pull scratch и нельзя запускать контейнер, основанный только на scratch, без добавления исполняемого процесса.
Что такое “scratch”?
scratch — специальный маркер, а не файл на Docker Hub. Все реальные Docker-образы в конечном счёте располагаются поверх scratch как базового слоя. Из-за того, что scratch пустой, в контейнере по умолчанию отсутствуют шелл, утилиты, менеджеры пакетов и системные библиотеки.
Что можно добавить в образ, основанный на scratch?
Минимально работающий образ на scratch содержит:
- исполняемый бинарный файл (команду), желательно статически скомпилированный для Linux;
- любые дополнительные файлы конфигурации, сертификаты, данные приложения;
- необходимые динамические библиотеки, если бинарник не статический (в этом случае нужно воспроизвести структуру /lib, /usr/lib и т.д.).
Простой пример с C-программой “hello world”:
Исходный код hello.c:
#include
int main() {
printf("Hello World\n");
return 0;
} Компиляция (статическая сборка предпочтительна):
gcc -static -o helloworld hello.cЗапуск локально для проверки:
./helloworldDockerfile для изображения на scratch:
FROM scratch
COPY helloworld /
CMD ["/helloworld"]Сборка и запуск:
docker build -t hello:latest .
docker run --rm hello:latestВы увидите вывод “Hello World”. Такой образ содержит только ваш бинарный файл и занимает минимум места (кило- или десятки килобайт), тогда как даже минимальные дистрибутивы увеличивают размер до мегабайт.
Когда нужно добавить дополнительные файлы
Если ваш исполняемый файл требует сертификатов (например, для HTTPS), конфигурационных файлов, или runtime-данных (шрифты, динамические модули), добавьте их в Dockerfile явно:
COPY ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY config.yaml /etc/myapp/config.yamlЕсли бинарник динамический, нужно добавить нужные библиотеки и настроить /lib64, /lib, /usr/lib и /etc/ld.so.cache; это сложнее и часто лучше использовать минимальный базовый образ.
Когда использовать scratch?
Используйте scratch когда:
- у вас статически скомпилированное приложение (Go, статически собранный C/C++), требующее минимум зависимостей;
- критична минимизация размера образа и стартового времени;
- вы готовы управлять всеми библиотеками и файлами вручную и тестировать окружение.
Не используйте scratch когда:
- приложение интерпретируется (Python, Ruby, Node.js) и требует системных пакетов и менеджеров пакетов;
- вам нужна интерактивная отладка внутри контейнера (шелл, утилиты);
- команда не готова на поддержку кастомной файловой системы в долгосрочной перспективе.
Практическое руководство: шаги, метод и чеклист
Мини‑методология для перехода на scratch:
- Определите, можно ли статически собрать приложение. Для Go и многих C/C++ проектов это просто. Для других — оцените динамические зависимости.
- Соберите и протестируйте бинарник локально с флагами статической сборки.
- Создайте минимальный Dockerfile FROM scratch, добавьте бинарник и необходимые файлы.
- Запустите контейнер и проверьте поведение (логи, код выхода, сетевые зависимости).
- Если требуются системные библиотеки — решите: добавить конкретные файлы вручную или перейти на лёгкий базовый образ (например, distroless или alpine).
- Пропишите тесты и критерии приёмки.
Чеклист перед релизом:
- Бинарник запускается в контейнере и отвечает на запросы;
- Все конфигурации и сертификаты присутствуют;
- Размер образа удовлетворяет требованиям;
- Логи и коды возврата корректны;
- По необходимости добавлены SUID/правила безопасности;
- CI/CD собирает и тестирует образ автоматически.
Примеры альтернатив и стратегий
- Distroless-образы: минимальные образы от Google, содержащие только runtime-библиотеки и ничего лишнего. Упрощают работу по сравнению со scratch, но больше по размеру.
- Alpine: лёгкий полноценный дистрибутив с пакетным менеджером apk. Удобен, если всё же нужны пакетные зависимости.
- Многостадийная сборка (multi-stage build): используйте полнофункциональный образ для сборки (например, golang:1.20), затем копируйте финальный статический бинарник в scratch.
Пример многостадийного Dockerfile для Go:
# Stage 1: build
FROM golang:1.20 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app ./cmd/app
# Stage 2: final
FROM scratch
COPY --from=builder /src/app /app
CMD ["/app"]Советы по статической компиляции и зависимостям
- Для Go: использовать CGO_ENABLED=0, чтобы получить полностью статический бинарник (если зависимости это позволяют).
- Для C/C++: статическая компоновка может потребовать указания статических версий библиотек (-static), но не все библиотеки доступны в статическом варианте.
- Убедитесь, что бинарник не зависит от специфичных системных файлов (/etc/nsswitch.conf, /etc/hosts и т.п.) — при необходимости добавьте их.
- Для SSL/HTTPS: добавьте набор корневых сертификатов (ca-certificates) в /etc/ssl/certs.
Критерии приёмки
- Приложение запускается и выполняет базовые сценарии (smoke tests).
- Контейнер корректно завершает работу при остановке и возвращает ожидаемые коды выхода.
- Отсутствуют скрытые зависимости: всё, что нужно для выполнения, добавлено в образ.
- Размер образа соответствует целевым ограничениям и не превышает допустимый порог.
- CI выполняет сборку и интеграционные тесты автоматически.
Чеклист по ролям
Разработчик:
- собрал статический бинарник и проверил локально;
- добавил минимально необходимые файлы;
- написал Dockerfile и тесты.
DevOps/Инженер по релизу:
- обеспечил CI/CD для сборки образов и запуска тестов;
- проверил параметры запуска (секьюрность, лимиты, healthcheck);
- настроил репозитории образов и политики сканирования уязвимостей.
QA-инженер:
- провёл интеграционные тесты контейнера;
- проверил сценарии восстановления и логирования.
Безопасность и поддержка
- Минимальные образы уменьшают поверхность атаки, но отсутствие стандартных утилит усложняет отладку.
- Обновления безопасности надо применять на уровне зависимостей: пересобирайте бинарник с обновлёнными библиотеками и CVE-фиксациями.
- Используйте сканеры уязвимостей для артефактов и Docker-образов в CI.
Когда scratch — это плохая идея
- Если приложение требует интерпретатора (Python, Node.js) и множество системных пакетов — использование scratch создаст избыточную сложность.
- Если команда должна часто деплоить и отлаживать контейнеры вручную — отсутствие шелла усложнит работу.
- Когда требования к поддержке и совместимости важнее экономии размера.
Модель принятия решения (Mermaid)
flowchart TD
A[Нужно ли минимизировать размер образа?] -->|Да| B[Можно ли статически собрать приложение?]
A -->|Нет| Z[Использовать лёгкий базовый образ]
B -->|Да| C[Использовать FROM scratch]
B -->|Нет| D[Рассмотреть distroless или alpine]
D --> E[Оценка затрат на поддержку]Тесты и кейсы приёмки
- Smoke: контейнер стартует и возвращает HTTP 200 на /health.
- Нагрузочный: приложение выдерживает ожидаемую нагрузку при старте нескольких реплик.
- Обновление: новая сборка заменяет старую без потери критичных данных (если применимо).
Резюме
FROM scratch — мощный инструмент для создания суперминимальных контейнеров, когда вы контролируете все зависимости и готовы поддерживать кастомную файловую систему. Он особенно полезен для статически скомпилированных приложений и когда критичен размер образа. В противном случае рассмотрите distroless или лёгкие базовые образы, которые уменьшают ручную работу и облегчают поддержку.
Ключевые рекомендации:
- используйте многостадийные сборки для отделения этапа сборки от финального образа;
- предпочитайте статическую компиляцию, если цель — scratch;
- автоматизируйте тесты и сканирование уязвимостей в CI.
Важно: scratch — это не универсальное решение; принимайте решение исходя из зависимостей приложения, требований к сопровождению и целевого окружения.
Похожие материалы
Как найти приложения для Android Wear 2.0
Как размыть фон в Canva: 3 простых способа
Установить и протестировать Windows 10 S
RRoD на Xbox 360: как устранить и диагностировать
Как проверить версию Ubuntu