Pourquoi utiliser Docker en tant que dev ?

Read the article in English

Aujourd’hui, je réalise qu’il y a un réel engouement autour de la conteneurisation, et plus particulièrement de Docker. On ne cesse de me rabacher qu’en tant que dev, cela pourrait me faciliter de nombreuses tâches au quotidien. Mais comment ?

Tout d’abord, quelles sont les différences entre une VM et un conteneur ?

Différences entre VMs et conteneurs

Aujourd’hui, je travaille sur Docker. C’est l’outil qui a démocratisé la conteneurisation et qui est le plus répandu. De plus, de nombreux outils open source se basent dessus, tels que Molecule (qui permet de tester du code Ansible), ou Kubernetes (un orchestrateur de conteneurs).

Pour débuter, il y a deux objets de base à connaître : l’image et le conteneur. Une image est une "photo" de l'état souhaité de notre serveur en production : code et dépendances. C'est à partir de cette image que l'on génère des conteneurs, des "boîtes" où tourne notre code, que l’on utilise puis que l’on détruit.

Si on peut tenter une analogie avec la programmation orientée objet : une image est l’équivalent d’une classe et un conteneur est l’équivalent d’une instance de cette classe.

De plus, la philosophie des conteneurs est assez simple :

  • un processus par conteneur
  • un conteneur est immutable
  • un conteneur est jetable

Un environnement de dev iso-prod

"Un bug ? En tous cas, ça marche chez moi !"

Que l’on soit Dev ou Ops, je pense que tout le monde a déjà entendu le fameux adage “ça marche sur mon poste”. Quand ce genre de problème survient, il se situe souvent dans les différences entre les configurations (soucis de versions, de droits) qui peuvent être difficiles à déboguer.

Un premier pas dans l’uniformisation des environnements a été entrepris avec l’introduction des machines virtuelles. Malheureusement, elles nécessitent les mêmes opérations que les machines de développement (mise à jour, configuration …).

Si mes applications sont stateless, configurables par variables d’environnement et si je respecte les standards de 12factor, alors mes conteneurs seront iso-fonctionnels avec les mêmes variables d’environnement. Cela implique que si les tests sont validés pour un conteneur alors je serai assuré que tous les conteneurs issus de la même image auront le même comportement. Car oui, utiliser un conteneur ne nous exempt pas de poser des tests !

Ainsi, l’image produite testée est utilisée en production, et on est sûrs qu’elle aura le comportement attendu. De même, si un bug se produit en production, il me suffit d'instancier un conteneur à partir de cette image avec la configuration de production pour être sûr de pouvoir le reproduire.

L'image d'un conteneur est utilisée de l'environnement de dev jusqu'à la prod

A noter que même si notre production ne fait pas tourner de conteneurs, on peut tout à fait en utiliser pour faciliter le développement de l’application. Cependant, il faut garder en mémoire que l’introduction de Docker nécessitera un apprentissage supplémentaire. Faut-il alors favoriser un développement avec une architecture simple ou se rapprocher au plus tôt de la CI/prod et viser la reproductibilité ? À vous de décider !

Une simplification de certaines de mes tâches au quotidien

"Cela rend l’onboarding tellement plus rapide !"

Je démarre un nouveau projet au sein d’une équipe qui utilise Docker ? Hop, j’installe Docker, je démarre le container avec la dernière version du code en cours de développement, et c’est parti ! Je suis sûr d’avoir les mêmes versions des outils que mes collègues, quel que soit l’OS sur mon poste de travail !

De plus, les montées de version sont facilitées pour toute l’équipe, et on est sûrs que tout le monde est à jour.

"Je ne veux pas polluer mon poste de dev avec tous les outils que je télécharge pour mes projets"

En tant que développeur, j’ai eu l’occasion de travailler sur plusieurs projets, et donc d’accumuler divers outils. Je me suis rapidement retrouvé confronté à un problème : j’ai plusieurs versions des mêmes outils installées sur ma machine. J'ai par exemple deux versions de PostgreSQL d’installées. Si je change de projet, je dois en désactiver une et activer l'autre. Cela me fait perdre du temps et je ne suis jamais sûr d'avoir tout bien désinstallé si je dois en supprimer une.

Disposer d'une base de données conteneurisée règle mon soucis : c’est simple à installer et à utiliser, et je suis sûr de tout supprimer à la destruction de mon conteneur !

Emprunter les outils du voisin

Prenons un exemple, je suis sur Mac et j’aurais bien aimé utiliser un outil sur Linux bien pratique : ipcalc. Avant, je me serais juste résigné et j’aurais cherché un outil similaire, mais avec un conteneur, je peux disposer de cet outil simplement !

Output de la commande ipcalc

"Je ne veux pas réinventer la roue"

L’un des avantages que j’ai découvert avec les conteneurs est celui de disposer de solutions “sur étagère”. En effet, il n’est pas pertinent de recoder une nouvelle solution à un problème déjà connu.

Il existe plusieurs endroits (on parle de registry), où trouver des images à partir desquelles instancier des conteneurs. Utilisant Docker, je suis allé sur https://hub.docker.com/ pour découvrir toutes les images disponibles et comment les utiliser. La plupart des images étant open-sources, on peut aussi contribuer à leur amélioration.

