Поиск по сайту:

Как создать Dockerfile из существующего образа


Образы Docker создаются путем создания Dockerfiles. Процесс сборки выполняет инструкции в Dockerfile для создания слоев файловой системы, формирующих окончательный образ.

Что делать, если у вас уже есть изображение? Можете ли вы получить файл Dockerfile, из которого он был создан? В этой статье мы рассмотрим два метода, с помощью которых можно этого добиться.

Цель

Когда вы создаете свои собственные образы Docker, вы должны хранить свои Dockerfiles как файлы с контролем версий в исходном репозитории. Эта практика гарантирует, что вы всегда сможете получить инструкции, используемые для сборки ваших изображений.

Однако иногда у вас не будет доступа к Dockerfile. Возможно, вы используете образ, который находится в общедоступном реестре, но имеет недоступный исходный репозиторий. Или вы можете работать со снимками образов, которые напрямую не соответствуют версионному Dockerfile. В этих случаях вам нужен метод, который может создать Dockerfile из образа на вашем компьютере.

Docker не предлагает никаких встроенных функций для достижения этой цели. Встроенные образы не связаны с Dockerfile, из которого они были созданы. Однако вы можете перепроектировать процесс сборки, чтобы получить хорошее приближение к Dockerfile образа по запросу.

Команда истории Docker

Команда docker history показывает историю слоев изображения. Он показывает команду, используемую для построения каждого последующего уровня файловой системы, что делает его хорошей отправной точкой при воспроизведении Dockerfile.

Вот простой Dockerfile для приложения Node.js:

FROM node:16
COPY app.js .
RUN app.js --init
CMD ["app.js"]

Соберите образ с помощью docker build:

$ docker build -t node-app:latest .

Теперь проверьте историю слоев изображения с помощью docker history:

$ 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:0c0828d0765af4dd...   50B       
424bc28f998d   4 days ago       /bin/sh -c #(nop)  CMD ["node"]                 0B        
<missing>      4 days ago       /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry...   0B        
...

История включает в себя полный список слоев изображения, в том числе унаследованных от базового изображения node:16. Слои упорядочены таким образом, что самый последний из них находится первым. Вы можете определить, где начинаются слои, созданные образцом Dockerfile, в зависимости от времени создания. Они показывают внутреннее представление Docker инструкций COPY и CMD, используемых в Dockerfile.

Вывод docker history более полезен, когда таблица ограничена только показом команды каждого слоя. Вы также можете отключить усечение, чтобы просмотреть полную команду, связанную с каждым слоем:

$ 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:0c0828d0765af4dd87b893f355e5dff77d6932d452f5681dfb98fd9cf05e8eb1 in . 
/bin/sh -c #(nop)  CMD ["node"]
/bin/sh -c #(nop)  ENTRYPOINT ["docker-entrypoint.sh"]
...

Из этого списка команд вы можете получить обзор шагов, предпринятых для сборки образа. Для простых изображений, подобных этому, этой информации может быть достаточно, чтобы точно воспроизвести Dockerfile.

Автоматизация извлечения слоев с помощью Whaler и Dfimage

Копирование команд из docker history — трудоемкий процесс. Вам также необходимо удалить /bin/sh -c в начале каждой строки, так как Docker обрабатывает каждую инструкцию как недействующий комментарий Bash.

К счастью, есть доступные инструменты сообщества, которые могут автоматизировать создание Dockerfile из истории слоев образа. Для целей этой статьи мы сосредоточимся на Whaler, который упакован в образ Docker alpine/dfimage (Dockerfile-from-Image) организацией Alpine.

Запуск образа dfimage и предоставление тега Docker приведет к выводу файла Dockerfile, который можно использовать для воспроизведения указанного изображения. Вы должны привязать сокет Docker вашего хоста к контейнеру dfimage, чтобы он мог получить доступ к вашему списку изображений и при необходимости вытащить тег.

$ 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:bcbc3d5784a8f1017653685866d30e230cae61d0da13dae32525b784383ac75f in .
    app.js

RUN ./app.js --init
CMD ["app.js"]

Созданный файл Dockerfile содержит все необходимое для перехода от с нуля (пустая файловая система) к последнему слою указанного образа. Он включает в себя все слои, исходящие из базового образа. Вы можете увидеть их в первых инструкциях ENTRYPOINT и CMD в образце вывода выше (другие слои базового изображения для краткости опущены).

За исключением COPY, инструкции для нашего образа соответствуют тому, что было написано в исходном файле Dockerfile. Теперь вы можете скопировать эти инструкции в новый Dockerfile, либо используя весь вывод dfimage, либо выбрав только ту часть, которая относится к окончательному образу. Последний вариант возможен только в том случае, если вы знаете идентификатор исходного базового образа, поэтому вы можете добавить инструкцию FROM в начало файла.

Ограничения

Во многих случаях dfimage сможет собрать пригодный для использования Dockerfile. Тем не менее, он не идеален, и точное совпадение не гарантируется. Степень расхождений по сравнению с исходным Dockerfile образа будет варьироваться в зависимости от используемых инструкций.

Не все инструкции фиксируются в истории слоев. Неподдерживаемые будут потеряны, и вы не сможете определить, какие они были. Наилучшая точность достигается с командами и инструкциями метаданных, такими как RUN, ENV, WORKDIR, ENTRYPOINT и CMD. Инструкции RUN могли отсутствовать, если их команда не приводила к изменениям файловой системы, то есть новый слой образа не создавался.

Инструкции COPY и ADD представляют собой уникальные проблемы. История не содержит путь к хост-файлу, который был скопирован в контейнер. Вы можете видеть, что копирование произошло, но исходный путь ссылается на хэш файла, который был скопирован в образ из контекста сборки.

Когда вы получите конечный пункт назначения, этого может быть достаточно, чтобы помочь вам понять, что было скопировано и почему. Затем вы можете использовать эту информацию для интерполяции нового исходного пути в Dockerfile, который вы можете использовать для будущих сборок. В других случаях проверка файла внутри образа может помочь выявить цель копии, чтобы вы могли определить осмысленное имя файла для пути к хосту.

Краткое содержание

Образы Docker не включают прямой способ работы в обратном направлении с файлом Docker, из которого они были созданы. Тем не менее, процесс сборки все еще можно собрать воедино. Для простых изображений с небольшим количеством инструкций вы часто можете разработать инструкции вручную, просмотрев столбец CREATED BY в выводе команды docker history.

Большие изображения с более сложными процессами сборки лучше всего анализируются такими инструментами, как dfimage. Это делает тяжелую работу по разбору подробных выходных данных docker history, создавая новый файл Dockerfile, который наилучшим образом соответствует вероятному оригиналу.

Усилия по обратному проектированию не идеальны, и некоторые инструкции Dockerfile теряются или искажаются в процессе сборки. Следовательно, вы не должны предполагать, что файлы Docker, созданные таким образом, являются точным представлением оригинала. Возможно, вам придется вручную внести некоторые коррективы в инструкции ADD и COPY, восстановив пути к хост-файлам, которые были преобразованы для создания контекстных ссылок.