AGL_Docker_1

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 11

Cours Inf3523 – Architecture et Génie des Logiciels

Lab_5 : Concepts de base de Docker


Ce lab a pour but de nous familiariser avec les concepts de base docker. Nous apprenons à créer des applications et
les empaqueter dans des images docker, à voir et modifier l’état d’un conteneur, découvrir les réseaux dans docker
et la notion de volumes

Création d’une application sur docker

Nous avons déjà toutes les informations nécessaires pour créer une application entièrement
fonctionnelle sous forme d'image Docker. À titre d'exemple, nous allons préparer, étape par étape, un
simple programme "hello world" en Python. Les étapes sont toujours les mêmes, quel que soit
l'environnement ou le langage de programmation que nous utilisons.

Code de l’application

Créez un nouveau répertoire et, à l'intérieur de ce répertoire, créez un fichier hello.py avec le contenu
suivant :

Préparation de l'environnement

Notre environnement sera défini dans le Dockerfile. Nous avons besoin d'instructions pour définir les
éléments suivants :

- Quelle image de base doit être utilisée

- Comment installer l'interpréteur Python

- Comment inclure hello.py dans l'image

- Comment démarrer l'application

Dans le même répertoire, créez le Dockerfile comme suit :

Création de l'image

Nous pouvons maintenant créer l'image de la même manière que précédemment, comme suit :

docker build -t hello_world_python .

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Exécution de l'application

Nous exécutons l'application en lançant le conteneur, comme ceci :

docker run hello_world_python

On obtient le résultat ci-dessous :

Ce qui est le plus intéressant dans cet exemple, c'est que nous pouvons exécuter l'application écrite
en Python sans avoir l'interpréteur Python installé sur notre système hôte. Cela est possible parce que
l'application emballée sous forme d'image inclut déjà l'environnement nécessaire.

NB : Une image avec l'interpréteur Python existe déjà dans le service Docker Hub, donc dans un
scénario réel, il suffirait de l'utiliser.

Variables d'environnement

Nous avons exécuté notre première application Docker. Cependant, que faire si l'exécution de
l'application dépendait de certaines conditions ?

Une solution serait de préparer un Dockerfile séparé pour chaque cas ; cependant, il existe une
meilleure façon : les variables d'environnement.

Modifions notre application "hello-world" pour afficher suivi du nom de celui qui l’a dit, transmis sous
forme de variable d’environnement : "Hello World from <nom_passé_en_variable_d'env> !". Pour
ce faire, nous devons suivre les étapes suivantes :

1. Modifiez le script Python hello.py pour utiliser la variable d'environnement, comme suit :

2. Créez une autre image, comme ceci :

docker build -t hello_world_python_name .

3. Exécutez le conteneur en passant la variable d'environnement, comme suit :

docker run -e NAME=Daffy hello_world_python_name

Vous verrez alors :

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


4. Alternativement, nous pouvons définir une valeur de variable d'environnement dans le Dockerfile,
comme suit :

5. Exécutez le conteneur sans spécifier l'option -e, comme suit :

docker build -t hello_world_python_name_default .

docker run hello_world_python_name_default

Vous verrez alors :

Les variables d'environnement sont particulièrement utiles lorsque nous avons besoin de différentes
versions du conteneur Docker en fonction de son objectif.

NB : Si une variable d'environnement est définie à la fois dans le Dockerfile et comme option de ligne
de commande, l'option de ligne de commande prend le dessus.

États des conteneurs Docker

Chaque application que nous avons exécutée jusqu'à présent était censée faire un travail et s'arrêter,
par exemple, nous avons affiché "Hello from Docker!" puis nous nous sommes arrêtés. Cependant,
certaines applications doivent fonctionner en continu, comme les services.

Pour exécuter un conteneur en arrière-plan, nous pouvons utiliser l'option -d (--detach). Essayons cela
avec l'image ubuntu, comme suit :

