Docker


Docker est un logiciel qui permet d’empaqueter des applications. Il les place dans ce que l’on appelle un conteneur isolé avec toutes les dépendances nécessaire à son fonctionnement. Docker facilite le déploiement d’une application sur un serveur car il englobe tout le nécessaire à son fonctionnement. Un conteneur va utiliser la puissance de la machine hôte et ne nécessite pas d’OS contrairement à une machine virtuelle. Docker est intéressant lors du développement car les problèmes tels que “pourquoi ça marche sur ton ordinateur et pas sur le mien ?” ne surviendront pas. En effet, les environnements sont identiques.
Dans cet article nous allons apprendre les rudiments de Docker avec une simple application NodeJS et une base de données mysql. Néanmoins, si vous utilisez une autre technologie pour votre application, cela fonctionnera de la même manière. Vous pouvez avancer en suivant cet article, à travers les différents exemples qui seront données si vous le souhaitez.
Cet article par du postulat que vous n’avez aucune connaissance de Docker.
Dans cet article, nous allons aborder les sujets suivants :

  • pourquoi Docker et qu’est-ce que c’est, c’est probablement la partie la plus importante de l’article ; pourquoi Docker, pourquoi pas une autre technologie ? Je vais essayer d’expliquer ce qu’est Docker et en quoi il consiste.
  • Docker en action, nous allons dockeriser une application pour montrer que nous comprenons et pouvons utiliser les concepts de base qui font Docker.
  • améliorer notre implémentation, nous devons nous assurer que notre solution ne repose pas sur des valeurs statiques. Nous définirons donc des variables d’environnement dont nous pouvons lire la valeur depuis notre application.
  • gérer notre conteneur, c’est maintenant assez facile de le mettre en place et de le faire fonctionner, mais voyons comment le gérer, nous ne voulons pas que le conteneur soit toujours monté et en fonctionnement.

Rappelez-vous qu’il s’agit de la première partie d’une série et que nous aborderons d’autres éléments lors de cette série sur Docker tels que les volumes, les liens, les micro-services et l’orchestration. Cependant, ces points seront abordés dans des parties ultérieures.

#1 Pourquoi Docker et qu’est-ce que c’est ?

Docker aide à créer un environnement reproductible. Vous pouvez spécifier le système d’exploitation spécifique, la version exacte des différentes bibliothèques, les différentes variables d’environnement et leurs valeurs, entre autres choses. Plus important, vous pouvez exécuter votre application de manière isolée à l’intérieur de cet environnement.
La grande question est pourquoi nous voudrions cela ? 

  • l’onboarding, chaque fois que vous accueillez un nouveau développeur dans un projet, il a besoin d’installer des SDK, des outils de développement, des bases de données, d’ajouter des permissions et ainsi de suite. Il s’agit d’un processus qui peut prendre d’une journée à deux semaines. Docker simplifie grandement cela.
  • les environnements sont similaires, vous pouvez créer un environnement DEV, STAGING ainsi qu’un environnement de PRODUCTION qui se ressemblent tous. C’est une vraie plus-value, avant Docker vous pouviez avoir des environnements similaires, mais il pouvait y avoir de petites différences et quand vous découvriez un bug, vous pouviez passer beaucoup de temps à chercher la cause réelle du bug. Parfois le bug se trouvait dans le code source lui-même, mais parfois il était dû à une certaine différence dans l’environnement et cela prend habituellement un certain temps à déterminer.
  • “ça fonctionne sur ma machine pourtant”, ce point ressemble beaucoup au précédent, mais parce que Docker crée ces conteneurs isolés, où vous spécifiez exactement ce qu’ils doivent contenir, vous pouvez également envoyer ces conteneurs aux clients et ils fonctionneront exactement de la même manière qu’ils l’ont fait sur vos machines de développement.

