Qu'est-ce que Docker
Docker et virtualisation
Lorsque nous développons une application, il est important qu'elle dispose des exigences de base pour fonctionner : système d'exploitation, bibliothèques, variables d'environnement, packages, entre autres besoins.
Lorsqu’une de ces conditions n’est pas remplie, l’application ne peut pas fonctionner comme elle le devrait. Pour éviter les problèmes de conflit, nous devons nous assurer que partout où elle est exécutée, l'application dispose toujours des exigences nécessaires pour fonctionner.
Il existe plusieurs façons de parvenir à un environnement isolé. La plus utilisée jusqu’à présent est la virtualisation, qui consiste à créer un environnement isolé au sein d’un périphérique hôte. Nous pourrions voir la virtualisation comme une boîte au sein d'un système d'exploitation qui a accès aux ressources de la machine telles que le processeur, le disque ou la RAM et permet aux applications de s'y exécuter.
Il existe 3 manières principales de réaliser la virtualisation :
Machines virtuelles
Les machines virtuelles sont une simulation d'un ordinateur réel. Autrement dit, au démarrage, il se comporte comme s'il s'agissait d'un nouvel appareil : il possède son propre système d'exploitation, il dispose de ressources mémoire et disque, de ses utilisateurs, etc.
Un exemple est que, sur un ordinateur doté du système d'exploitation MacOS, nous créons une machine virtuelle pour installer Windows et pouvoir exécuter des programmes non disponibles pour Mac. Il existe plusieurs programmes qui permettent de créer des machines virtuelles. Certains sont:
- Boîte Virtuelle
- Parallèles
- VMware Fusion
Les machines virtuelles sont le type de virtualisation utilisé dans les serveurs cloud pour créer des VPS (Virtual Private Server) et créer des serveurs avec des ressources et des besoins différents.
Cependant, ce type de virtualisation pose un problème. Son exécution sur un système d'exploitation nécessite beaucoup de ressources. Ce problème a donné naissance à d'autres solutions à l'isolation et à la virtualisation.
Environnements virtuels
Les environnements virtuels sont un type de virtualisation largement utilisé pour exécuter des programmes écrits en Python. Pour chaque environnement virtuel, une version spécifique de Python et des bibliothèques sont utilisées. De cette façon, nous pouvons savoir pour chaque projet quels packages et bibliothèques sont nécessaires pour l'exécuter.
En règle générale, tous les packages et bibliothèques Python nécessaires se trouvent dans le fichier exigeants.txt créé via la commande : pip freeze > exigences.txt.
Voici quelques exemples d'outils qui vous permettent de créer des environnements virtuels :
- conda
- Env virtuel
- Pipenv
Conteneurs
Venons-en enfin au système de virtualisation qui nous intéresse dans cet article : les conteneurs. Ces objets s’apparentent aux machines virtuelles à la différence qu’ils sont beaucoup plus légers et ne nécessitent pas de ressources intensives. En effet, ils ne disposent pas de leur propre système d’exploitation mais utilisent plutôt le système d’exploitation de la machine hôte.
L’exemple le plus connu est Docker, bien qu’il existe d’autres outils pour créer des conteneurs comme Vagrant ou Ansible. Docker utilise un système de création, de suppression et de gestion de conteneurs appelé containersd.
Conteneurisation dans Docker
La meilleure façon de comprendre le fonctionnement de Docker est d'imaginer un conteneur dans lequel nous avons tout le nécessaire pour exécuter une application : fichiers, variables d'environnement, bibliothèques, compilateurs, interpréteurs...
Il est important de mentionner que le système de conteneurs fonctionne très bien pour les systèmes UNIX, donc si vous utilisez Windows, vous aurez sûrement besoin d'une machine virtuelle pour exécuter une distribution Linux.
En bref, Docker permet la génération de conteneurs où nous pouvons isoler nos applications afin qu'elles puissent s'exécuter sur n'importe quel appareil puisque le conteneur héberge toutes les dépendances et bibliothèques nécessaires à l'exécution.
Images dans Docker
Les images sont des abstractions qui permettent de créer des conteneurs. Nous pourrions visualiser les images sous forme de classes dans un langage de programmation orienté objet (POO). En utilisant cette comparaison, les conteneurs seraient des instances (objets) créées à partir d'images (classes).
En règle générale, les instructions pour créer une image se trouvent dans un fichier appelé Dockerfile. Dans la notice on retrouve la distribution Linux, les bibliothèques ou les logiciels nécessaires à l'exécution. Nous pouvons également spécifier quels fichiers doivent être copiés dans le conteneur que nous générerons à partir de l'image Docker.
Comment fonctionne Docker
Normalement, lorsque nous travaillons avec des conteneurs et plus particulièrement avec l'outil Docker, nous utilisons ce qu'on appelle le client Docker. Ce composant contactera via une API le composant en charge de générer, gérer et exécuter à la fois les images et les conteneurs.
Afin de bien gérer les images et les conteneurs, il est important de savoir comment exécuter certaines des commandes les plus élémentaires :
- version docker
- extraction du docker
- poussée du docker
- exécution du docker
- docker ps
- directeur de docker
- arrêt du docker
- tuer un docker
DockerHub
DockerHub est le référentiel Docker officiel. Il existe tous les types d'images pour pouvoir exécuter des conteneurs : applications, bases de données, logiciels... De plus, vous pouvez également créer votre référentiel et télécharger vos propres images à l'aide de la commande docker push, de la même manière que nous téléchargerions notre code sur Github ou Gitlab.
Communication entre conteneurs
Lorsque nous créons, par exemple, une application Web, nous pouvons créer différents services dans différents conteneurs Docker. Même s’il s’agit de systèmes isolés, ils doivent se connecter les uns aux autres pour transférer des données. C'est là que la mise en réseau dans Docker entre en jeu.
Dans Docker, il existe 3 types de réseaux différents :
- Réseau aucun : Utilisé pour définir que ledit conteneur n'a aucun réseau attribué.
- pont rouge : Le réseau pont est celui configuré par défaut dans tous les conteneurs Docker. Cela permet la communication et l'envoi de données entre eux via leurs IP (chaque conteneur se voit attribuer une IP dès sa création).
- Réseau hôte - Ce type de réseau n'isole pas les conteneurs de la machine invitée. Par conséquent, si le conteneur dispose d'un service actif sur le port numéro 80, la machine invitée fournira également ce service via ce port.
Persistance des données
Les données générées dans chaque conteneur Docker sont supprimées une fois celui-ci arrêté. Afin de conserver les données, que nous arrêtions ou démarrions Docker, il existe ce qu'on appelle Volumes .
Les volumes sont des dossiers dans lesquels sont stockées les données à conserver. Ces dossiers se trouvent au sein de la machine invitée, plus précisément dans le répertoire : /var/lib/docker/volumes .
Docker Composer
Docker Compose est un ensemble d'outils qui nous permettent de lancer plusieurs conteneurs en même temps. Lorsque l'on crée une application, elle peut avoir plusieurs composants : bases de données relationnelles, bases de données non relationnelles, services de gestion de files d'attente...
Tous ces composants doivent être lancés dans des conteneurs différents. Les soulever un par un serait très fastidieux. C'est pourquoi nous le faisons via un fichier de configuration appelé docker-compose.yml. Ce fichier spécifie les images à utiliser, les variables d'environnement, les spécifications du réseau, les volumes où les données doivent être enregistrées, entre autres configurations.
Dans les prochains articles, nous entrerons plus en détail sur la partie technique de Docker, en précisant à quoi doivent ressembler les fichiers et commandes nécessaires au lancement des conteneurs.
De plus, nous verrons également comment des outils tels que Kubernetes ou Docker Swarm nous permettent de lancer plusieurs conteneurs qui interagissent entre eux de manière distribuée, c'est-à-dire sur différents nœuds, permettant une grande scalabilité des applications.