Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
technique:docker:general:quemu_cross_running [2020/10/15 12:28] – ajout information performances gblondtechnique:docker:general:quemu_cross_running [2021/10/16 18:19] (Version actuelle) qduchemi
Ligne 1: Ligne 1:
-{{indexmenu_n>60}}+{{indexmenu_n>70}} 
 + 
 +# QEMU : utiliser une architecture incompatible avec l'hôte 
 + 
 +<bootnote warning> 
 +Cet article est très spécifique, vous n'avez probablement pas besoin de le lire si vous voulez découvrir Docker! 
 +</bootnote>
  
-# QEMU : Utiliser des conteneurs avec une architecture a priori incompatible avec l'hôte 
 Saviez-vous que l'utilisation conjointe de Docker et QEMU permet d'utiliser par exemple un conteneur ARMv7 sur un hôte x86-AMD64 de manière (presque) totalement transparente ? Non vous ne rêvez pas, c'est même très simple à mettre en place ! Saviez-vous que l'utilisation conjointe de Docker et QEMU permet d'utiliser par exemple un conteneur ARMv7 sur un hôte x86-AMD64 de manière (presque) totalement transparente ? Non vous ne rêvez pas, c'est même très simple à mettre en place !
  
Ligne 12: Ligne 17:
 Ces noms d’architecture représentent la manière dont sont conçus et fonctionnent les microprocesseurs. La famille x86 est la plus répandue dans le monde des PC et des serveurs, il s'agit de l'ensemble des processeurs conçus par Intel et AMD, privilégiant la puissance de calcul. La famille ARM, développée par l’entreprise éponyme, privilégie l’efficacité énergétique ; elle équipe votre smartphone, les Raspberry Pi, et la plupart des objets connectés. Il existe plein d’autres architectures, comme MIPS, AVR, Risc-V… Un programme compilé pour une famille d’architecture ne fonctionnera directement sur aucune autre famille. Ces noms d’architecture représentent la manière dont sont conçus et fonctionnent les microprocesseurs. La famille x86 est la plus répandue dans le monde des PC et des serveurs, il s'agit de l'ensemble des processeurs conçus par Intel et AMD, privilégiant la puissance de calcul. La famille ARM, développée par l’entreprise éponyme, privilégie l’efficacité énergétique ; elle équipe votre smartphone, les Raspberry Pi, et la plupart des objets connectés. Il existe plein d’autres architectures, comme MIPS, AVR, Risc-V… Un programme compilé pour une famille d’architecture ne fonctionnera directement sur aucune autre famille.
  