Qu’est-ce que c’est :
Qu’est ce que Docker en réalité. Il nous permet de spécifier un environnement comme l’OS, de trouver et exécuter les applications et les variables dont nous avons besoin, mais qu’y a-t-il d’autre à savoir sur Docker ?
Docker crée des packages autonomes appelés conteneurs qui contiennent tout ce dont vous avez besoin pour exécuter votre application. Chaque conteneur possède ses propres ressources CPU, mémoire et réseau et ne dépend pas d’un système d’exploitation ou d’un noyau spécifique. La première chose qui me vient à l’esprit lorsque je décris cela est la machine virtuelle, mais Docker diffère dans la façon dont il partage ou consacre ses ressources. Docker utilise un système de fichiers en couches qui permet aux conteneurs de partager des parties communes. Donc les conteneurs sont beaucoup moins gourmands en ressources sur le système hôte qu’une machine virtuelle.
Les conteneurs Docker contiennent tout ce dont vous avez besoin pour exécuter une application, y compris le code source que vous avez écrit. Les conteneurs sont des unités légères isolées et sécurisées sur votre système. Cela permet de créer facilement plusieurs micro-services qui sont écrits dans différents langages de programmation et qui utilisent différentes versions de la même librairie et du même OS.

#2 Docker en action

On a vu ce qu’est Docker et quelques avantages. Nous avons également compris que ce qui exécute notre application s’appelle un conteneur. Mais comment y arriver ? Commençons par un fichier de description, appelé Dockerfile. Dans ce Dockerfile, nous spécifions tout ce dont nous avons besoin en termes d’OS, de variables d’environnement et de la manière d’y intégrer notre application.
Nous allons construire une application et la dockeriser, de sorte que nous aurons notre application fonctionnant à l’intérieur d’un conteneur, isolé du monde extérieur mais accessible sur les ports que nous ouvrons explicitement.
Nous allons suivre les étapes suivantes :

  • créer une application, nous allons créer une application Node.js Express, qui agira comme une API REST.
  • créer un Dockerfile, un fichier texte qui indique à Docker comment faire le build de notre application
  • construire une image, l’étape préalable à la mise en place et au fonctionnement de notre application consiste d’abord à créer une image Docker.
  • créer un conteneur, nous allons créer un conteneur à partir d’une image Docker

Créer notre application
Nous allons maintenant créer un projet Express Node.js et il sera composé des fichiers suivants :

  • app.js, c’est le fichier qui fait tourner notre application REST
  • package.json, c’est le fichier dans lequel nous allons voir toutes les dépendances comme express. Nous allons aussi déclarer un script ‘start’ pour facilement démarrer notre application
  • Dockerfile, c’est un fichier que nous allons créer pour indiquer à Docker comment Dockeriser notre application

Pour générer notre package.json, il suffit de se placer dans le dossier de notre projet et de taper dans le terminal :

Un fichier package.json avec des valeurs par défaut va être créé.
Nous allons ajouter les dépendances que nous sommes sur le point d’utiliser, la bibliothèque express, nous l’installons en tapant ceci dans le terminal :

Ajoutons un peu de code
Nous avons fait tout le travail préliminaire avec la génération du package.json et l’installation des dépendances, nous allons ajouter le code nécessaire à l’exécution de notre application dans le fichier app.js.

Nous pouvons essayer d’exécuter cette application en tapant :

Allez via votre navigateur web sur l’URL http://localhost:3000 vous devriez voir Hello World!
Attention cependant, gardons en tête que nous assignons le port à 3000 lorsque nous créerons plus tard notre Dockerfile.
 

#3 Création d’un Dockerfile

Le Dockerfile agit comme un fichier d’instructions de compilation, il indique comment faire fonctionner notre application. De quoi a-t-on besoin pour que l’application soit opérationnelle ? Il faut :

  • copier tous les fichiers de l’application dans le conteneur Docker
  • installer les dépendances comme express
  • exposer un port dans le conteneur accessible depuis l’extérieur
  • indiquer au conteneur comment démarrer notre application

Dans une application plus complexe, nous pourrions avoir besoin de définir des variables d’environnement, définir des informations d’identification pour une base de données ou exécuter un script d’initiation de base de données, etc. Pour l’instant, nous n’avons besoin que des choses que nous avons spécifiées dans notre liste ci-dessus. Essayons donc de l’exprimer dans notre Dockerfile :

Décomposons les commandes ci-dessus :

  • FROM, on y sélectionnons une image d’OS depuis Docker Hub. Docker Hub est un repository global qui contient des images que nous pouvons extraire localement. Nous choisissons une image basée sur Ubuntu qui a Node.js installé, c’est ce que l’on appelle un noeud. Nous spécifions également que nous voulons la dernière version de celui-ci, en utilisant la balise :latest
  • WORKDIR, on défini le répertoire de travail pour Docker.
  • COPY, nous copions les fichiers du répertoire courant vers le répertoire spécifié par notre commande WORKDIR. Le premier ‘.’ indique donc le répertoire courant sur l’ordinateur, et le second ‘.’ indique le répertoire courant dans le conteneur, soit dans le dossier ‘app’.
  • RUN, lance une commande dans le terminal. Nous installons toutes les bibliothèques dont nous avons besoin pour construire notre application express Node.js.
  • EXPOSE, indique qu’on ouvre un port, c’est via ce port que nous communiquons avec notre conteneur.
  • ENTRYPOINT, on y indique comment démarrer notre application, les commandes doivent être spécifiées sous forme de tableau pour que le tableau [“node”, “app.js”] soit traduit “node app.js” dans le terminal

