Réfléchissez bien avant d'utiliser Docker-in-Docker pour CI ou environnement de test

Réfléchissez bien avant d'utiliser Docker-in-Docker pour CI ou environnement de test

Docker-in-Docker est un environnement démon Docker virtualisé s'exécutant dans le conteneur lui-même pour créer des images de conteneur. L'objectif principal de la création de Docker-in-Docker était d'aider au développement de Docker lui-même. De nombreuses personnes l'utilisent pour exécuter Jenkins CI. Cela semble normal au début, mais des problèmes surviennent ensuite et peuvent être évités en installant Docker dans un conteneur Jenkins CI. Cet article vous explique comment procéder. Si vous êtes intéressé par la solution finale sans détails, lisez simplement la dernière section de l'article, « Résoudre le problème ».

Réfléchissez bien avant d'utiliser Docker-in-Docker pour CI ou environnement de test

Docker-in-Docker : "Bien"

Il y a plus de deux ans, j'ai installé Docker drapeau –privilégié et écrit première version de dind. L'objectif était d'aider l'équipe principale à développer Docker plus rapidement. Avant Docker-in-Docker, le cycle de développement typique ressemblait à ceci :

  • piratage informatique ;
  • construire;
  • arrêter un démon Docker en cours d'exécution ;
  • lancer un nouveau démon Docker ;
  • tests;
  • répéter le cycle.

Si vous vouliez réaliser un bel assemblage reproductible (c'est-à-dire dans un conteneur), alors cela devenait plus complexe :

  • piratage informatique ;
  • assurez-vous qu'une version fonctionnelle de Docker est en cours d'exécution ;
  • construire un nouveau Docker avec l'ancien Docker ;
  • arrêter le démon Docker ;
  • démarrer un nouveau démon Docker ;
  • test;
  • arrêter le nouveau démon Docker ;
  • répéter.

Avec l'avènement de Docker-in-Docker, le processus est devenu plus simple :

  • piratage informatique ;
  • assemblage + lancement en une seule étape ;
  • répéter le cycle.

N'est-ce pas beaucoup mieux ainsi ?

Réfléchissez bien avant d'utiliser Docker-in-Docker pour CI ou environnement de test

Docker-in-Docker : "Mauvais"

Cependant, contrairement aux idées reçues, Docker-in-Docker, ce n'est pas à 100% stars, poneys et licornes. Ce que je veux dire, c'est qu'un développeur doit être conscient de plusieurs problèmes.

L'un d'eux concerne les LSM (modules de sécurité Linux) tels qu'AppArmor et SELinux : lors de l'exécution d'un conteneur, le « Docker interne » peut tenter d'appliquer des profils de sécurité qui entreront en conflit ou confondront le « Docker externe ». C’est le problème le plus difficile à résoudre lorsqu’on essaie de fusionner l’implémentation originale de l’indicateur –privileged. Mes modifications ont fonctionné et tous les tests étaient réussis sur ma machine Debian et mes machines virtuelles de test Ubuntu, mais ils plantaient et brûlaient sur la machine de Michael Crosby (il avait Fedora si je me souviens bien). Je ne me souviens pas de la cause exacte du problème, mais c'est peut-être parce que Mike est un gars malin qui travaille avec SELINUX=enforce (j'ai utilisé AppArmor) et que mes modifications n'ont pas pris en compte les profils SELinux.

Docker-in-Docker : "Mal"

Le deuxième problème concerne les pilotes de stockage Docker. Lorsque vous exécutez Docker-in-Docker, Docker externe s'exécute sur un système de fichiers standard (EXT4, BTRFS ou tout ce que vous avez) et Docker interne s'exécute sur un système de copie sur écriture (AUFS, BTRFS, Device Mapper , etc.). , selon ce qui est configuré pour utiliser Docker externe). Cela crée de nombreuses combinaisons qui ne fonctionneront pas. Par exemple, vous ne pourrez pas exécuter AUFS par-dessus AUFS.

Si vous exécutez BTRFS par-dessus BTRFS, cela devrait fonctionner au début, mais une fois qu'il y aura des sous-volumes imbriqués, la suppression du sous-volume parent échouera. Le module Device Mapper n'a pas d'espace de noms, donc si plusieurs instances Docker l'exécutent sur la même machine, elles pourront toutes voir (et influencer) les images les unes sur les autres et sur les périphériques de sauvegarde du conteneur. C'est mauvais.

Il existe des solutions de contournement pour résoudre bon nombre de ces problèmes. Par exemple, si vous souhaitez utiliser AUFS dans Docker interne, transformez simplement le dossier /var/lib/docker en volume et tout ira bien. Docker a ajouté des espaces de noms de base aux noms de cibles de Device Mapper afin que si plusieurs appels Docker sont exécutés sur la même machine, ils ne se marcheront pas les uns sur les autres.

Cependant, une telle configuration n’est pas du tout simple, comme le montrent ces articles dans le dépôt dind sur GitHub.

Docker-in-Docker : c'est pire

Qu'en est-il du cache de build ? Cela peut aussi être assez difficile. Les gens me demandent souvent « si j'utilise Docker-in-Docker, comment puis-je utiliser les images hébergées sur mon hôte au lieu de tout récupérer dans mon Docker interne » ?

Certaines personnes entreprenantes ont essayé de lier /var/lib/docker de l'hôte à un conteneur Docker-in-Docker. Parfois, ils partagent /var/lib/docker avec plusieurs conteneurs.