-C’est là qu’intervient QEMU : [[https://www.qemu.org/ | QEMU]] est un logiciel libre et open-source qui permet d’émuler le comportement de n’importe quel architecture de microprocesseur, pour un programme conçu pour un système Linux ou BSD.+C’est là qu’intervient QEMU : [[https://www.qemu.org/ | QEMU]] est un logiciel libre et open-source qui permet d’émuler le comportement de n’importe quelle architecture de microprocesseur, pour un programme conçu pour un système Linux ou BSD.
  
 <bootnote question>Mais pourquoi aurait-on besoin de mélanger ces architectures ?</bootnote> <bootnote question>Mais pourquoi aurait-on besoin de mélanger ces architectures ?</bootnote>
  
-Il y a plusieurs cas d’utilisation où il est utile émuler un microprocesseur différent de celui de la machine hôte, notamment durant des phases de développement ou de test d’un programme. Imaginez que vous développez un gros logiciel compilé pour des Raspberry Pi, architecture ARMv7, mais que votre poste de travail est équipé d’un microprocesseur x86-AMD64. Il y a alors 4 manières de compiler ce programme :+Il y a plusieurs cas d’utilisation où il est utile d’émuler un microprocesseur différent de celui de la machine hôte, notamment durant des phases de développement ou de test d’un programme. Imaginez que vous développez un gros logiciel compilé pour des Raspberry Pi, architecture ARMv7, mais que votre poste de travail est équipé d’un microprocesseur x86-AMD64. Il y a alors 4 manières de compiler ce programme :
  
   * Compiler depuis une Raspberry Pi : cette solution fonctionne pour des petits projets, mais montre rapidement ses limites en matières de ressources matérielles disponibles (surtout la RAM), et est en général la plus lente des 4 solutions ;   * Compiler depuis une Raspberry Pi : cette solution fonctionne pour des petits projets, mais montre rapidement ses limites en matières de ressources matérielles disponibles (surtout la RAM), et est en général la plus lente des 4 solutions ;
Ligne 37: Ligne 42:
  
 ### Installer (temporairement) le pilote QEMU dans Docker ### Installer (temporairement) le pilote QEMU dans Docker
 +
 +Afin de pouvoir exécuter les programmes de l'architecture différente dans un conteneur, on peut enregistrer temporairement les interpréteurs sur la machine hôte pour les mettre à disposition. Cela est réalisé par la commande suivante :
 +
 +<code>
 + $ docker run --rm --priviledged multiarch/qemu-user-static --reset -p yes
 +Setting /usr/bin/qemu-alpha-static as binfmt interpreter for alpha
 +Setting /usr/bin/qemu-arm-static as binfmt interpreter for arm
 +[...]
 +Setting /usr/bin/qemu-microblazeel-static as binfmt interpreter for microblazeel
 +Setting /usr/bin/qemu-or1k-static as binfmt interpreter for or1k
 +</code>
 +
 +Veuillez noter la présence de l'option ''--privileged'' qui permet au conteneur d'interagir avec le système hôte. L'option ''--reset'' demande au script de remplacer les exécutables s'ils existent déjà, et ''-p yes'' les persiste lorsque le conteneur se ferme.
 +
 +<bootnote warning>
 +Cette commande est à exécuter à chaque fois que le démon docker est redémarré (typiquement quand la machine hôte redémarre).
 +</bootnote>
 +
 +Le script a ajouté les interpréteurs dans le dossier virtuel ''/proc/sys/fs/binfmt_misc/'' de la machine hôte. On peut vérifier leur activation :
 +
 +<code>
 +$ cat /proc/sys/fs/binfmt_misc/qemu-arm                                            
 +enabled
 +interpreter /usr/bin/qemu-arm-static
 +flags: F
 +offset 0
 +magic 7f454c4601010100000000000000000002002800
 +mask ffffffffffffff00fffffffffffffffffeffffff
 +</code>
 +
 +Cette méthode a l'avantage de ne pas nécessiter d'installer des paquets supplémentaires sur la machine hôte. Mais tout ce qui suit est tout-à-fait valable si vous avez installé qemu en global (''apt install qemu-user-static'').
  
 ### Exécuter un conteneur d’une architecture incompatible ### Exécuter un conteneur d’une architecture incompatible
 +
 +Comme indiqué dans l'introduction, ici rien ne change, on peut utiliser docker normalement. Enfin presque, car docker sélectionnera toujours l'architecture hôte pour une image disponible avec plusieurs architecture. Pour sélectionner l'architecture voulue, il faut utiliser l'option ''--platform=linux/arm64''. À ce jour (docker v19.03.13), cette option n'est disponible que en [[https://docs.docker.com/engine/reference/commandline/dockerd/#description | mode experimental]].
 +
 +Voici un exemple d'exécution :
 +
 +<code>
 +$ uname -a
 +Linux gaetan-PC 5.8.0-38-generic #43-Ubuntu SMP Tue Jan 12 12:42:13 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
 +$ docker run --rm -it arm32v7/alpine uname -a
 +Linux fcee9f02cc9a 5.8.0-38-generic #43-Ubuntu SMP Tue Jan 12 12:42:13 UTC 2021 armv7l Linux
 +$ docker run --rm -it arm64v8/alpine ls         
 +bin    etc    lib    mnt    proc   run    srv    tmp    var
 +dev    home   media  opt    root   sbin   sys    usr
 +</code>
 +
 +<bootnote>
 +Si vous obtenez l'erreur
 +
 +<code>
 +standard_init_linux.go:211: exec user process caused "exec format error"
 +</code>
 +c’est que vous avez oublié de configurer qemu !
 +</bootnote>
 +
 +À partir de maintenant, il est possible d'utiliser une image docker contenant un compilateur d'une autre architecture pour effectuer de la cross-compilation. Mais on peut encore aller plus loin : créer une image pour une autre architecture !
  
 ### Créer un conteneur pour une architecture incompatible ### Créer un conteneur pour une architecture incompatible
 +
 +De même que pour l'exécution, en cas d'ambiguïté sur l'architecture d'une image, Docker sélectionnera toujours celle de l'architecture hôte. Ainsi, il n'est pas possible de commencer une image avec
 +
 +<code dockerfile>
 +FROM scratch
 +</code>
 +
 +Cependant, on peut préciser une image exclusivement disponible pour l'architecture souhaitée, puis continuer à la construire comme on construirait une image normale. Il est aussi autorisé de mélanger les architectures — à vos risques et périls — pour une image //multi-stage//. Par exemple,
 +
 +<code dockerfile Dockerfile>
 +FROM alpine as first_stage
 +
 +RUN uname -a > /uname1.txt
 +
 +FROM arm64v8/busybox
 +
 +COPY --from=first_stage /uname1.txt /uname1.txt
 +
 +CMD echo "First stage: " && cat /uname1.txt && echo "Last stage: " && uname -a
 +</code>
 +
 +on obtient alors par exemple ces résultats :
 +
 +<code bash>
 +$ docker build -t test_docker .  
 +Sending build context to Docker daemon  2.048kB
 +Step 1/5 : FROM alpine as first_stage
 + ---> 7731472c3f2a
 +Step 2/5 : RUN uname -a > /uname1.txt
 + ---> Using cache
 + ---> 7d147fe81388
 +Step 3/5 : FROM arm64v8/busybox
 + ---> bffe63f0059e
 +Step 4/5 : COPY --from=first_stage /uname1.txt /uname1.txt
 + ---> Using cache
 + ---> 0b7ea3dd5953
 +Step 5/5 : CMD echo "First stage: " && cat /uname1.txt && echo "Last stage: " && uname -a
 + ---> Running in 33ffd9407823
 +Removing intermediate container 33ffd9407823
 + ---> b76215700cbb
 +Successfully built b76215700cbb
 +Successfully tagged test_docker:latest
 +
 +$ docker run --rm -it test_docker                                       
 +First stage: 
 +Linux cd2527490809 5.8.0-38-generic #43-Ubuntu SMP Tue Jan 12 12:42:13 UTC 2021 x86_64 Linux
 +Last stage: 
 +Linux 211b99ac36e4 5.8.0-38-generic #43-Ubuntu SMP Tue Jan 12 12:42:13 UTC 2021 aarch64 GNU/Linux
 +</code>
 +
 +De même, il est possible d'utiliser l'option ''--platform=linux/arm64'' dans l'instruction ''FROM'' si docker est en mode experimental.
 +
 +<bootnote important>
 +Suivant les clients dockers, il est possible que l'architecture indiquée dans l'image ne corresponde pas à celle réelle ; dans ce cas vous pourriez par mégarde écraser une image de l'architecture hôte au moment de pousser l'image sur un serveur multi-arch !
 +
 +Pour connaître l'architecture que docker a associé avec une image, on peut utiliser la commande suivante :
 +
 +<code bash>
 +$ docker inspect test_docker | grep Architecture
 +        "Architecture": "arm64",
 +</code>
 +
 +</bootnote>
 +
 +<bootnote>
 +En théorie, rien ne vous interdit de copier un binaire d'une architecture A dans une image d'une architecture B. Cependant, vous ne pourrez pas l'exécuter, même si qemu est configuré sur la machine hôte.
 +</bootnote>
  • technique/docker/general/quemu_cross_running.1602757703.txt.gz
  • de gblond