#4 Résumé rapide

Nous avons créé tous les fichiers dont nous avons besoin pour notre projet qui devrait ressembler à ceci :

#5 Builder une image

Il y a deux étapes pour que notre application soit opérationnelle dans un conteneur :

  • créer une image, à l’aide du Dockerfile et de la commande docker build nous allons créer une image
  • démarrer le conteneur, avec l’image que nous venons de créer, nous devons créer un conteneur

Tout d’abord, créons notre image avec la commande suivante :

L’instruction ci-dessus crée une image. Le ‘.’ à la fin est important car il indique au Docker où se trouve votre Dockerfile. Ici, c’est le répertoire courant. Si vous n’avez pas l’image du système d’exploitation que vous demandez dans la commande FROM, elle sera téléchargée depuis le Docker Hub et votre image spécifique sera alors construite.
Vous pouvez voir dans votre terminal comment le nœud d’image de l’OS:latest est téléchargée depuis le Docker Hub. Puis chacune des commandes est exécutée comme WORKDIR, RUN et ainsi de suite. Chaque conteneur intermédiaire est supprimé après chaque étape. Docker met en cache les différentes couches de fichiers après chaque commande pour que ça aille plus vite. A la fin, nous voyons ‘successfully built’ qui nous indique que tout a été buildé avec succès. 
Nous pouvons vérifier que notre image existe bien avec :

Nous devons y trouver une image qui se nomme uxrepublic/node.

#6 Création d’un conteneur

Nous allons utiliser notre image pour construire un conteneur à partir de celle-ci. Un conteneur est un programme isolée qui exécute notre application a l’intérieur de lui-même. Nous construisons un conteneur à l’aide d’un docker run :

Nous devons encore mapper le port interne de l’application à un port externe, sur la machine hôte. Rappelez-vous qu’il s’agit d’une application que nous voulons atteindre via notre navigateur. Nous faisons le mapping en utilisant l’option -p comme ça :

La commande complète est :

Cette commande nous permet de visiter notre conteneur en allant sur http://localhost:8000, 8000 est notre port externe n’oubliez pas qu’il est mappé avec le port interne 3000. En ouvrant un navigateur sur cette page nous voyons Hello World! apparaître. Bravo ! Vous avez un conteneur qui fonctionne.

#7 Amélioration de notre configuration avec des variables d’environnement

Nous avons appris comment construire notre image Docker, utiliser un conteneur, placer notre application à l’intérieur de celui-ci. Cependant, nous pourrions mieux nous occuper du PORT. Pour l’instant nous gardons le port sur lequel nous démarrons le serveur express dans une variable à l’intérieur de notre app.js pour nous assurer qu’il correspond à ce que nous écrivons dans le Dockerfile. Cela expose à des erreurs en cas de changement de port dans le Dockerfile non reporté dans app.js.
Pour y remédier, nous pourrions introduire une variable d’environnement. Nous devons faire deux choses :

  • ajouter une variable d’environnement au Dockerfile
  • lire la variable d’environnement dans app.js

#8 Ajouter une variable d’environnement

Nous devons utiliser la commande ENV, de la manière suivante :

Mettons à jour EXPOSE afin d’utiliser notre nouvelle variable d’environnement. Nous nous débarrassons des valeurs statiques et utilisons dorénavant des variables.
Ajoutons cela à notre Dockerfile :

Notez comment nous changeons notre commande EXPOSE avec $PORT. Toutes les variables que nous utilisons doivent être préfixées d’un $.

#9 Lire la valeur d’une variable d’environnement dans App.js

Nous pouvons lire les valeurs des variables d’environnement dans Node.js comme ceci :

Mettons donc à jour notre code dans app.js :