docker run -d -t ubuntu

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Cette commande a démarré le conteneur Ubuntu mais n'a pas attaché la console à celui-ci. Nous
pouvons voir qu'il est en cours d'exécution en utilisant la commande suivante :

docker ps

Cette commande affiche tous les conteneurs qui sont en cours d'exécution. Qu'en est-il de nos anciens
conteneurs qui sont déjà arrêtés ? Nous pouvons les trouver en affichant tous les conteneurs, comme
ceci :

docker ps -a

Notez que tous les anciens conteneurs sont à l'état exited. Il y a deux autres états : paused et
restarting, que nous ne verrons pas ici.

Par exemple, nous pouvons arrêter le conteneur Ubuntu en cours d'exécution, comme montré ici :

docker stop 46281b4f3a67

NB : Nous avons toujours utilisé la commande `docker run` pour créer et démarrer un conteneur.
Cependant, il est possible de créer un conteneur sans le démarrer (avec `docker create`).

Docker et les réseaux

La plupart des applications de nos jours ne fonctionnent pas de manière isolée ; elles ont besoin de
communiquer avec d'autres systèmes via le réseau. Si nous voulons exécuter un site web, un service
web, une base de données ou un serveur de cache dans un conteneur Docker, nous devons d'abord
comprendre comment exécuter un service et exposer son port à d'autres applications.

Exécution des services

Commençons par un exemple simple et exécutons un serveur Tomcat directement depuis Docker
Hub, comme suit :

docker run -d tomcat

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Tomcat est un serveur d'applications web dont l'interface utilisateur peut être accessible par le port
8080. Par conséquent, si nous installons Tomcat sur notre machine, nous pourrions y accéder à
l'adresse http://localhost:8080. Dans notre cas, cependant, Tomcat fonctionne à l'intérieur du
conteneur Docker. Nous l'avons démarré de la même manière que dans le premier exemple Hello
World. Nous pouvons voir qu'il est en cours d'exécution, comme suit :

docker ps

Étant donné qu'il s'exécute en arrière-plan (-d), nous ne voyons pas les journaux dans la console
immédiatement. Nous pouvons, cependant, y accéder en exécutant le code suivant (mettre votre
hash) :

docker logs db226c2880e7

S'il n'y a pas d'erreurs, nous devrions voir beaucoup de logs, indiquant que Tomcat a été démarré et
est accessible via le port 8080. Nous pouvons essayer d'accéder à http://localhost:8080, mais nous ne
pourrons pas nous connecter. Cela est dû au fait que Tomcat a été démarré à l'intérieur du conteneur
et que nous essayons d'y accéder depuis l'extérieur. En d'autres termes, nous ne pouvons y accéder
que si nous nous connectons à la console dans le conteneur. Comment rendre Tomcat accessible de
l'extérieur ?

Nous devons démarrer le conteneur en spécifiant le mappage de port avec l'option -p (--publish),
comme suit :

-p ou --publish <host_port>:<container_port>

Alors, arrêtons d'abord le conteneur en cours d'exécution et démarrons-en un nouveau, comme ceci :

docker stop db226c2880e7

docker run -d -p 8080:8080 tomcat

Après quelques secondes, Tomcat devrait avoir démarré et nous devrions pouvoir ouvrir sa page à
l'adresse http://localhost:8080.

Une simple commande de mappage de port est suffisante pour la plupart des cas d'utilisation courants
de Docker. Nous pouvons déployer des (micro) services en tant que conteneurs Docker et exposer
leurs ports pour faciliter la communication.

Conteneurs et réseaux

Nous nous sommes connectés à l'application qui fonctionne à l'intérieur du conteneur. En fait, la
connexion est bidirectionnelle car, si vous vous souvenez de nos exemples précédents, nous avons
exécuté les commandes `apt-get install` de l'intérieur et les packages ont été téléchargés depuis
Internet. Comment est-ce possible ?

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Nous pouvons voir quelles interfaces sont créées à l'intérieur du conteneur Docker Tomcat en utilisant
la commande `docker inspect`, comme suit :

docker inspect db226c2880e7

