Автоматизация извлечения слоёв с помощью Whaler и Dfimage
Ограничения
Резюме
Цель
Когда вы создаёте образы Docker самостоятельно, храните Dockerfile под версионным контролем рядом с кодом. Так вы всегда сможете восстановить точную историю сборки.
Но что делать, если Dockerfile недоступен — например, образ из публичного реестра, закрытый репозиторий или снимок, не привязанный к версии? В таких случаях полезно иметь методику, позволяющую получить воспроизводимый Dockerfile прямо из образа, который уже есть на машине. Docker не хранит исходный Dockerfile внутри образа, но историю слоёв и команды, которые создавали слои, — да. На основе этой информации можно собрать приближённую копию Dockerfile.
Важно: реконструированный Dockerfile — это реконструкция, а не гарантированная точная копия оригинала.
Команда docker history
Команда docker history показывает историю слоёв образа и команду, которая создала каждый слой. Это отправная точка для восстановления Dockerfile.
Пример исходного простого Dockerfile для Node.js:
FROM node:16
COPY app.js .
RUN app.js --init
CMD ["app.js"]
Построим образ:
$ docker build -t node-app:latest .
Посмотрим историю слоёв:
$ docker history node-app:latest
Пример упрощённого вывода (таблица):
IMAGE CREATED CREATED BY SIZE COMMENT c06fc21a8eed 8 seconds ago /bin/sh -c #(nop) CMD [“app.js”] 0B 74d58e07103b 8 seconds ago /bin/sh -c ./app.js –init 0B 22ea63ef9389 19 seconds ago /bin/sh -c #(nop) COPY file:… 50B 424bc28f998d 4 days ago /bin/sh -c #(nop) CMD [“node”] 0B
4 days ago /bin/sh -c #(nop) ENTRYPOINT ... 0B...
По умолчанию Docker усекaет длинные команды. Чтобы увидеть полные команды, используйте формат и флаг `--no-trunc`:
```
$ docker history node-app:latest --format "{{.CreatedBy}}" --no-trunc
```
Вы получите список, похожий на:
```
/bin/sh -c #(nop) CMD ["app.js"]
/bin/sh -c ./app.js --init
/bin/sh -c #(nop) COPY file:0c0828d0765af4dd... in .
/bin/sh -c #(nop) CMD ["node"]
/bin/sh -c #(nop) ENTRYPOINT ["docker-entrypoint.sh"]
...
```
Из этого списка видно, какие инструкции Docker интерпретировал как `COPY`, `RUN`, `CMD`, `ENTRYPOINT` и т.д. Для простых образов этого может быть достаточно, чтобы вручную воспроизвести Dockerfile.
Совет: используйте `--no-trunc` и форматирование, чтобы получить читаемый вывод, и фильтруйте лишние базовые слои, если знаете базовый образ.
## Автоматизация извлечения слоёв с помощью Whaler и Dfimage
Ручной разбор вывода `docker history` утомителен и подвержен ошибкам. Существуют утилиты, которые автоматизируют процесс и формируют Dockerfile по истории слоёв. Одна из таких — Whaler, объединённая в образ `alpine/dfimage`.
Запустите `dfimage`, передав тег образа. Контейнеру потребуется доступ к Docker сокету хоста, чтобы он мог просмотреть список образов и при необходимости подтянуть образ.
Пример запуска:
```
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock alpine/dfimage node-app:latest
```
Пример вывода анализа:
```
Analyzing node-app:latest
Docker Version: 20.10.13
GraphDriver: overlay2
Environment Variables
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|NODE_VERSION=16.14.2
|YARN_VERSION=1.22.18
Image user
|User is root
Dockerfile:
...
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node"]
COPY file:bcbc3d5784a8f10... in .
app.js
RUN ./app.js --init
CMD ["app.js"]
```
`dfimage` генерирует Dockerfile, который воспроизводит переход от пустой FS (`scratch`) до последнего слоя образа, включая слои базового образа. В результате вы получите последовательность инструкций, которые при повторной сборке дадут похожий образ.
Полезные альтернативы/дополнения:
- dive — интерактивный анализатор слоёв и содержимого образа (помогает понять, какие файлы добавлены в каждый слой).
- skopeo — позволяет просматривать метаданные образа без необходимости докачивать большой образ локально.
- docker save + tar — распаковка образа для исследования содержимого слоёв вручную.
## Практические приёмы для работы с копиями и содержимым образа
Иногда `docker history` показывает, что произошёл `COPY`, но не указывает исходный путь. В таких случаях можно извлечь файл из образа и проанализировать его:
1. Создайте контейнер без запуска:
```
$ docker create --name tmp node-app:latest
```
2. Скопируйте файл из контейнера на хост:
```
$ docker cp tmp:/app/app.js ./recovered-app.js
```
3. Просмотрите содержимое и признаки исходного файла (заголовок, комментарии, метаданные внутри кода).
4. Удалите временный контейнер:
```
$ docker rm tmp
```
Этот подход помогает подобрать осмысленный путь-источник для инструкции `COPY` в реконструируемом Dockerfile.
## Ограничения
Реконструкция Dockerfile из образа — полезный приём, но не волшебство. Основные ограничения:
- Не все инструкции оставляют след в слое. Инструкции, которые не изменили файловую систему (например, некоторые `ENV`, `LABEL` или пустые `RUN`), могут вообще не появиться в истории.
- `COPY` и `ADD` теряют исходный путь: история содержит лишь ссылку на хеш файла из build-context. Вы не узнаете оригинальный путь в хосте.
- Мультистадийные сборки усложняют соответствие между финальным образом и промежуточными Dockerfile-инструкциями. Если не знать исходный базовый образ и этапы, восстановить точный многослойный Dockerfile сложно.
- Скук (squashed) образы или образы, в которых слои были объединены/оптимизированы, теряют промежуточную информацию.
- Хакерские приёмы в Dockerfile (скрипты, которые меняют метаданные, динамическая загрузка) невозможно восстановить полностью.
Важно: никогда не полагайтесь на автоматически сгенерированный Dockerfile без его ручной проверки и тестирования.
## Когда метод не сработает
- Исходный образ — минимизированный, где команды были объединены или удалены после оптимизации.
- Образ создан с помощью нестандартных билд-систем (например, сборка через custom toolchain, не использующая обычные инструкции Docker).
- Исходные файлы были встроены в бинарные артефакты: по содержимому невозможно понять путь и контекст.
## Альтернативные подходы
Если автоматическая реконструкция недостаточна, рассмотрите:
- Поиск в репозиториях и реестрах: иногда Dockerfile хранится рядом с образом в registry/description или на GitHub/GitLab.
- Обратная разработка содержимого образа: извлеките бинарные артефакты и сопроводительную документацию, чтобы составить Dockerfile с нуля.
- Обращение к автору образа: запросите оригинальный Dockerfile или инструкции по сборке.
- Использование инструментов для статического анализа образа (`dive`, `trivy` для проверки уязвимостей) и восстановления зависимостей.
## Мини-методология для восстановления Dockerfile (шаг за шагом)
1. Скопируйте образ локально: `docker pull `.
2. Изучите историю: `docker history --no-trunc --format '{{.CreatedBy}}' `.
3. Сгенерируйте Dockerfile через `alpine/dfimage` или аналог: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock alpine/dfimage `.
4. Инспектируйте содержимое образа: `docker run --rm -it --entrypoint sh ` или создайте контейнер и используйте `docker cp` для извлечения файлов.
5. Сравните с предполагаемым базовым образом и при необходимости добавьте `FROM ` в начало.
6. Проверьте Dockerfile, соберите локально и прогоните тесты: `docker build -t reconstructed:latest .`.
7. Запустите тестовые сценарии и проверьте соответствие поведения оригинального образа.
## Критерии приёмки
- Сборка проходит без ошибок и создаёт образ, запускающийся как оригинал.
- Ключевые файлы и бинарники совпадают по содержимому или поведению.
- Сервис стартует и проходит smoke-тесты (например, отвечает на HTTP-запросы, возвращает ожидаемый код состояния).
- Размер образа находится в разумном диапазоне относительно оригинала (большие отличия могут сигнализировать о проблемах).
## Рольовые чек-листы
Developer
- Извлечь слои и посмотреть `docker history`.
- Найти и извлечь конфигурационные файлы и скрипты.
- Попробовать собрать образ локально и запустить unit/integration тесты.
Security Engineer
- Просканировать исходный и реконструированный образы на уязвимости (trivy, clair).
- Проверить права пользователей и наличие секретов в слоях.
Release Manager
- Сопоставить теги образов и версии зависимостей.
- Убедиться, что Dockerfile размещён под версионным контролем после восстановления.
## Чек-лист команд и шпаргалка
- Просмотр истории: `docker history --no-trunc `
- Полный формат: `docker history --format "{{.CreatedBy}}" --no-trunc `
- Генерация через dfimage: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock alpine/dfimage `
- Просмотр файлов: `docker create --name tmp `; `docker cp tmp:/path/file ./`; `docker rm tmp`
- Интерактивный анализ: `dive `
- Экспорт образа: `docker save -o image.tar` и распаковка `tar -xf image.tar`
## Критические рекомендации и безопасность
Важно: при подключении Docker сокета в контейнер вы даёте ему привилегированный доступ к Docker демону. Используйте образы с доверенным кодом и по возможности выполняйте анализ в изолированном CI-окружении.
Проверяйте, чтобы восстановленный Dockerfile не содержал секретов и чувствительных данных, которые могли оказаться включены в слои.
## Короткая шпаргалка по работе с COPY и ADD
- Если история показывает `COPY file: in /app`, извлеките файл из образа и посмотрите, какие зависимости он требует.
- Подумайте, откуда логично брать этот файл в исходном коде (например, `src/app.js` или `dist/app.js`).
- Для восстановления используйте осмысленные пути в build-контексте и документируйте предположения.
## Примеры ошибок и когда ожидать ручной работы
- Многоступенчатая сборка: нужно определить, какие артефакты были перенесены между стадиями.
- Скрипты в RUN выполняли сетевые загрузки: вы увидите только результат, но не саму команду в точности, если она не изменила FS заметно.
## 1‑строчный глоссарий
- Слой — инкремент файловой системы, созданный инструкцией Dockerfile.
- Build context — набор файлов и директорий, которые доступны при выполнении `docker build`.
- Squash — объединение слоёв в один пол для уменьшения размера.
## Часто задаваемые вопросы
### Можно ли гарантированно восстановить точный оригинальный Dockerfile?
Нет. Можно получить точную последовательность слоёв и команды, которые создали видимые изменения, но исходные пути для COPY/ADD и некоторые незаметные инструкции утрачиваются.
### Помогает ли `docker inspect`?
Да. `docker inspect` показывает метаданные образа — теги, переменные окружения, точки входа, CMD. Это полезно для восстановления метаданных Dockerfile.
### Как минимум тестировать реконструированный образ?
Соберите образ и прогоните smoke- и integration-тесты. Сравните поведение сервиса и, при необходимости, контейнерные показатели.
## Резюме
Реконструкция Dockerfile возможна и часто даёт достаточно точный результат для практических задач: дебага, миграции, воспроизведения среды. Команда `docker history` — базовый инструмент; `alpine/dfimage`, `dive` и ручная работа с контейнером и файлами помогают автоматизировать и дополнять вывод. Всегда проверяйте и тестируйте результат: некоторые инструкции восстановить невозможно, особенно связанные с исходными путями для `COPY/ADD`, мультистадийными сборками и командами без изменения FS.
Важно: не используйте автоматическую реконструкцию как единственный источник правды — храните Dockerfile в системе контроля версий и документируйте процесс сборки.
---
Социальные превью
OG title: Восстановление Dockerfile из образа
OG description: Как по слоям образа восстановить Dockerfile: команды, инструменты и ограничения. Практичные шаги и чек-листы.
Короткое объявление (для рассылки, 100–200 слов):
Нужен Dockerfile, но он утерян? В этой статье показаны практические способы восстановить Dockerfile из существующего образа Docker. Вы узнаете, как использовать `docker history` и инструменты вроде `alpine/dfimage` и `dive`, как извлечь файлы из образа и как проверить реконструированный Dockerfile. Также рассмотрены ограничения метода и чек-листы для разработчиков, security-инженеров и релиз-менеджеров. Статья поможет быстро получить воспроизводимый Dockerfile и избежать проблем при миграции или отладке образов.