REMARQUE, lorsque nous faisons un changement dans notre app.js ou notre Dockerfile, nous avons besoin de rebuilder notre image. Nous devons exécuter à nouveau la commande docker build et, avant cela, nous devons avoir supprimé notre conteneur avec docker stop et docker rm. Plus de détails à ce sujet dans les prochaines sections.

#10 Gérer notre conteneur

Vous venez de démarrer votre conteneur avec docker run et vous remarquez que vous ne pouvez pas l’éteindre dans le terminal. A ce stade, vous pouvez aller dans une autre fenêtre de terminal et faire :

Cette commande listera tous les conteneurs en cours d’exécution, vous pourrez voir le nom du conteneur ainsi que son identifiant. Vous pouvez observer les colonnes CONTAINER_ID et NAMES. Ces colonnes donnent les informations suffisantes pour stopper notre conteneur. Je choisi d’utiliser l’id de mon conteneur et dans mon cas, l’id est le suivant : f40e795d6151.

Cette commande stop mon conteneur. Notons que seuls les 3 premiers caractères de mon id suffisent à identifier le conteneur à stopper.

#11 Mode Deamon

Pour gérer notre conteneur nous pouvons toujours ouvrir un autre onglet de terminal et lancer des commandes sur notre conteneur, mais l’exécuter en mode Daemon est une meilleure option. Cela exécute le conteneur en arrière-plan. Tous les output de ce conteneur ne seront pas visibles, ce qui nous permet d’utiliser ce terminal pour lancer d’autres commandes. Pour ce faire, nous ajoutons simplement l’option -d .

Nous récupérons directement l’id du conteneur que nous venons de lancer et c’est tout. Nous n’aurons pas d’autres output de ce conteneur. Nous pouvons donc utiliser cet id pour stopper notre conteneur lorsque nous en aurons besoin.

#12 Mode Interactif

Le mode interactif est intéressant. Il nous permet de nous introduire dans un conteneur en fonctionnement, d’ajouter, de supprimer ou juste d’afficher des fichiers, de lancer des commandes bash… Pour cela utilisons la commande docker exec. Si par exemple, l’id de notre conteneur commence par 268 nous pouvons utiliser la commande suivante

REMARQUE, le conteneur doit être en fonctionnement. Si vous l’avez arrêté précédemment, vous devez le démarrer avec le docker start 268. Remplacez 268 par n’importe quel id que vous avez obtenu lors de sa création lorsque vous avez tapé docker run.
268 correspond aux trois premiers chiffres de l’id de notre conteneur et -it signifie qu’il est en mode interactif. Notre argument bash à la fin signifie que nous allons lancer un shell bash.
Nous pouvons exécuter la commande ls une fois que nous avons lancé le bash, cela nous permet de lister ce qu’il y a dans le conteneur pour vérifier que nous l’avons construit correctement. C’est aussi un bon moyen de déboguer notre conteneur.
Si nous voulons exécuter quelque chose sur le conteneur comme une commande node, par exemple, nous pouvons taper :

Cela lancera la commande node app.js dans le conteneur.

#13 Docker kill vs Docker stop

Jusqu’à présent, nous avons utilisé docker stop comme moyen d’arrêter le conteneur. Il y a une autre façon d’arrêter le conteneur : docker kill, alors quelle est la différence entre ces deux commandes ?

  • docker stop envoie le signal SIGTERM suivi de SIGKILL. C’est une manière d’arrêter le conteneur d’une manière plus propre car il libère des ressources.
  • docker kill, envoie SIGKILL. La libération des ressources pourrait ne pas fonctionner comme prévu. En développement, peu importe laquelle des deux commandes est utilisée, mais dans un scénario de production, il est plus sage de se fier à docker stop.

Nettoyage

Au cours du développement, vous finirez par créer beaucoup de conteneurs, alors assurez-vous de nettoyer votre machine avec :

Résumé

Nous voici à la fin du premier article de notre série sur Docker. Nous avons abordé les concepts de base et les motivations menant à l’utilisation de Docker. Nous avons vu comment Dockeriser une application et, ce faisant, nous avons découvert quelques commandes Docker utiles. Il y a d’autres choses à appréhender, à savoir comment travailler avec les bases de données, les volumes, comment lier des conteneurs et pourquoi et comment filtrer et gérer plusieurs conteneurs, l’orchestration.
Rendez-vous au prochain article où nous parlerons des volumes et des bases de données.
Traduction de l’article https://dev.to/azure/docker—from-the-beginning-part-i-28c6
 
Valentin Dupetitpre, JS Craftsman @UX-Republic