Cela imprime toutes les informations sur la configuration du conteneur au format JSON. Entre autres
choses, nous pouvons trouver la partie liée aux paramètres réseau, comme illustré dans l'extrait de
code suivant :

Nous pouvons observer que le conteneur Docker a une adresse IP de 172.17.0.2 et qu'il communique
avec l'hôte Docker via l'adresse IP 172.17.0.1.

Les différents réseaux peuvent être listés et gérés avec la commande `docker network`, comme suit :

docker network ls

Exposition des ports du conteneur

Nous avons mentionné à plusieurs reprises que le conteneur expose le port. En fait, si nous visualisons
l'image Tomcat sur GitHub (https://github.com/docker-library/tomcat), nous pouvons voir, dans le
Dockerfile, la ligne EXPOSE 8080.

Cette instruction Dockerfile stipule que le port 8080 doit être exposé depuis le conteneur. Cependant,
comme nous l'avons déjà vu, cela ne signifie pas que le port est automatiquement publié. L'instruction
EXPOSE informe simplement les utilisateurs des ports qu'ils devraient publier.

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Attribution automatique de port

Essayons de lancer le deuxième conteneur Tomcat sans arrêter le premier, comme suit :

Cette erreur peut être courante. Dans de tels cas, nous devons soit nous assurer de l'unicité des ports
par nous-mêmes, soit laisser Docker attribuer les ports automatiquement en utilisant l'une des
versions suivantes de la commande de publication :

- `-p <container_port>` : Publie le port du conteneur sur un port hôte inutilisé :

- `-P (--publish-all)` : Publie tous les ports exposés par le conteneur sur les ports hôtes inutilisés :

Nous pouvons voir que la deuxième instance de Tomcat a été publiée sur le port 32768, donc elle peut
être consultée à l'adresse http://localhost:32768.

Utilisation des volumes Docker

Imaginez que vous souhaitez exécuter une base de données dans un conteneur. Vous pouvez démarrer
un tel conteneur et saisir des données. Où sont-elles stockées ? Que se passe-t-il lorsque vous arrêtez
le conteneur ou le supprimez ? Vous pouvez en démarrer un nouveau, mais la base de données sera à
nouveau vide. À moins que ce ne soit votre environnement de test, vous vous attendriez à ce que vos
données soient persistantes en permanence.

Un volume Docker est le répertoire de l'hôte Docker monté à l'intérieur du conteneur. Il permet au
conteneur d'écrire sur le système de fichiers de l'hôte comme s'il écrivait sur le sien.

Les volumes Docker permettent la persistance et le partage des données d'un conteneur. Soit cet
exemple cet exemple de cas suivant :

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


1. Spécifiez un volume avec l'option `-v <chemin_hôte>:<chemin_conteneur>` puis connectez-vous
au conteneur, comme suit :

docker run -i -t -v ~/docker_ubuntu:/host_directory ubuntu /bin/bash

2. Créez un fichier vide dans `host_directory` dans le conteneur, comme ceci :

touch /host_directory/file.txt

cat "bonjour Daffy" >> file<txt

3. Vérifiez si le fichier a été créé dans le système de fichiers de l'hôte Docker en exécutant la commande
suivante, mais avant on sort du conteneur avec exit :

exit

ls ~/docker_ubuntu/

4. Nous pouvons voir que le système de fichiers a été partagé et que les données ont donc été
persistées en permanence. Arrêtez le conteneur et exécutez-en un nouveau pour voir si notre fichier
sera toujours là, comme suit :

docker run -i -t -v ~/docker_ubuntu:/host_directory ubuntu /bin/bash

ls /host_directory/

exit

5. Au lieu de spécifier un volume avec l’option `-v`, il est possible de le spécifier comme une instruction
dans le Dockerfile, comme dans l'exemple suivant :

VOLUME /host_directory

NB : Si un volume est défini à la fois dans un Dockerfile et avec un drapeau, la commande du drapeau
prend le dessus.

