Avec la version en accès anticipé de Docker Scout , Docker Hub commence enfin à visualiser les éléments internes des images. C'est bien! Pourquoi Docker Hub ne l'a-t-il pas fait il y a des années ? En raison de son modèle économique.
Avant d'aller plus loin, passons brièvement en revue de quoi sont constituées les images Docker.
Lorsque vous téléchargez une image Docker en exécutant docker pull
sur la ligne de commande, la CLI Docker affiche la progression du téléchargement de chaque couche au fur et à mesure qu'elle extrait l'image. Si vous avez déjà téléchargé une image Docker, vous l'avez probablement vue :
Si vous avez déjà travaillé avec Docker, vous avez probablement défini certains de ces calques pour vos propres images. Les développeurs définissent implicitement ces couches lorsqu'ils écrivent des Dockerfiles. Par exemple, chaque ligne de ce Dockerfile produit une couche :
FROM ubuntu:22.04 # install apt dependencies RUN apt-get update RUN apt-get install -y iputils-ping python3 python3-pip # install python dependencies RUN pip3 install numpy # cleanup apt lists RUN rm -rf /var/lib/apt/lists/* CMD ["/bin/bash"]
Les couches sont des répertoires Linux archivés : ce sont des archives tar du système de fichiers. Docker télécharge toutes vos couches d'images et décompresse chacune d'elles dans un répertoire distinct. Lorsque vous lancez un conteneur à partir d'une image Docker à l'aide de la commande docker run
, le démon Docker combine les calques d'image pour former un conteneur.
Une grande partie de la valeur ajoutée de Docker en tant que produit réside dans le fait qu'il fait abstraction de ces détails, permettant aux utilisateurs de profiter des avantages des conteneurs en couches sans penser à leur fonctionnement. Mais toutes les abstractions fuient, et Docker ne fait pas exception : il faut parfois tirer le rideau.
Les couches d'images Docker contiennent l'histoire d'origine de chaque binaire présent dans le système de fichiers du conteneur. La première ligne d'une image Docker est « la ligne FROM ». Il définit l'image Docker (et donc les calques d'image) sur laquelle le Dockerfile est construit.
En inspectant les couches du Dockerfile actuel et les couches de sa chaîne d'images parent, les développeurs peuvent déterminer l'origine de chaque fichier du système de fichiers racine d'un conteneur. Ce sont des informations très précieuses. Il aide les développeurs :
Imaginez cliquer sur les couches d'une visualisation pour suivre les modifications apportées aux fichiers dans les versions d'images Docker. Lorsqu'une analyse de sécurité automatisée identifie une vulnérabilité dans l'une de vos images, imaginez utiliser un outil d'inspection de couche pour identifier comment la vulnérabilité a été introduite.
Des images de taille excessive peuvent coûter très cher aux entreprises. De nombreux pipelines CI/CD extraient des images Docker pour chaque demande d'extraction, de sorte que les longs téléchargements d'images Docker peuvent ralentir les pipelines, rendant les développeurs moins efficaces et faisant perdre du temps CPU. Étant donné que les organisations paient généralement leurs coûts d’infrastructure sur une base horaire, chaque heure perdue constitue une dépense inutile.
Au-delà du gaspillage de ressources informatiques, les images Docker gonflées peuvent entraîner des coûts de transfert réseau excessifs. Si une organisation télécharge des images Docker dans les régions AWS ou depuis AWS vers l'Internet ouvert, le gonflement des images Docker se traduit directement par un gaspillage d'infrastructure. Cela s’additionne rapidement.
Il est facile d'introduire accidentellement des ballonnements dans vos calques d'image Docker. Le Dockerfile décrit précédemment contient un exemple classique : la couche de la ligne 4 enregistre 40 Mo de fichiers sur le disque, qui sont ensuite supprimés par la couche de la ligne 11. En raison du fonctionnement des images Docker, ces données font toujours partie de l'image, ajoutant 40 Mo de taille d'image inutile.
Il s'agit d'un exemple simple : en fait, il vient directement de la documentation de Docker. Dans les Dockerfiles plus complexes, cette erreur peut être beaucoup plus difficile à repérer.
Il peut être difficile d'interagir avec les couches d'images Docker à l'aide de la ligne de commande, mais avant la sortie récente de Docker Scout, la ligne de commande était l'endroit où vous trouviez l'état de l'art. Voici les deux approches de base.
Il s'agit de la méthode Unix simple et à faire soi-même. Tout ce dont vous avez besoin est un hôte exécutant un démon Docker. C'est une approche simple :
docker create
pour démarrer un conteneur à partir de l'image.docker inspect
pour trouver les répertoires de couches du nouveau conteneur.cd
dans ces répertoires sur la ligne de commande et raisonnez sur les couches dans votre tête.
C'est un problème. Disons que nous essayons de détecter une certaine surcharge d'image dans une image Docker que nous utilisons depuis quelques mois mais dont la taille a récemment considérablement augmenté.
Tout d’abord, nous créons un conteneur à partir de l’image :
where-the@roadmap-ends ~ $ docker create --name example where-the-roadmap-ends 339b8905b681a1d4f7c56f564f6b1f5e63bb6602b62ba5a15d368ed785f44f55
Ensuite, docker inspect
nous indique où les répertoires de couches de l'image téléchargée se sont retrouvés sur notre système de fichiers :
where-the@roadmap-ends ~ $ docker inspect example | grep GraphDriver -A7 "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47-init/diff:/var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff:/var/lib/docker/overlay2/j0dekt7y8xgix11n0lturmf8t/diff:/var/lib/docker/overlay2/zd57mz6l4zrsjk9snc2crphfq/diff:/var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff:/var/lib/docker/overlay2/8c639b22627e0ad91944a70822b442e5bff848968263a37715a293a15483c170/diff", "MergedDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/merged", "UpperDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/diff", "WorkDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/work" }, "Name": "overlay2"
Les couches que nous souhaitons examiner pour cette enquête sont la liste des répertoires « LowerDir ». Les autres répertoires ne font pas partie de l'image Docker elle-même : nous pouvons les ignorer.
Nous analysons donc la liste des répertoires "LowerDir" sur la ligne de commande :
where-the@roadmap-ends ~ $ docker inspect example | grep GraphDriver -A7 | grep LowerDir | awk '{print $2}' | sed 's|"||g' | sed 's|,||g' | sed 's|:|\n|g' /var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47-init/diff /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff /var/lib/docker/overlay2/j0dekt7y8xgix11n0lturmf8t/diff /var/lib/docker/overlay2/zd57mz6l4zrsjk9snc2crphfq/diff /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff /var/lib/docker/overlay2/8c639b22627e0ad91944a70822b442e5bff848968263a37715a293a15483c170/diff
Il s'agit de la liste des calques de l'image, dans l'ordre, le calque le plus bas étant en premier. Nous devons maintenant corréler manuellement ces répertoires de couches avec les lignes Dockerfile qui les ont produits.
Malheureusement, Docker ne nous permet pas d'extraire directement ces lignes d'une image Docker. C'est le mieux que nous puissions obtenir en utilisant docker history
:
where-the@roadmap-ends ~ $ docker history where-the-roadmap-ends IMAGE CREATED CREATED BY SIZE COMMENT 6bbac081b2a7 2 hours ago CMD ["/bin/bash"] 0B buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c rm -rf /var/lib/apt/lists/* #… 0B buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c pip3 install numpy # buildkit 70MB buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c apt-get install -y iputils-pi… 343MB buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c apt-get update # buildkit 40.1MB buildkit.dockerfile.v0 <missing> 8 months ago /bin/sh -c #(nop) CMD ["bash"] 0B <missing> 8 months ago /bin/sh -c #(nop) ADD file:550e7da19f5f7cef5… 69.2MB
En utilisant cette sortie, nous pouvons identifier quelles couches ont des répertoires et quelle commande Dockerfile a créé la couche (affichée dans la colonne CREATED BY).
La commande docker history
affiche les couches de votre conteneur dans le même ordre que docker inspect
répertorie les répertoires de couches. Sachant cela, nous pouvons fusionner manuellement les deux sorties pour voir quelles couches sont plus grandes que les autres, quelle commande Dockerfile les a créées et quel répertoire contient chaque couche.
Voici le contenu de la couche A, qui effectue une apt-get update
:
where-the@roadmap-ends ~ $ du -hs /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff/var/lib/apt/lists 38.2M /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff/var/lib/apt/lists
Par rapport au contenu du calque B, cela supprime les fichiers restants du calque A :
where-the@roadmap-ends ~ $ du -hs /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff/var/lib/apt/lists 4.0K /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff/var/lib/apt/lists
Le répertoire /var/lib/apt/lists
existe dans les deux couches, mais dans la couche B, le répertoire n'utilise presque aucun espace.
En effet, le répertoire de la couche B contient des « fichiers de voile blanc », que Docker utilise pour marquer les fichiers à exclure du système de fichiers du conteneur final.
Pour cette raison, bien que les fichiers soient « supprimés » dans le calque B, ils existent toujours dans le calque A et contribuent ainsi à la taille globale de votre image : 38,2 Mo de surcharge inutile.
N'était-ce pas facile ? 😉
La méthode manuelle est si complexe et peu maniable que la communauté open source a créé un outil spécifiquement pour cette tâche : il s'appelle dive
. Dive est un outil CLI qui prend une image en entrée, analyse ses systèmes de fichiers et présente une interface utilisateur interactive basée sur du texte dans votre terminal. Il corrèle les calques d'image pour vous, vous permettant d'inspecter les répertoires de calques plus facilement.
Lorsqu'il est exécuté sur l'image Docker du Dockerfile ci-dessus, cela ressemble à ceci :
┃ ● Layers ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ Aggregated Layer Contents ├─────────────────────────────────────────────────────────────────────────────── Cmp Size Command └── var 69 MB FROM acee8cf20a197c9 └── lib 40 MB RUN /bin/sh -c apt-get update # buildkit └── apt 343 MB RUN /bin/sh -c apt-get install -y iputils-ping python3 python3-pip # buildkit └── lists 70 MB RUN /bin/sh -c pip3 install numpy # buildkit ├── auxfiles 0 B RUN /bin/sh -c rm -rf /var/lib/apt/lists/* # buildkit ├── lock ├── partial │ Layer Details ├─────────────────────────────────────────────────────────────────────────────────────────── ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_main_binary-arm64_Packages.lz4 Tags: (unavailable) ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_universe_binary-arm64_Packages.lz4 Id: 2bc27a99fd5750414948211814da00079804292360f8e2d7843589b9e7eb5eee ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_InRelease Digest: sha256:6e6fb36e04f3abf90c7c87d52629fe154db4ea9aceab539a794d29bbc0919100 ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_main_binary-arm64_Packages.lz4 Command: ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_multiverse_binary-arm64_Packages.lz4 RUN /bin/sh -c apt-get update # buildkit ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_restricted_binary-arm64_Packages.lz4 ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_universe_binary-arm64_Packages.lz4 │ Image Details ├─────────────────────────────────────────────────────────────────────────────────────────── ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_main_binary-arm64_Packages.lz4 Image name: where-the-roadmap-ends ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_multiverse_binary-arm64_Packages.lz4 Total Image size: 522 MB ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_restricted_binary-arm64_Packages.lz4 Potential wasted space: 62 MB ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_universe_binary-arm64_Packages.lz4 Image efficiency score: 90 % ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_main_binary-arm64_Packages.lz4 Count Total Space Path ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_multiverse_binary-arm64_Packages.lz4 2 28 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy_universe_binary-arm64_Pack ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_restricted_binary-arm64_Packages.lz4 2 7.3 MB /usr/bin/perl └── ports.ubuntu.com_ubuntu-ports_dists_jammy_universe_binary-arm64_Packages.lz4 2 4.4 MB /usr/lib/aarch64-linux-gnu/libstdc++.so.6.0.30 2 2.9 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy_main_binary-arm64_Packages 2 2.0 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_main_binary-arm64_ 2 1.7 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_universe_binary-ar
La plongée est un outil merveilleux, et je suis heureux qu'il existe, mais parfois, il ne suffit pas. Les interfaces textuelles ne sont pas les plus faciles à utiliser : parfois Grafana est plus agréable que top
.
De plus, les images volumineuses peuvent dépasser les capacités de Dive. Lors de l'inspection d'images volumineuses, Dive consomme beaucoup de mémoire. Parfois, le noyau tue le processus Dive avant de générer des données.
Du point de vue d'un développeur, il a toujours été logique de s'attendre à ce que les visualisations de couches d'images Docker existent dans Docker Hub. Après tout, nos données d'image Docker résident déjà dans le backend de Docker Hub.
J'ai souvent imaginé inspecter les éléments internes de l'image Docker dans mon navigateur, en utilisant une interface utilisateur qui ressemblait à ceci :
Avec Docker Scout, il semble que Docker s'oriente dans cette direction en tant qu'entreprise. Aujourd'hui, si je navigue vers la dernière image Postgres dans Docker Hub, je suis accueilli avec ceci :
En tant que développeur, c'est passionnant. Cette nouvelle interface utilisateur me permet de parcourir visuellement les détails de la couche d’image, en mettant en évidence les problèmes de vulnérabilité et de taille d’image comme je le souhaite.
Lorsque Docker Hub a été lancé pour la première fois, il comptait deux types d'utilisateurs :
Et Docker a dû aborder ces deux types d’utilisateurs différemment.
Après le lancement de Docker en 2013, les développeurs ont immédiatement voulu utiliser leur produit. Les conteneurs Docker standardisent le packaging et le déploiement des logiciels de chacun. Les conteneurs sont rapidement devenus populaires dans la communauté des développeurs de logiciels.
Les éditeurs de logiciels se sont en revanche montrés plus hésitants. Pourquoi devraient-ils télécharger leurs logiciels sur une plate-forme dont l'utilitaire principal consiste à supprimer la différenciation entre les produits logiciels ?
Docker a dû les convaincre pour faire de Docker Hub un succès. Ainsi, au lieu de se concentrer sur l’attraction des développeurs, la conception et l’ensemble des fonctionnalités de Docker Hub s’adressent aux éditeurs de logiciels.
Pour les éditeurs, Docker Hub était un site marketing. Cela leur a donné un endroit pour annoncer leurs produits. Il ne permettait pas aux développeurs de voir les détails internes de l’image Docker, pas plus que les concessionnaires automobiles ne vous laissaient démonter leurs blocs moteurs. Les détails techniques internes n'étaient pas exposés, ils étaient cachés, de sorte que les acheteurs restaient concentrés sur les produits eux-mêmes, et non sur la façon dont ils étaient fabriqués.
Docker Hub manquait de fonctionnalités destinées aux développeurs pour l'optimisation de la taille des images et l'introspection de la chaîne d'approvisionnement, car Docker avait déjà conquis les développeurs sans ces fonctionnalités. En fait, ces fonctionnalités ont tendance à donner une mauvaise image aux éditeurs de logiciels – et c’étaient les utilisateurs que Docker Hub devait encore convaincre pour réussir.
Cette dynamique au service à la fois des éditeurs de logiciels et des développeurs a fait de Docker Hub une plateforme à double face.
Les plates-formes bilatérales sont des entreprises qui ont deux catégories d'utilisateurs différentes et qui ont besoin que les deux participent à la plate-forme pour que les choses fonctionnent. Habituellement, c'est la motivation pour utiliser la plateforme qui divise les utilisateurs en différents groupes.
Bien que les deux groupes d'utilisateurs ne puissent pas effectuer de transactions directement entre eux, les plateformes bilatérales sont comme des marchés : elles ne créent de valeur que si les vendeurs se présentent pour vendre et les acheteurs se présentent pour acheter.
Dans l’industrie technologique, les plateformes bilatérales sont omniprésentes. En cas de succès, ces modèles économiques ont tendance à produire des effets de réseau qui propulsent les entreprises vers une croissance et une rentabilité soutenues. Après un certain point, la taille de la plateforme et sa position établie dans un espace attirent de nouveaux utilisateurs vers la plateforme.
Une fois que vous savez ce que vous recherchez, les plateformes à deux faces sont faciles à repérer. Voici trois exemples.
Sur LinkedIn, il existe deux types d'utilisateurs : les employés et les responsables du recrutement. Les deux groupes participent pour des raisons différentes : les employés veulent un emploi et les responsables du recrutement veulent embaucher des gens.
Aucun des deux groupes n'a pu obtenir ce qu'il voulait du site jusqu'à ce que l'autre groupe commence à participer. Une fois qu'un nombre suffisant de chaque groupe s'est inscrit, il est devenu le lieu par défaut pour les nouveaux membres de l'un ou l'autre groupe, et la croissance du site s'est perpétuée, jusqu'à être rachetée par Microsoft pour 26 milliards de dollars.
Sur YouTube , il y a des créateurs de contenu et il y a des téléspectateurs. Les téléspectateurs viennent sur le site pour voir des vidéos : les créateurs de contenu publient sur le site à la recherche de bonnes vibrations, de gloire et de fortune.
Après que YouTube ait acquis une réputation de lieu où les créateurs de contenu pouvaient réussir, de plus en plus de créateurs de contenu sont apparus et, à mesure qu'ils généraient plus de contenu, de plus en plus de téléspectateurs sont venus le visiter. Une fois que la plateforme a dépassé une certaine taille, les créateurs de contenu et les téléspectateurs n’ont eu d’autre choix que de continuer à l’utiliser : chaque groupe avait besoin de l’autre et ils ne pouvaient se trouver que via YouTube.
Pour que Docker Hub soit pertinent, il fallait que les éditeurs de logiciels téléchargent des images et que les développeurs les téléchargent. Une fois qu’un nombre suffisant d’éditeurs auraient envoyé des images vers Docker Hub, celui-ci deviendrait l’endroit par défaut à partir duquel les développeurs pourraient les extraire. Au fur et à mesure que la plate-forme continuait de croître, Docker Hub consoliderait sa domination en tant que registre unique pour les gouverner tous.
Au moins c'était le plan. En réalité, Docker (l'entreprise) a décliné et Docker Hub n'est jamais devenu « celui-là ». Lorsqu’elles construisent une plate-forme bilatérale, les startups n’ont qu’un seul produit, mais elles doivent trouver deux fois l’adéquation produit-marché. Pour Docker, ce fardeau était trop lourd à supporter : en fin de compte, il a divisé Docker en deux .
Aujourd'hui, après avoir vendu son activité d'entreprise , Docker s'est réinventé en concentrant son service d'abonnement exclusivement sur les développeurs et leurs employeurs. Docker Hub n'est plus une plate-forme à double face, c'est un simple SaaS : les clients paient des frais mensuels en échange du transfert et de l'extraction de leurs images.
Cela change le calcul. Il n'y a plus de personnage d'utilisateur « éditeur » que Docker puisse satisfaire : ils s'intéressent tous aux développeurs. Pour Docker Hub, cela ouvre la voie à la fonctionnalité d'inspection des couches d'images de Docker Scout. Pour ceux d'entre nous qui regardent de loin, cela démontre le couplage subtil et fondamental entre le modèle économique d'une startup et son offre de produits.
Également publié ici.