Un retour sur Docker

Bonjour …. ah … y a plus personne de vivant ici !

Ca fait plusieurs années que je ne me suis pas dérouillé les phalanges sur un article, mais il n’est jamais trop tard pour s y remettre. Allons-y!

Un retour sur Docker

Après avoir utilisé, dans un début d’environnement d’intégration, l’outil Docker, comme plateforme pour nos microservices Java, je vais ici faire un retour sur les points positifs et négatifs que j’ai pu constater sur Docker et sur les outils cousins (Docker-Compose, Kubernetes, Rancher, …). Si vous ne connaissez pas les outils dont je parle, je vous invite a jeter un œil avant, même si je vais expliquer tout cela par la suite, ça ne sera clairement pas exhaustif et vous risquez d’être perdus.

Présentation de notre ami Docker

Commençons par expliquer un peu l’outil et la hype qu’il a attisée. Docker, ce n’est pas un concept nouveau (ICTX et Solaris Container existaient avant, et remplissaient le même rôle). Mais ce sont les plus malins, qui ont réussi à vendre à grande échelle leurs idées et leur marque.

Le principe de base du problème, était de répondre à un déploiement facilité dans un monde ou la variété des langages et des OS s’étend de jour en jour. Question:  100 apps en C#, Java, c++ et NodeJS veulent être déployées avec des bases PostgreSQL, Elasticsearch et Redis pour faire fonctionne une seule et même application. Comment allez-vous versionner dans une équipe de 300 developpeurs & devops, maintenir et déployer en continu sur 3 environnements de dev, pre-prod et prod ? Réponse : Docker (+ quelques outils indispensables qu’on verra après).

Si on prend un exemple dans la vie de tous les jours : les conteneurs physiques on étés créés pour répondre au besoin de transporter X types de marchandises de manière uniformisée, dans des moyens de transport différents avec des moyens de préhension et de manutention différents.

standard-problem

 

Et bien si on regarde l’image ci-dessous, on retrouve a peu près le même principe : on peut embarquer n’importe quel type d’application, et déployer et scaler (mettre à l’échelle) à l’infini. Le seul prérequis est d’avoir des OS hôtes de distribution Linux.

programming-problem

Ils ont clairement gagné la bataille de la conteneurisation pour un bon moment dans le monde de l’IT et écrasent de loin les autres qui sont dans le même domaine. La bataille de la quoi ? La conteneurisation (ou « contenerization » ?), c’est un concept qui vise à surpasser la virtualisation, que vous connaissez déjà, en simplifiant et en optimisant les ressources des machines hôtes.

Voici un petit schéma qui devrait vous permettre de comparer la contenerization avec la virtualization:

A gauche, la virtualisation et à droite, la conteneurisation.

cvsvPar rapport à des VM, on voit que les containers utilisent tous les fonctionnalités de l’OS hôte, et pour Docker, en l’occurrence, ils utilisent le kernel Linux. Ensuite, le Docker Daemon (l’engine) s’occupe d’exécuter les instructions nécessaires à la vie de chacun. Sur de la virtualisation, on voit que c’est plus lourd, que chaque VM embarque sont OS invité, et que l’hyperviseur vient rajouter une couche entre l’OS hôte et les VM. Il y a plus de couches, les VM ne se partagent rien, et pompent de la RAM et du stockage à foison.

Voila donc le concept, comme tout est partagé et optimisé, on a des temps de redémarrage plus courts, et une migration vers d’autres serveurs plus rapides (grosse économie de bande passante pour le contenu a télécharger, vous verrez plus loin !).

Docker est séparé en trois parties : il possède son moteur (le Docker Daemon) dont j’ai parlé un peu plus haut, son API REST, qui permet d’envoyer les ordres au Docker Daemon, et enfin, la Docker CLI (Command Line Interface) qui permet de scripter/d’intéragir directement depuis la console vers le moteur Docker.

Docker ne fonctionne donc pas sur Windows ?

Et bien si. Je vous ai dit avant qu’il se basait obligatoirement sur un kernel Linux, mais il se trouve que cela fonctionne sur Windows via le biais d’une VM Linux. En fait, pour être plus précis, sur Windows 7, l’outil s’appelle Docker Toolbox et créer une VM a l’aide de VirtualBox, VM qui va héberger le docker daemon ainsi que l’adressage des containers. Sur Windows 10, on utilise l’hyperviseur built-in : j’ai nommé HyperV. Il faut juste activer l’option via le BIOS et on peut utiliser Docker for Windows, qui va gentiment nous créer une VM HyperV et binder son adresse à notre boucle locale. La VM Linux est va donc servir de base de référence pour le Docker Engine.