Les volumes Docker peuvent être beaucoup plus compliqués, notamment dans le cas des bases de
données.

NB : Une approche très courante de la gestion des données avec Docker est d'introduire une couche
supplémentaire, sous la forme de conteneurs de volumes de données. Un conteneur de volumes de
données est un conteneur Docker dont le seul but est de déclarer un volume. Ensuite, d'autres
conteneurs peuvent l'utiliser (avec l'option `--volumes-from <conteneur>`) au lieu de déclarer le
volume directement. Pour en savoir plus, consultez https://docs.docker.com/storage/volumes/.

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Utilisation des noms dans Docker

Jusqu'à présent, lorsque nous avons travaillé sur des conteneurs, nous avons toujours utilisé des noms
générés automatiquement. Cette approche présente certains avantages, tels que l'unicité des noms
(pas de conflits de noms) et l'automatisation (pas besoin de faire quoi que ce soit). Cependant, dans
de nombreux cas, il est préférable de donner un nom convivial à un conteneur ou à une image.

Nommer les conteneurs

Il y a deux bonnes raisons de nommer un conteneur : la commodité et la possibilité d'automatisation :

• Commodité : Il est plus simple d'effectuer des opérations sur un conteneur en l'adressant par
son nom plutôt qu'en vérifiant les hachages ou le nom généré automatiquement.
• Automatisation : Parfois, nous aimerions dépendre de la dénomination spécifique d'un
conteneur. Par exemple, nous voudrions avoir des conteneurs qui dépendent les uns des
autres et en avoir un lié à un autre. Par conséquent, nous devons connaître leurs noms.

Pour nommer un conteneur, nous utilisons le paramètre `--name`, comme suit :

docker run -d --name tomcat tomcat

Nous pouvons vérifier (avec `docker ps`) que le conteneur a un nom significatif. De plus, en
conséquence, toute opération peut être effectuée en utilisant le nom du conteneur, comme dans
l'exemple suivant :

docker logs tomcat

Veuillez noter que lorsqu'un conteneur est nommé, il ne perd pas son identité. Nous pouvons toujours
adresser le conteneur par son ID de hachage généré automatiquement, comme nous le faisions
auparavant.

NB : Un conteneur a toujours à la fois un ID et un nom. Il peut être adressé par l'un ou l'autre, et les
deux sont uniques.

Nommer (étiqueter ou taguer) les images

Les images peuvent être étiquetées (taguées). Nous l'avons déjà fait lors de la création de nos propres
images, par exemple, lors de la construction de l'image `hello_world_python`, comme illustré ici :

docker build -t hello_world_python .

L’option (tag ou étiquette) `-t` décrit l'étiquette de l'image. Si nous ne l'utilisons pas, l'image sera
construite sans aucune étiquette et, par conséquent, nous devrons l'adresser par son ID (hash) pour
exécuter le conteneur.

Une image peut avoir plusieurs étiquettes, et elles doivent suivre cette convention de nommage :

<registry_address>/<image_name>:<version>

Une étiquette se compose des parties suivantes :

• registry_address : IP et port du registre ou le nom d'alias


• image_name : Nom de l'image qui est construite, par exemple, `ubuntu`
• version : Une version de l'image sous n'importe quelle forme, par exemple, `20.04`, `20170310`

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


Nous aborderons les registres Docker plus tard. Si une image est conservée sur le registre officiel
Docker Hub, nous pouvons ignorer l'adresse du registre. C'est pourquoi nous avons exécuté l'image
`tomcat` sans aucun préfixe. La dernière version est toujours étiquetée comme `latest` et cela peut
également être omis, donc nous avons exécuté l'image `tomcat` sans aucun suffixe.

NB : Les images ont généralement plusieurs tags ; par exemple, toutes ces trois tags désignent la
même image : `ubuntu:18.04`, `ubuntu:bionic-20190122` et `ubuntu:bionic`.

Nettoyage de Docker

