Comment créer un Dockerfile à partir d’une image existante
Les images Docker sont créées en créant des Dockerfiles. Le processus de construction exécute les instructions du Dockerfile pour créer les couches du système de fichiers qui forment l’image finale.
Et si vous avez déjà une image ? Pouvez-vous récupérer le Dockerfile à partir duquel il a été construit ? Dans cet article, nous allons examiner deux méthodes qui peuvent y parvenir.
Sommaire
L’objectif
Lorsque vous créez vos propres images Docker, vous devez stocker vos Dockerfiles en tant que fichiers contrôlés par version dans votre référentiel source. Cette pratique garantit que vous pouvez toujours récupérer les instructions utilisées pour assembler vos images.
Parfois, vous n’aurez pas accès à un Dockerfile. Vous utilisez peut-être une image qui se trouve dans un registre public mais dont le référentiel source est inaccessible. Ou vous pourriez travailler avec des instantanés d’image qui ne correspondent pas directement à un Dockerfile versionné. Dans ces cas, vous avez besoin d’une technique capable de créer un Dockerfile à partir d’une image sur votre machine.
Docker n’offre aucune fonctionnalité intégrée pour y parvenir. Les images construites n’ont pas d’association avec le Dockerfile à partir duquel elles ont été créées. Cependant, vous pouvez désosser le processus de construction pour produire une bonne approximation du Dockerfile d’une image à la demande.
La commande Docker History
La docker history
La commande révèle l’historique des calques d’une image. Il montre la commande utilisée pour construire chaque couche de système de fichiers successive, ce qui en fait un bon point de départ lors de la reproduction d’un Dockerfile.
Voici un Dockerfile simple pour une application Node.js :
FROM node:16 COPY app.js . RUN app.js --init CMD ["app.js"]
Construire l’image en utilisant docker build
:
$ docker build -t node-app:latest .
Inspectez maintenant l’historique des calques de l’image avec docker history
:
$ docker history node-app:latest IMAGE CRÉÉE CRÉÉE PAR TAILLE COMMENTAIRE c06fc21a8eed Il y a 8 secondes /bin/sh -c #(nop) CMD ["app.js"] 0B 74d58e07103b il y a 8 secondes /bin/sh -c ./app.js --init 0B 22ea63ef9389 il y a 19 secondes /bin/sh -c #(nop) COPY file:0c0828d0765af4dd... 50B 424bc28f998d il y a 4 jours /bin/sh -c #(non) CMD ["node"] 0Bil y a 4 jours /bin/sh -c #(nop) ENTRYPOINT ["docker-entry... 0B ...
The history includes the complete list of layers in the image, including those inherited from the node:16
base image. Layers are ordered so the most recent one is first. You can spot where the layers created by the sample Dockerfile begin based on the creation time. These show Docker’s internal representation of the COPY
and CMD
instructions used in the Dockerfile.
The docker history
output is more useful when the table’s limited to just showing each layer’s command. You can disable truncation too to view the full command associated with each layer:
$ 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) COPIER le fichier : 0c0828d0765af4dd87b893f355e5dff77d6932d452f5681dfb98fd9cf05e8eb1 dans . /bin/sh -c #(nop) CMD ["node"] /bin/sh -c #(nop) POINT D'ENTREE ["docker-entrypoint.sh"] ...
À partir de cette liste de commandes, vous pouvez obtenir un aperçu des étapes suivies pour assembler l’image. Pour des images simples comme celle-ci, cela peut être une information suffisante pour reproduire avec précision un Dockerfile.
Automatisation de l’extraction de couches avec Whaler et Dfimage
Copier des commandes hors de docker history
est un processus laborieux. Vous devez également retirer le /bin/sh -c
au début de chaque ligne, car Docker traitait chaque instruction comme un commentaire Bash sans opération.
Heureusement, il existe des outils communautaires disponibles qui peuvent automatiser la création de Dockerfile à partir de l’historique des calques d’une image. Pour les besoins de cet article, nous nous concentrerons sur Whaler qui est emballé dans le alpine/dfimage
(Dockerfile-from-Image) Image Docker par l’organisation Alpine.
Exécution de la dfimage
image et en fournissant une balise Docker générera un Dockerfile qui peut être utilisé pour reproduire l’image référencée. Vous devez lier le socket Docker de votre hôte dans le dfimage
conteneur afin qu’il puisse accéder à votre liste d’images et tirer la balise si nécessaire.
$ 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"]
Le Dockerfile créé contient tout ce dont vous avez besoin pour passer de scratch
(un système de fichiers vide) au dernier calque de l’image spécifiée. Il comprend toutes les couches qui proviennent de l’image de base. Vous pouvez les voir dans le premier ENTRYPOINT
et CMD
instructions dans l’exemple de sortie ci-dessus (les autres couches d’image de base ont été omises par souci de brièveté).
À l’exception de COPY
, les instructions spécifiques à notre image correspondent à ce qui était écrit dans le Dockerfile d’origine. Vous pouvez maintenant copier ces instructions dans un nouveau Dockerfile
soit en utilisant l’ensemble dfimage
sortie ou en ne prenant que la partie qui se rapporte à l’image finale. Cette dernière option n’est possible que si vous connaissez l’identité de l’image de base d’origine afin que vous puissiez ajouter un FROM
instruction en haut du fichier.
Les limites
Dans de nombreux cas dfimage
pourra assembler un Dockerfile utilisable. Néanmoins, ce n’est pas parfait et une correspondance exacte n’est pas garantie. L’étendue des écarts par rapport au Dockerfile original de l’image variera en fonction des instructions utilisées.
Toutes les instructions ne sont pas capturées dans l’historique des couches. Ceux qui ne sont pas pris en charge seront perdus et il n’y a aucun moyen de déterminer ce qu’ils étaient. La meilleure précision est obtenue avec des instructions de commande et de métadonnées telles que RUN
, ENV
, WORKDIR
, ENTRYPOINT
et CMD
. RUN
des instructions pourraient toujours être manquantes si leur commande n’entraînait pas de modifications du système de fichiers, ce qui signifie qu’aucune nouvelle couche d’image n’était créée.
COPY
et ADD
les instructions présentent des défis uniques. L’historique ne contient pas le chemin du fichier hôte qui a été copié dans le conteneur. Vous pouvez voir qu’une copie s’est produite, mais le chemin source fait référence au hachage de fichier qui a été copié dans l’image à partir du contexte de génération.
Comme vous obtenez la destination finale, cela peut être suffisant pour vous aider à déterminer ce qui a été copié et pourquoi. Vous pouvez ensuite utiliser ces informations pour interpoler un nouveau chemin source dans le Dockerfile que vous pourrez utiliser pour les futures versions. Dans d’autres cas, l’inspection du fichier à l’intérieur de l’image peut aider à révéler le but de la copie afin que vous puissiez déterminer un nom de fichier significatif pour le chemin de l’hôte.
Sommaire
Les images Docker n’incluent pas de moyen direct de revenir au Dockerfile à partir duquel elles ont été créées. Il est cependant toujours possible de reconstituer le processus de construction. Pour les images simples avec peu d’instructions, vous pouvez souvent travailler les instructions manuellement en regardant le CREATED BY
colonne dans le docker history
la sortie de la commande.
Les images plus grandes avec des processus de construction plus complexes sont mieux analysées par des outils tels que dfimage
. Cela fait le travail difficile d’analyser le verbeux docker history
sortie pour vous, produisant un nouveau Dockerfile qui correspond au mieux à l’original probable.
Les efforts d’ingénierie inverse ne sont pas parfaits et certaines instructions Dockerfile sont perdues ou mutilées pendant le processus de construction. Par conséquent, vous ne devez pas supposer que les Dockerfiles créés de cette manière sont une représentation exacte de l’original. Vous devrez peut-être faire quelques ajustements manuels pour ADD
et COPY
instructions également, ressuscitant les chemins de fichiers hôtes qui ont été convertis en références de contexte de construction.