Image disponible sur le Docker Hub

Des indicateurs permettent de faire un rapide tri pour choisir : le nombre d’étoiles (données par les utilisateurs), le nombre de téléchargements de l’image, les dates de mises à jour et du dernier téléchargement.

Attention tout de même : il reste important de vérifier le contenu des images à utiliser si l’on ne souhaite pas lancer des processus malveillants (backdoored docker images).

Mais comment faire quand on n’a pas envie de publier ses images publiquement sur Docker ? Pour ça, on peut créer soi-même sa registry à partir d’une image docker (exemple : https://docs.docker.com/registry/) et déployer ses images dessus.

"Je peux réaliser mes POCs en toute sérénité"

Je peux travailler sur mes projets sans avoir peur de télécharger des packages inutiles lorsque j’ai un fichier à compiler, ou de télécharger un outil qui rentre en conflit avec des outils déjà existants sur ma machine. Il me suffit d’utiliser un conteneur et de le détruire à la fin pour laisser ma machine impeccable.

Je suis aussi plus à l’aise avec l’idée de faire des POCs, ou de tester de nouveaux outils, dans la mesure où je peux les installer dans un conteneur et supprimer ce dernier dès que je n’en ai plus besoin. C’est rapide - démarrer ou supprimer un conteneur ne prend que quelques secondes - et il me permet de simuler l’environnement exact sur lequel je souhaite travailler. Je peux aussi simplement simuler des appels à une base ou à une API. Si cela ne marche pas, il me suffit de quelques secondes pour détruire mon conteneur et en remonter un identique tout neuf !

Cela me réduit considérablement la boucle de feedback

"Mon appli est tellement compliquée à tester !"

Sur la plupart de mes projets, j’ai une application avec sa base de données et une API qu’elle consomme. J’aimerais pouvoir tester toutes ces interactions : cela reste faisable sur mon poste, mais sur une intégration continue, cela devient compliqué.

On l’a vu, Docker me permet de rapidement monter ma base de données. De la même manière, si j’ai la main sur l’API, je peux créer son image pour la lancer dans un conteneur. Je dispose ainsi en quelques secondes d’une API de test, utilisable en intégration continue. Si je n’ai pas la main sur cette dépendance, je peux toujours créer un mock de cette API et l'utiliser de la même manière.

Docker me permet rapidement et simplement de recréer la topologie de mon projet.

Utilisation de conteneurs en tests

"Mon appli est tellement longue à tester !"

Jusqu’à présent, lorsque je voulais tester mon code dans un environnement similaire à celui  de prod, je demandais au client de me fournir une VM. Cela pouvait prendre plusieurs semaines. Quelle perte de temps ! Et si je m’étais trompé et que j’avais besoin de détruire et de relancer une nouvelle VM, rebelote - et cela occasionne des frais supplémentaires.

Un conteneur me permet de faire tout cela sans avoir à passer par un intermédiaire, et de valider le fonctionnement de mon application et de ses dépendances.  Il faut garder à l’esprit qu’une VM est plus longue à monter et à détruire qu’un conteneur car elle réserve une partie des ressources de la machine hôte à son lancement, alors qu’un conteneur ne consomme que ce dont il a besoin.

Conclusion

Les avantages

Dans l’ensemble, en tant que développeur, l’utilisation de conteneurs peut me faciliter la vie.

  • Cela facilite le partage d’un même environnement entre devs.
  • Les dépendances seront les mêmes peu importe l’environnement. (Je suis sûr d’avoir la même version des outils que mes collègues) ?
  • Docker est cross-plateforme : les différences d’OS entre dev ne sont plus un soucis
  • Je travaille sur un environnement cloisonné.
  • Je dispose de solutions sur étagère grâce aux images open source à disposition.
  • Je peux faire des POCs rapides grâce aux images Docker sans polluer ma machine.
  • C’est plus rapide à démarrer/arrêter qu’une VM.
  • Cela accélère considérablement la boucle de feedback et la livraison en production.
  • Je suis contraint à de bonnes pratiques de dev : 12factor

À prendre en considération

Attention cependant à ne pas se lancer tête baissée dans du Docker !

  • On ne peut pas mettre n’importe quelle application dans un conteneur : il faut que celle-ci respecte les règles du 12factor app.
  • Docker propose beaucoup de fonctionnalités qu’il faut expérimenter avant de créer des topologies complexes.
  • Docker peut être moins pertinent sur des petits projets.
  • Docker introduit une complexité supplémentaire : le soucis peut venir de ma configuration, de ma topologie Docker ou de mon application.
  • Les conteneurs se basent sur le noyau de l’OS de ma machine :
    • Si j’utilise des fonctions particulières du noyau, la compatibilité sur une autre machine n’est pas assurée (ex : Docker Windows et Docker Linux ne sont pas compatibles)
    • Un processus conteneurisé peut arriver à exploiter une faille du noyau et impacter les autres processus de la machine hôte.

Enfin, il faut garder à l’esprit que Docker n’est qu’un outil parmi d’autres. On peut citer RKT (CoreOS), ou encore Windows Containers (Microsoft) par exemple. À vous de faire votre choix !

Sources

À toi de jouer !