Tu parles de containers, mais je vois pas trop ce que sa représente

Un container est l’instance d’une image. Lol, de rien. Je plaisante, je vais expliquer un peu mieux que ça:

Les 4 éléments principaux que l’on manipule sur Docker sont :

  • Les containers
  • Les images
  • Les volumes
  • Les networks

Pour comprendre ce qu’est un container, comprenons d’abord les images.

Une image est votre livrable, en tant que dev. Elle est construite avec des couches, et elle peut se baser sur une autre image existante. Les images qui servent de base de construction sont appelées des « base images ».

Pour se construire, on leur fournit un fichier d’instructions que la commande « docker build » va se charger de comprendre et d’exécuter. Ce fichier s’appelle le Dockerfile.

Ensuite, notre image est disponible dans notre registry local. On pourra plus tard la publier, mais ça, c’est la partie déploiement et c’est un peu plus loin.

Voici a quoi ressemble un Dockerfile :

dockerfile_ex

Ici, on spécifie que notre image doit se baser sur celle existante et disponible sur le registry public de docker (Docker Hub) « java:8u111-jre-alpine ». On lui indique également de copier notre *.jar dans l’image, avec le nom « app.jar ».

Ensuite, on lui dit d’exposer le port 8080 et enfin de lancer le jar avec la commande « java -jar /app.jar ».

L’avantage est que ce Dockerfile est donc très facile à versionner et permet de manière relativement intuitive, de comprendre comment nos images Docker sont construites.

Lorsque j’exécute ensuite un « docker run -d "monimage", Docker va s’occuper de créer un container, et va instancier mon image à l’intérieur. Voila donc l’explication, les containers sont des coquilles qui permettent de gérer ensuite le cycle de vie de notre image qui y réside (en tout cas, je le vois comme ça, et je trouvais ça assez joliment dit, alors vous avez dû opiner du chef :p )

Les volumes et les networks

Maintenant parlons un peu des volumes et des networks. Les volumes sont des points de montage d’un répertoire de l’hôte dans le container que l’image exploite. En gros, on créé un répertoire partagé entre l’hôte et le container. Cela permet de persister de la donnée, car lors du lancement d’une image, celle-ci sera toujours reset et purgée des modifications qu’elle aura subi au cours de son exécution.

Vous l’aurez compris, cela nous est très utile lors de l’utilisation de base de données ou l’écriture de fichiers sur le filesystem.

Les networks quant à eux, sont les mécanismes de communication entre les containers Docker. Ils peuvent être paramétrés en pont avec la carte réseau de l’hôte, en host (clone de la configuration de la carte) ou coupé du monde (none).

Voilà le résumé des différents éléments de base qui permettent de comprendre Docker dans sa généralité.

Les registries

Avec Docker, au même titre que le NodeJS a npm ou yarn, Linux a apt-get ou yum, MacOSX a brew, nous avons également une commande qui nous permet de puller/pusher dans un registry public ou privé des images Docker. Ces registries peuvent êtres accessibles avec ou sans login, via HTTP ou HTTPS (même si un registry en HTTPS avec un certificat valide reste la meilleure option niveau sécurité). Ils stockent les images sous le format repository-image_name:version. Le repository est un groupe d’images, image_name le nom de l’image et :version représente le tag de version (ou ‘latest’ est la derniere image_name poussée en date).

Grâce au système de layers, on économise beaucoup de bande passante et de temps. Le petit défaut, c’est que les layers, images, containers et volumes restent sur le filesystem si on ne les suppriment pas explicitements. Le suffixe ‘prune’ dans les commandes (ex: docker image prune) permet de supprimer les objets qui ne sont plus utilisés.

 Mon opinion

Bon alors, clairement, j’ai quand même une majorité de points positifs à remonter. Docker, c’est du solide. Toutefois attention, j’émettrais quelques bémols, sur la stabilité et l’aspect pratique de certaines choses. Mais c’est quand même super pratique sur une machine avec un bon CPU et assez de RAM, de pouvoir se payer le luxe de déployer un NGINX en un clin d’oeil, ou un serveur Minecraft, juste avec un docker run 😀

Les orchestrateurs

Docker est rapide et simple avec quelques images, mais il devient un tantinet plus complexe quand on commence à vouloir déployer une application sur plus d’une dizaine de containers. A partir de là (et même avant) on découvre une autre guerre : celle des orchestrateurs.  Il en existe plein (Swarm, Docker Compose, Kubernetes, Mesos, …) et beaucoup sont très complexes à prendre en main. Il existe aussi des IHM de gestion de containers, qui se pluggent au dessus de l’orchestrateur, comme Rancher. Mais comme j’ai dit un peu plus haut, on ne présenteras pas ces outils ici, même si ils valent vraiment le détour.

