URL: https://linuxfr.org/users/gouttegd/journaux/ma-decouverte-de-docker Title: Ma découverte de Docker Authors: gouttegd Date: 2021年05月15日T04:25:26+02:00 License: CC By-SA Tags: docker et m1 Score: 43 ’Jour ’Nal, Aujourd’hui, je vais te parler un peu de [Docker](https://fr.wikipedia.org/wiki/Docker_(logiciel)), parce que j’ai été amené à l’utiliser récemment dans le cadre professionnel et que ça n’a pas été sans mal, alors ça mérite bien un journal. Je précise que je n’ai rien de particulier contre Docker lui-même. Je dirais même que je trouve le concept intéressant, mais en revanche je n’aime pas du tout la manière dont elle est utilisée, et notamment la tendance à ne plus fournir certains logiciels *que* sous la forme d’une image Docker. Un des outils que j’utilise dans le cadre de mon nouveau travail, l’[Ontology Development Kit](https://github.com/INCATools/ontology-development-kit) (ODK), est justement dans ce cas. En théorie, l’utilisation est simple, il suffit d’installer Docker Desktop, de faire un `docker pull obolibrary/odkfull`, après quoi l’ODK est prêt à servir. Puisque j’écris ce journal, c’est qu’évidemment en pratique ça s’est révélé plus compliqué que ça.[^1] # "Portable application development", qu’ils disaient (parce que "write once, run everywhere" était déjà pris) Dans le cadre professionnel, j’utilise un Mac Mini flambant neuf, fourni par mon employeur[^2]. Flambant neuf, ça veut dire non seulement qu’il vient avec le dernier Mac OS (11.3 « Big Sur »), mais aussi et surtout qu’il est équipé d’une de ces nouvelles puces M1[^3] (parfois appelées « Apple Silicon ») basées sur une architecture arm64, au lieu d’une puce Intel x86\_64. Je suis, semble-t-il, le premier utilisateur d’ODK au monde à essayer de l’utiliser sur un processeur M1. Tous les autres (y compris les développeurs d’ODK) ont encore des ordinateurs à puce Intel. En théorie, la différence d’architecture [n’est pas supposée poser problème](https://docs.docker.com/docker-for-mac/apple-silicon/). Docker Desktop est disponible pour M1, et même si toutes les images sur Docker Hub ne sont pas nécessairement disponibles pour cette architecture, Docker est normalement capable d’exécuter sur un processeur arm64 une image ciblant l’architecture x86\_64. « Normalement », dans la phrase précédente, signifiant qu’en principe, ça marche, mais que des fois, ça crashe :> However, attempts to run Intel-based containers on Apple Silicon machines can crash as QEMU sometimes fails to run the container. Puisque j’écris ce journal, vous vous doutez bien de quel côté je suis tombé... En effet, l’ODK, l’outil dont j’ai besoin, et qui n’est disponible sur Docker Hub que pour x86\_64 (puisque tous ses développeurs sont sur cette architecture, et tous ses utilisateurs aussi jusqu’à ce que Bibi arrive), ne fonctionne *pas* sur une machine arm64. La machine virtuelle chargée de l’émulation plante avec une bonne vieille erreur de segmentation qui ne pardonne pas. # La portabilité, c’est pas automatique Seule solution, créer une image ODK ciblant l’architecture arm64, supprimant ainsi le besoin de la couche d’émulation. Docker construit par défaut des images ciblant la même architecture que celle sur laquelle il tourne (ce qui est assez logique, c’est aussi ce que fait typiquement un compilateur par exemple : *gcc* exécuté sur une machine x86\_64 produit un binaire pour x86\_64). Du coup, en théorie construire une image ODK pour arm64 est supposé être simple, il suffit de faire `docker build .` depuis les sources d’ODK sur mon Mac Mini à puce M1. Puisque j’écris ce journal, vous vous doutez bien que la commande `docker build .` s’est terminée par une erreur. Là pour le coup, ce n’est plus réellement Docker qui est en cause, mais ODK lui-même. Je ne vais pas détailler tous les problèmes (il y en avait plusieurs), mais en gros, l’image était impossible à construire sur arm64 parce que les développeurs n’avaient pas imaginé que certains paquets dont l’image a besoin pouvaient ne pas être disponibles sous forme précompilée pour arm64. Juste un exemple pour illustrer : Une des étapes de la construction de l’image était d’installer, entre autres choses, le paquet Python `matplotlib`. Lorsque le `Dockerfile` est exécuté sous x86\_64, la commande `pip install matplotlib` trouve facilement un paquet binaire sur PYPI et l’installe comme si de rien n’était. Sous arm64 en revanche, `pip` ne trouve aucun paquet binaire de `matplotlib` pour cette architecture (tout simplement parce qu’un tel paquet n’existe pas, pour l’instant du moins), et tente donc de compiler `matplotlib` depuis les sources... ce qui échoue car certaines dépendances *non-Python* (donc non-résolvables par `pip`) ne sont pas présentes sur l’image en cours de construction. Une fois le problème compris (ce qui peut prendre un peu de temps si comme moi vous n’avez découvert Docker qu’au cours de l’heure précédente), il se règle facilement : ici, il suffisait d’installer les dépendances non-Python de `matplotlib` (avec `apt-get`, l’image ODK est basée sur une image Ubuntu) avant de tenter d’installer `matplotlib` lui-même, permettant à `pip` de compiler ce dernier si un paquet binaire n’est pas disponible. Mais il illustre un point qu’il est bon de rappeler : la portabilité, ça se travaille — ce n’est pas parce qu’une technologie *permet* de faire des trucs portables que tout ce que vous faites avec l’est forcément.[^4] Après avoir réglé un certain nombres de soucis dans le même genre, l’image peut finalement être construite jusqu’au bout, et fonctionne sur mon Mac à puce M1 comme attendu. C’était juste *un poil* plus long et *un chouia* plus compliqué que de faire `docker pull obolibrary/odkfull`, mais bon, au moins ça marche. # Les images multi-arch Parce « qu’aucun problème ne devrait avoir à être résolu deux fois » ([Eric Raymond](http://www.catb.org/~esr/faqs/hacker-howto.html#believe2)), j’ai naturellement soumis aux développeurs d’ODK mon patch permettant de construire l’image sous arm64. Ça permet au moins à quiconque souhaitant utiliser ODK sous cette architecture de construire leur propre image, à défaut de pouvoir utiliser une image pré-compilée pour arm64 disponible sur Docker Hub. Mais ce n’est évidemment guère satisfaisant et il serait éminemment préférable que les utilisateurs de Mac à puce M1 (ou de tout autre ordinateur avec une architecture arm64) puissent simplement télécharger une image d’ODK toute prête, comme le font les autres utilisateurs d’ODK depuis leurs machines x86\_64[^5]. À cette fin, Docker offre le concept d’images *multi-architectures*. C’est-à-dire que derrière un seul nom d’image et une seule étiquette (*tag*) peuvent se cacher plusieurs images distinctes pour plusieurs architectures différentes. À titre d’exemple, regardez l’image [ubuntu/nginx](https://hub.docker.com/r/ubuntu/nginx/tags?page=1&ordering=last_updated) : derrière l’étiquette `1.18-20.04_beta` se trouvent quatre images différentes, pour les architectures x86\_64 (*aka* amd64), arm64, ppc64, et s390. Quelqu’un faisant un `docker pull ubuntu/nginx` depuis une machine x86\_64 récupérera automatiquement l’image pour x86\_64, alors que quelqu’un exécutant la même commande depuis un Mac M1 récupérera l’image pour arm64. Nous allons donc voir maintenant comment créer des images *multi-arch* de ce genre. ## Installer buildx La première est d’installer et/ou activer [buildx](https://github.com/docker/buildx), un greffon de Docker spécialement conçu pour la compilation croisée d’images. Sous MacOS avec Docker Desktop dans une version récente, il n’y a en fait rien de spécial à faire, `buildx` est *déjà* installé et activé — contrairement à ce que dit la documentation, il n’est même pas nécessaire d’activer les « fonctionnalités expérimentales ». Sous GNU/Linux, `buildx` n’est pas fourni avec Docker 20.10.2, il faut l’installer soi-même : ```sh $ export DOCKER_BUILDKIT=1 $ git clone git://github.com/docker/buildx $ cd buildx $ docker build --platform=local -o . $ install -D -m 755 buildx ~/.docker/cli-plugins/docker-buildx ``` Le démon Docker doit de plus être démarré avec l’option `--experimental`. ## Permettre l’exécution de programmes ciblant une architecture étrangère La plupart du temps, pour ne pas dire systématiquement, la construction d’une image Docker implique l’exécution de commandes à l’intérieur du conteneur. Quand on construit une image « native » (ciblant la même architecture que la machine sur laquelle elle est construite), pas de problème. Mais pour construire (par exemple) une image arm64 depuis une machine x86\_64, cela nécessite de pouvoir exécuter des binaires arm64 sur un processeur x86\_64.> D’après la documentation de Docker, le meilleur moyen de construire une image arm64 depuis une machine x86\_64 est en réalité de déporter la construction vers une machine arm64, contournant ainsi complètement la difficulté. Le greffon `buildx` permet de faire ça, mais je n’ai pas exploré cette possibilité qui est de toute façon *overkill* pour les besoins d’ODK. Pour ceux qui comme moi n’ont pas envie de monter un *cluster* de machines de build (ou qui n’ont pas les moyens : il faut quand même prévoir une machine par architecture que l’on veut prendre en charge...), `buildx` permet d’utiliser une couche d’émulation qui rend possible de construire toutes les images souhaitées sur une seule et même machine. Une fois encore, sous MacOS, il n’y a rien à faire, Docker Desktop est fourni avec cette couche d’émulation déjà en place et prête à l’emploi. Sous GNU/Linux, cette couche d’émulation repose sur le mode « User » de QEMU, combiné avec le système [binfmt_misc](https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html) du noyau Linux comme [décrit ici](https://wiki.debian.org/QemuUserEmulation). En gros, l’idée est d’indiquer au noyau que les binaires arm64 (par exemple) doivent être interprétés par le programme `qemu-user-aarch64`, une version de QEMU émulant un processeur arm64. Vous pouvez mettre en place cette couche d’émulation vous-même si vous le souhaitez : installez les versions de QEMU émulant les architectures qui vous intéressent, puis utilisez le script `qemu-binfmt-conf.sh` (fourni par QEMU) pour configurer le système `binfmt_misc`.> Attention : Il est important que les différents émulateurs QEMU pour les différentes architectures soient compilés *statiquement* ! Si vous êtes fainéant, et puisqu’à ce point on est déjà vendu à Docker de toute façon, vous pouvez aussi procéder de la manière suivante : ```sh $ docker pull multiarch/qemu-user-static $ docker run --rm privileged multiarch/qemu-user-static --reset -p yes ``` La deuxième commande installera automatiquement la couche d’émulation pour un certain nombre d’architectures (comme arm64, arm/v6, arm/v7, s390, ppc64, riscv64). Attention, contrairement à une installation « manuelle », cette couche n’est pas permanente : elle ne survit pas à un redémarrage de la machine, pour la rétablir après un redémarrage il faut relancer la deuxième commande une fois le démon Docker disponible. ## Créer un builder multi-arch On y est presque. La dernière étape est de créer un *builder* dédié aux compilations croisées : ``` $ docker buildx create --name multiarch --driver docker-container --use ``` L’option `--driver docker-container` indique que ce *builder* tournera lui-même dans un conteneur Docker. C’est nécessaire pour la compilation croisée d’images ; le *builder* par défaut, qui s’exécute dans l’environnement « normal » de l’hôte, ne peut construire que des images ciblant l’architecture native. Et voilà, `buildx` est maintenant prêt à servir. Il fournit une commande `build` qui s’utilise similairement à la commande `build` standard de Docker, mais qui accepte une option `--platform` indiquant les plates-formes pour lesquelles généner une image. Par exemple, pour créer une image ciblant à la fois x86\_64 et arm64 : ``` $ docker buildx build --platform linux/amd64,linux/arm64 -t example/myimage:v1.0.0 --push . ``` L’option `--push` instruit Docker de publier immédiatement les images nouvellement générées sur Docker Hub. Et donc, comme `buildx` semble effectivement fonctionner exactement comme ses auteurs disent qu’il est censé fonctionner[^6], après tout cela j’ai le plaisir d’annoncer que dans le futur, les images d’ODK seront disponibles en version x86\_64 *et* en version arm64 (voire même, pendant qu’on y est pourquoi pas, arm/v6, des fois que quelqu’un veuille l’utiliser sur un Raspberry Pi). [^1]: C’est déjà arrivé que quelqu’un écrive un journal sur un truc informatique qui fonctionne exactement comme ses promoteurs disent qu’il est censé fonctionner ? [^2]: J’ai bien tenté de demander un PC sous GNU/Linux, sans succès : le service IT préfère un parc uniforme, donc c’est MacOS pour tout le monde. [^3]: Alors que tous mes collègues sont au mieux sous MacOS 10.15 avec des processeurs Intel, autant pour l’uniformité du parc... [^4]: Il est très facile d’écrire du code Java qui ne fonctionnera que sous un seul système par exemple. [^5]: *A priori* la plupart des utilisateurs d’ODK sont des scientifiques, pas des développeurs. [^6]: Comme quoi, ça arrive, en fait.