Réfléchissez bien avant d'utiliser Docker-in-Docker pour CI ou environnement de test
Voulez-vous corrompre vos données ? Car c’est justement cela qui va endommager vos données !

Le démon Docker a été clairement conçu pour avoir un accès exclusif à /var/lib/docker. Rien d'autre ne doit « toucher, pousser ou produire » les fichiers Docker situés dans ce dossier.

Pourquoi cela est-il ainsi? Parce que c'est le résultat de l'une des leçons les plus difficiles apprises lors du développement de dotCloud. Le moteur de conteneur dotCloud fonctionnait en ayant plusieurs processus accédant simultanément à /var/lib/dotcloud. Des astuces astucieuses telles que le remplacement atomique de fichiers (au lieu de l'édition sur place), le parsèment du code avec des verrous consultatifs et obligatoires et d'autres expériences avec des systèmes sécurisés tels que SQLite et BDB n'ont pas toujours fonctionné. Lorsque nous avons repensé notre moteur de conteneurs, qui est finalement devenu Docker, l'une des grandes décisions de conception a été de consolider toutes les opérations de conteneurs sous un seul démon afin d'éliminer toutes les absurdités de la concurrence.

Ne vous méprenez pas : il est tout à fait possible de créer quelque chose de bon, fiable et rapide qui implique plusieurs processus et un contrôle parallèle moderne. Mais nous pensons qu'il est plus simple et plus facile d'écrire et de maintenir du code en utilisant Docker comme seul lecteur.

Cela signifie que si vous partagez le répertoire /var/lib/docker entre plusieurs instances Docker, vous rencontrerez des problèmes. Bien sûr, cela peut fonctionner, surtout dans les premières étapes des tests. « Écoute, maman, je peux exécuter Ubuntu en tant que Docker ! » Mais essayez quelque chose de plus complexe, comme extraire la même image de deux instances différentes, et vous verrez le monde brûler.

Cela signifie que si votre système CI effectue des builds et des reconstructions, chaque fois que vous redémarrez votre conteneur Docker-in-Docker, vous risquez de déposer une bombe nucléaire dans son cache. Ce n'est pas cool du tout !

La solution

Prenons du recul. Avez-vous vraiment besoin de Docker-in-Docker ou voulez-vous simplement pouvoir exécuter Docker et créer et exécuter des conteneurs et des images à partir de votre système CI pendant que ce système CI lui-même est dans un conteneur ?

Je parie que la plupart des gens souhaitent cette dernière option, ce qui signifie qu'ils souhaitent qu'un système CI tel que Jenkins soit capable d'exécuter des conteneurs. Et le moyen le plus simple de le faire est simplement d'insérer un socket Docker dans votre conteneur CI et de l'associer à l'indicateur -v.

En termes simples, lorsque vous exécutez votre conteneur CI (Jenkins ou autre), au lieu de pirater quelque chose avec Docker-in-Docker, démarrez-le avec la ligne :

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Ce conteneur aura désormais accès au socket Docker et pourra donc exécuter des conteneurs. Sauf qu’au lieu d’exécuter des conteneurs « enfants », il lancera des conteneurs « frères ».

Essayez ceci en utilisant l'image Docker officielle (qui contient le binaire Docker) :

docker run -v /var/run/docker.sock:/var/run/docker.sock 
           -ti docker

Il ressemble et fonctionne comme Docker-in-Docker, mais ce n'est pas Docker-in-Docker : lorsque ce conteneur crée des conteneurs supplémentaires, ils seront créés dans le Docker de niveau supérieur. Vous ne rencontrerez pas les effets secondaires de l’imbrication et le cache d’assembly sera partagé sur plusieurs appels.

Remarque : les versions précédentes de cet article conseillaient de lier le binaire Docker de l'hôte au conteneur. Ceci est désormais devenu peu fiable car le moteur Docker ne couvre plus les bibliothèques statiques ou quasi-statiques.

Ainsi, si vous souhaitez utiliser Docker depuis Jenkins CI, vous avez 2 options :
installer la CLI Docker à l'aide du système de packaging d'images de base (c'est-à-dire si votre image est basée sur Debian, utilisez les packages .deb), à l'aide de l'API Docker.

Quelques publicités 🙂

Merci de rester avec nous. Vous aimez nos articles ? Vous voulez voir du contenu plus intéressant ? Soutenez-nous en passant une commande ou en recommandant à vos amis, cloud VPS pour les développeurs à partir de 4.99 $, un analogue unique des serveurs d'entrée de gamme, que nous avons inventé pour vous : Toute la vérité sur le VPS (KVM) E5-2697 v3 (6 Cores) 10Go DDR4 480Go SSD 1Gbps à partir de 19$ ou comment partager un serveur ? (disponible avec RAID1 et RAID10, jusqu'à 24 cœurs et jusqu'à 40 Go de DDR4).

Dell R730xd 2 fois moins cher dans le centre de données Equinix Tier IV à Amsterdam ? Ici seulement 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV à partir de 199$ aux Pays-Bas! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - à partir de 99$ ! En savoir plus Comment construire une infrastructure corp. classe avec l'utilisation de serveurs Dell R730xd E5-2650 v4 qui valent 9000 XNUMX euros pour un sou ?

Source: habr.com

Ajouter un commentaire