L’intérêt de la chose est clairement de pouvoir lancer son application sous la forme d’une ruche de micro services, séparés sur plusieurs hôtes, et avec un service par conteneur (oui , quand je francise les termes, ça peut provoquer des saignements occulaires). Cela permet par exemple, d’avoir admettons le service social de votre application « Messagerie instantanée et commentaires » qui ne fonctionne plus si il a un problème, mais qui n’affecte pas les autres composants de votre application de par une séparation physique et un découplage fort de vos autres services.

Le rôle d’un orchestrateur dans ce que je viens de mentionner, est justement de pouvoir mettre à l’échelle, ou « scaler » (créer d’autres instances du même service afin de répondre à un pic de montée en charge) et également de relancer ce/ces containers si ils s’arrêtent ou si un problème survient. De cette façon, on limite énormément les downtime et on peut faire de l’upgrade à chaud relativement facilement.

Donc Docker sans orchestrateur pour une application digne de ce nom, on oublie. Personne ne veut se relever la nuit pour vérifier que ses 40 containers sont bien en éxécution. Personnellement, j’ai testé Docker Compose et Kubernetes. Alors attention, Docker Compose n’est pas « vraiment » un orchestrateur, c’est plus un outil auxiliaire de Docker qui permet simplement de décrire sa configuration à lancer, afin que toutes vos images, networks et volumes se créent et se lancent de concert. Mais il ne gère plus grand chose après, il reste vraiment basique, mais pratique pour des tests/dev quick & dirty (on peut tout de même lui préciser une option restart et de lancer les instances en mode ‘détaché’, pour pouvoir laisser le process indépendant suivre sa vie sur la machine).

La documentation

La documentation est vraiment claire, la communauté assez expressive en ligne, et nombreux sont les contributeurs sur le projet. Toutefois, la technologie étant jeune et évoluant rapidement, il se peut que vous ayez quelques soucis de pionnier qui ne sont pas référencés (EDIT : maintenant un peu moins mais j’ai commencé à écrire l’article il y a un an ahah), ou que vous trouviez des posts datant de quelques mois, qui ne sont déjà plus valables car des naming ou des méthodes ont changées.

Bien entendu, le meilleur endroit pour discuter de ces problèmes là reste sur le github de Docker : https://github.com/docker

Il y a toutefois certains problèmes qui ne sont pas explicitement documentés et il faut surveiller les changelogs ainsi que regarder les issues sur Github justement. Et pourquoi pas contribuer si vous vous y connaissez en Go ? :p

Le software

Je vais mettre ici mes pensées pêle-mêle à propos de Docker en soi. La technologie peu paraître toutefois un peu bricolée sur certains environnement (comme sur Windows 7) mais ça peut se comprendre par le fait que initialement, Docker est quand même construit pour fonctionner en se reposant sur un noyau Linux. Il est donc normal de nécessiter des outils de virtualisation, comme Virtualbox ou HyperV. Mais malgré cela, on peut constater des petites incohérences entre les versions de différents OS notamment au niveau Docker Compose, qui, entre Windows 7 et 10 n’accepte pas les « / » chez Windows 7.

Il est également parfois compliqué de débugguer des applications qui sont instanciées par des containers Docker. Certes, c’est facile d’ouvrir un port vers l’extérieur de son container Docker, et s y connecter avec un remote debugguer (beaucoup d’IDE gèrent cette fonctionnalité, ou en tous cas les deux que je connais IntelliJ et Eclipse).

Il y a aussi parfois des problèmes de stabilité (encore maintenant) sur Windows, ou les networks rentrent dans un état corrompu et on doit les récréer pour que notre commande « docker pull » fonctionne correctement.

Depuis quelques versions, il est également obligatoire de se connecter sur notre instance Docker avec un « docker login » afin de pouvoir s’authentifier sur le registry Docker officiel (Docker Hub) et puller des images.

La mise en place d’un certificat pour pouvoir pousser ou télécharger des images sur des registres securisés est aussi un peu délicate, et encore plus sur la version Docker Toolbox pour Windows 7.

 La configuration

Docker demande beaucoup de configuration, si vous vous basez sur des « bases images » et que vous avez beaucoup d’images différentes dans votre application. Sur les bases de données, notamment, on peut définir énormément de variables d’environnement, de configuration, d’ajout de plugin à la volée, dans le Dockerfile, ou dans un fichier de description ou dans le docker-compose. Cela peut devenir un boulot assez lourd à gérer, pour les montées de versions des images, les configurations de JVM pour tuner vos containers à la perfection, définition des timezones, etc …

Laisser un commentaire