Tout au long de ce lab, nous avons créé un certain nombre de conteneurs et d'images. Cependant, ce
n'est qu'une petite partie de ce que vous verrez dans des scénarios réels. Même lorsque les conteneurs
ne sont pas en cours d'exécution, ils doivent être stockés sur l'hôte Docker. Cela peut rapidement
entraîner une saturation de l'espace de stockage et arrêter la machine. Comment pouvons-nous
aborder cette préoccupation ?

Nettoyage des conteneurs

Tout d'abord, examinons les conteneurs qui sont stockés sur notre machine. Voici les étapes que nous
devons suivre :

1. Pour afficher tous les conteneurs (quel que soit leur état), nous pouvons utiliser la commande
`docker ps -a`, comme suit :

docker ps -a

2. Pour supprimer un conteneur arrêté, nous pouvons utiliser la commande `docker rm` (si un
conteneur est en cours d'exécution, nous devons d'abord l'arrêter), comme suit :

docker container stop (hash ou nom du conteneur)

docker rm (hash ou nom du conteneur)

3. Si nous voulons supprimer tous les conteneurs arrêtés, nous pouvons utiliser la commande suivante

docker container prune

4. Nous pouvons également adopter une approche différente et demander au conteneur de se


supprimer dès qu'il est arrêté en utilisant le drapeau `--rm`, comme dans l'exemple suivant :

docker run --rm hello-world

Dans la plupart des scénarios réels, nous n'utilisons pas les conteneurs arrêtés, et ils sont laissés
uniquement à des fins de débogage.

Nettoyage des images

Nettoyer les images est tout aussi important que nettoyer les conteneurs. Elles peuvent occuper
beaucoup d'espace, surtout dans le cadre du processus de CI/CD, où chaque build se termine par une
nouvelle image Docker. Cela peut rapidement entraîner une erreur de type "no space left on device".
Voici les étapes à suivre :

1. Pour vérifier toutes les images dans le conteneur Docker, nous pouvons utiliser la commande
`docker images`, comme suit :

docker images

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD


2. Pour supprimer une image, nous pouvons utiliser la commande suivante :

docker rmi (hash ou nom de l’image)

3. Dans le cas des images, le processus de nettoyage automatique est légèrement plus complexe. Les
images n'ont pas d'états, donc nous ne pouvons pas leur demander de se supprimer lorsqu'elles ne
sont pas utilisées. Une stratégie courante serait de configurer une tâche de nettoyage qui supprime
toutes les images anciennes et inutilisées. Nous pourrions le faire en utilisant la commande suivante :

docker image prune

NB : Si nous avons des conteneurs qui utilisent des volumes, alors, en plus des images et des
conteneurs, il est utile de penser au nettoyage des volumes. Le moyen le plus simple de le faire est
d'utiliser la commande `docker volume prune`. Utilisez la commande `docker system prune` pour
supprimer tous les conteneurs, images et réseaux inutilisés. De plus, vous pouvez ajouter le paramètre
`--volumes` pour nettoyer les volumes.

Aperçu des commandes Docker

Toutes les commandes Docker peuvent être trouvées en exécutant la commande d'aide suivante :

docker help

Pour voir toutes les options d'une commande Docker particulière, nous pouvons utiliser `docker help
<command>`, comme dans l'exemple suivant :

docker help run

Il y a aussi une très bonne explication de toutes les commandes Docker sur la page officielle de Docker
à l'adresse suivante : https://docs.docker.com/engine/reference/commandline/docker/.

Résumé :

Dans ce lab, nous avons vu les concepts de base de docker tels que la notion de variables
d’environnement, de réseaux, de noms, de volumes, etc. Le lab suivant sera une application des deux
premiers labs sur docker et permettra de déployer une application fullstack avec donc une BD, un
backend et un frontend tous déployés dans docker. Nous verrons également comment publier ses
images dans Docker Hub afin que celles-ci puissent être utilisées par une communauté plus large.

Dr. E_H_B TOURE Cours : AGL-DMI/FST/UCAD

Vous aimerez peut-être aussi