Comment j'ai exécuté Docker dans Docker et ce qui en est ressorti

Salut tout le monde! Dans son article précédent, j'ai promis de parler de l'exécution de Docker dans Docker et des aspects pratiques de l'utilisation de cette leçon. Il est temps de tenir votre promesse. Un développeur expérimenté objectera probablement que ceux qui ont besoin de Docker dans Docker transfèrent simplement le socket du démon Docker de l'hôte vers le conteneur et cela sera suffisant dans 99% des cas. Mais ne vous précipitez pas pour me lancer des cookies, car nous parlerons de l'exécution réelle de Docker dans Docker. Cette solution a de nombreuses applications possibles et cet article concerne l'une d'entre elles, alors asseyez-vous et tendez vos bras devant vous.

Comment j'ai exécuté Docker dans Docker et ce qui en est ressorti

début

Tout a commencé un soir pluvieux de septembre alors que je nettoyais la machine que j'avais louée pour 5 $ sur Digital Ocean, qui était gelée car Docker avait rempli les 24 Go d'espace disque disponible avec ses images et ses conteneurs. L'ironie était que toutes ces images et conteneurs étaient éphémères et n'étaient nécessaires que pour tester les performances de mon application à chaque fois qu'une nouvelle version d'une bibliothèque ou d'un framework était publiée. J'ai essayé d'écrire des scripts shell et de mettre en place une planification cron pour nettoyer les déchets, mais cela n'a pas aidé : à chaque fois, cela se terminait inévitablement par une consommation d'espace disque de mon serveur et une suspension du serveur (au mieux). À un moment donné, je suis tombé sur un article sur la façon d'exécuter Jenkins dans un conteneur et comment il peut créer et supprimer des pipelines de construction via un socket de démon Docker qui y est transféré. J'ai aimé l'idée, mais j'ai décidé d'aller plus loin et d'essayer d'expérimenter l'exécution directe de Docker dans Docker. À cette époque, il me semblait une solution tout à fait logique de télécharger des images Docker et de créer des conteneurs pour toutes les applications dont j'avais besoin pour tester dans un autre conteneur (appelons-le un conteneur intermédiaire). L'idée était de démarrer un conteneur intermédiaire avec l'indicateur -rm, qui supprime automatiquement l'intégralité du conteneur et tout son contenu lorsqu'il est arrêté. J'ai bricolé l'image Docker de Docker lui-même (https://hub.docker.com/_/docker), mais il s'est avéré trop encombrant et je n'ai jamais réussi à le faire fonctionner comme j'en avais besoin et j'ai voulu aller jusqu'au bout moi-même.

Pratique. Cônes

J'ai décidé de faire fonctionner le conteneur comme j'en avais besoin et j'ai continué mes expériences, qui ont abouti à une myriade de têtes. Le résultat de mon auto-torture était l’algorithme suivant :

  1. Nous lançons le conteneur Docker en mode interactif.

    docker run --privileged -it docker:18.09.6

    Faites attention à la version du conteneur, avancez à droite ou à gauche et votre DinD se transforme en citrouille. En fait, les choses se cassent assez souvent lorsqu'une nouvelle version sort.
    Il faut immédiatement rentrer dans la coquille.

  2. Nous essayons de savoir quels conteneurs sont en cours d'exécution (Réponse : aucun), mais exécutons quand même la commande :

    docker ps

    Vous serez un peu surpris, mais il s'avère que le démon Docker ne fonctionne même pas :

    error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 
    192.168.65.1:53: no such host

  3. Exécutons-le nous-mêmes :

    dockerd &

    Autre mauvaise surprise :

    failed to start daemon: Error initializing network controller: error obtaining controller instance: failed 
    to create NAT chain DOCKER: Iptables not found

  4. Installez les packages iptables et bash (tout est plus agréable à travailler en bash qu'en sh) :

    apk add --no-cache iptables bash

  5. Lançons bash. Enfin nous sommes de retour dans la coquille habituelle

  6. Essayons d'exécuter à nouveau Docker :

    dockerd &

    Nous devrions voir une longue feuille de logs se terminant par :

    INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization          
    INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sock

  7. Appuyez sur Entrée. Nous sommes de retour à la fête.

À partir de maintenant, nous pouvons essayer de lancer d'autres conteneurs à l'intérieur de notre conteneur Docker, mais que se passe-t-il si nous voulons lancer un autre conteneur Docker à l'intérieur de notre conteneur Docker ou si quelque chose ne va pas et que le conteneur plante ? Recommencer à zéro.

Propre conteneur DinD et nouvelles expériences

Comment j'ai exécuté Docker dans Docker et ce qui en est ressorti
Pour éviter de répéter encore et encore les étapes ci-dessus, j'ai créé mon propre conteneur DinD :

https://github.com/alekslitvinenk/dind

La solution DinD fonctionnelle m'a donné la possibilité d'exécuter Docker dans Docker de manière récursive et de faire des expériences plus aventureuses.
Je vais maintenant décrire une de ces expériences (réussies) avec l'exécution de MySQL et Nodejs.
Les plus impatients peuvent voir comment ça s'est passé ici

Commençons donc:

  1. Nous lançons DinD en mode interactif. Dans cette version de DinD, nous devons mapper manuellement tous les ports que nos conteneurs enfants peuvent utiliser (je travaille déjà là-dessus)

    docker run --privileged -it 
    -p 80:8080 
    -p 3306:3306 
    alekslitvinenk/dind

    Nous entrons dans la fête, d'où nous pouvons immédiatement commencer à lancer des conteneurs enfants.

  2. Lancez MySQL :

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql

  3. Nous nous connectons à la base de données de la même manière que nous nous y connecterions localement. Assurons-nous que tout fonctionne.

  4. Lancez le deuxième conteneur :

    docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server

    Veuillez noter que le mappage des ports sera exactement 8080:8080, puisque nous avons déjà mappé le port 80 de l'hôte vers le conteneur parent vers le port 8080.

  5. Nous allons sur localhost dans le navigateur, assurez-vous que le serveur répond « Hello World ! »

Dans mon cas, l'expérience avec les conteneurs Docker imbriqués s'est avérée plutôt positive et je continuerai à développer le projet et à l'utiliser pour la mise en scène. Il me semble que c'est une solution beaucoup plus légère que Kubernetes et Jenkins X. Mais c'est mon opinion subjective.

Je pense que c'est tout pour l'article d'aujourd'hui. Dans le prochain article, je décrirai plus en détail des expériences d'exécution récursive de Docker dans Docker et de montage de répertoires profondément dans des conteneurs imbriqués.

PS Si vous trouvez ce projet utile, donnez-lui une étoile sur GitHub, partagez-le et parlez-en à vos amis.

Edit1 Erreurs corrigées, concentrées sur 2 vidéos

Source: habr.com

Ajouter un commentaire