Tuto Docker – Comment créer et déployer des conteneurs ?

Tuto pour déployer des conteneurs grâce à la technologie Docker
Lucas GEFFROYMis à jour le 19 Mars 2021
Tuto Docker – Comment créer et déployer des conteneurs ?

Dans la première partie de cet article, nous avons découvert Docker et les contextes dans lesquels il est intéressant de l’utiliser. Dans cette seconde partie, nous allons développer certains points techniques afin de vous permettre de déployer vos premiers conteneurs !

Pour rappel, il existe trois concepts clés dans Docker : les conteneurs, les images et les fichiers Docker (Dockerfile). Dans cette partie, nous allons voir comment les implémenter. A l’instar du premier article, nous partirons du résultat final (les conteneurs) pour remonter jusqu’à leur origine, les Dockerfiles, en passant par les images qui se trouvent entre les deux.

Docker : les conteneurs

Nous avons vu en quoi consistait un conteneur, essayons maintenant d’en déployer un !

Démarrer un conteneur Docker

La commande docker run <nom_image> nous permet de créer et de démarrer un conteneur sur la base d’une image.

Ici, nous instancions un conteneur nommé mysql-container, basé sur l’image mysql, et dont on définit la variable d’environnement MYSQL_ROOT_PASSWORD à thisIsAPassword :

root@root:~# docker run --name mysql-container -e MYSQL ROOT PASSWORD=thisIsAPassword -d mysql:latest

Exemple de résultat

9cf38cab46f968a23e9541531b0a94eecddld6481db5052e81ffc612b4faffd6

Chaque conteneur possède son propre id aléatoire. Celui de notre conteneur est la deuxième ligne de l’image : 9cf…

Lorsque nous démarrons un conteneur, nous pouvons lui passer un ensemble de paramètres comme un nom ou un port. L’image utilisée peut aussi nécessiter la définition de variables d’environnement. L’ensemble de ces paramètres est disponible ici : https://docs.docker.com/engine/reference/run/

Liste des conteneurs Docker lancés

La liste des conteneurs actifs est visible avec la commande docker ps.

docker ps

Exemple de résultat

CONTAINER ID     IMAGE            COMMAND                     CREATED              STATUS           PORTS                 NAMES
9cf38cab46f9     mysql:latest     "docker-entrypoint.s..."    17 seconds ago       Up 16 seconds    3306/tcp, 33060/tcp   mysql-container

Dans la liste, nous reconnaissons la ligne correspondant à notre conteneur fraîchement créé, et différentes informations y sont visibles comme son ID, l’image utilisée ou encore son statut.

  • Il y a son ID, le fameux 9cf…
  • Le nom et la version de l’image utilisée par le conteneur : mysql:latest
  • Le moment où a été créé le conteneur
  • Son statut, c’est-à-dire s’il est en cours d’exécution ou arrêté
  • Le mappage des ports entre l’intérieur et l’extérieur du conteneur
  • Et enfin le nom qu’on a donné au conteneur avec le paramètre –-name mysql-container

Créons un nouveau conteneur basé sur la même image et nommons-le mysql-container-2 :

docker run --name mysql-container-2 -e MYSQL_ROOT_PASSWORD=thisIsAPassord -d mysql:latest

Exemple de résultat

72bb7dbac3ab38a4013dac825ae81b20214fe8a4a7397875ca29a062c5f68835

Nous avons désormais deux conteneurs dans la liste.

Docker : arrêter un conteneur

Nous pouvons à tout moment arrêter un conteneur grâce à la commande docker stop <id_du_conteneur>.

Les ids des conteneurs sont des chaînes assez indigestes qui seraient longues à écrire mais pas de panique, il vous suffit d’entrer les premiers caractères de cet ID pour que Docker trouve par lui-même de quel conteneur vous parlez.

docker stop 72

Liste des conteneurs Docker lancés et arrêtés

Si nous retapons la commande docker ps, nous avons le même résultat qu’avant car nous n’avons qu’un conteneur qui est en cours d’exécution. En effet, la commande docker ps seule n’affiche que les conteneurs en cours d’exécution.

docker ps

Exemple de résultat

CONTAINER ID     IMAGE            COMMAND                     CREATED              STATUS           PORTS                 NAMES
9cf38cab46f9     mysql:latest     "docker-entrypoint.s..."    33 seconds ago       Up 32 seconds    3306/tcp, 33060/tcp   mysql-container

Pour afficher la liste de tous les conteneurs, même ceux éteints, le paramètre -a doit rentrer en jeu : docker ps -a :

docker ps -a

Exemple de résultat

CONTAINER ID    IMAGE          COMMAND                     CREATED              STATUS                         PORTS                 NAMES
72bb7dbac3ab    mysql:latest   "docker-entrypoint.s..."    34 seconds ago       Exited (137) 14 seconds ago                         mysql-container-2
9cf38cab46f9    mysql:latest   "docker-entrypoint.s..."    About a minute ago   Up About a minute ago    3306/tcp, 33060/tcp        mysql-container

Et nous voyons ainsi apparaître le conteneur mysql-container-2 dont l’identifiant est 72…

Redémarrer un conteneur Docker

Désormais, nous pouvons nous demander ce qu’on peut bien faire de ce conteneur arrêté, car il n’a pas grande utilité.

Nous avons la possibilité de le redémarrer avec docker restart <id_conteneur> :

docker restart 72

Supprimer un conteneur Docker

Nous pouvons également le supprimer complètement, lui et son contexte, avec la commande docker rm <id_conteneur> :

docker rm 72

Il n’apparaît désormais plus dans la liste qu’affiche docker ps -a.

CONTAINER ID    IMAGE          COMMAND                     CREATED              STATUS                   PORTS                 NAMES
9cf38cab46f9    mysql:latest   "docker-entrypoint.s..."    About a minute ago   Up About a minute ago    3306/tcp, 33060/tcp   mysql-container

Récapitulatif des commandes Docker abordées

Nous avons utilisé beaucoup de commandes dans le cadre des conteneurs. Afin de ne pas nous y perdre, voici la liste des commandes que nous avons abordées :

Commande Fonction
docker run <nom_image> Permet de démarrer un conteneur
docker ps Permet d’avoir la liste des conteneurs en cours d’exécution
docker ps -a Permet d’avoir la liste des conteneurs, même ceux qui ne sont pas en cours d’exécution
docker stop <id_conteneur> Permet d’arrêter un conteneur à partir de son identifiant
docker restart <id_conteneur> Permet de redémarrer un conteneur arrêté à partir de son identifiant
docker rm <id_conteneur> Permet de supprimer définitivement un conteneur à partir de son identifiant

Docker : les images

Il existe deux manières d’obtenir une image. La première est de créer son propre Dockerfile et de le compiler. La seconde est de la récupérer sur Dockerhub (disponible ici https://hub.docker.com/) par le biais de la commande docker pull

imagedockerhub.png

Dockerhub est un répertoire en ligne rendant disponibles des millions d’images docker. Il est possible de récupérer mais aussi de mettre en ligne des images pour tout et n’importe quoi, comme par exemple cette image qui permet de faire tourner Docker… dans un conteneur ! 

imagedockerhub2.png

Les images interviennent lors du processus de création des conteneurs. On spécifie, pour chaque conteneur, l’image à laquelle se rattacher pour se créer. Cette liaison peut se faire lors du lancement d’un conteneur à l’aide de la commande docker run <nom_image> vue précédemment. Ainsi, un conteneur possédera l’ensemble du cœur de l’image dont il est l’instance.

Récupérer une image de Dockerhub

Un peu plus haut, pour instancier nos deux premiers conteneurs, nous nous sommes basés sur l’image mysql de Dockerhub. Pour l’installer sur notre machine, il nous suffit de faire docker pull mysql :

docker pull mysql
``` ![Commandedocker9.png](https://assets.axopen.com/assets/uploads/Commandedocker9_2103518cb4.png)

L’image **mysql** possède plusieurs versions, comme on peut le voir sur sa page : [https://hub.docker.com/_/mysql](https://hub.docker.com/_/mysql) ![mysql.jpg](https://assets.axopen.com/assets/uploads/mysql_24013ccdc8.jpg)

Si nous souhaitons télécharger une version précise de **mysql**, il suffit de préciser le tag de cette version après le nom dans la commande pull. Par exemple _docker pull mysql:8.0.22_.

Si on ne précise aucun tag, Docker mettra par défaut le tag **latest**.

### Liste des images installées

Docker met à disposition différentes commandes en rapport aux images. Nous allons en voir deux ici.

À l’instar des conteneurs, nous avons la possibilité de voir la liste des images installées sur notre Docker. Pour cela, nous allons taper _docker images_ :

```shell script
docker images
``` ![Commandedocker10.png](https://assets.axopen.com/assets/uploads/Commandedocker10_11909451d0.png)


On retrouve dans cette liste le même principe d’identifiant que celui des conteneurs. Il y a également le tag (la version téléchargée), la date de création, la taille de l’image et pour terminer son nom.

### Supprimer une image Docker

Nous pouvons également **supprimer définitivement** une image avec la commande _docker rmi <id_image>_.

## Docker : le Dockerfile

Pour rappel, un Dockerfile est un fichier qui liste les instructions à exécuter pour build une image.

### Quelques instructions du Dockerfile

Chaque ligne est une instruction, et il en existe une dizaine :

**FROM :** Elle est obligatoire et doit être sur la première ligne. Elle permet d’indiquer l’image sur laquelle on se base pour construire notre Dockerfile. L’ensemble du contexte de l’image sur laquelle on se base sera ainsi utilisé dans l’image créée à la suite de la compilation du Dockerfile.

_FROM mysql:latest_

**COPY :** Permet de copier des dossiers du client Docker à l’intérieur du conteneur. En effet, les conteneurs n’ont (normalement) pas accès aux dossiers du système faisant tourner Docker, il faut donc déplacer en son sein les fichiers que l’on souhaite pouvoir utiliser. Par exemple, le code source de notre application.

_COPY . /my-app_

**RUN :**  Permet de spécifier une commande à exécuter pendant le build de l’image. On peut par exemple l’utiliser pour télécharger des dépendances.

_RUN apt-get install -y git_

**CMD :** À ne pas confondre avec RUN, cette instruction permet d’indiquer une commande qui doit être exécutée au lancement des conteneurs. Par exemple une commande qui permet de compiler le projet ou bien de lancer un script.

_CMD ./my-app/gradlew bootRun_

**WORKDIR :** Permet de changer de répertoire à l’intérieur du contexte pour l’ensemble des instructions qui suivent. On peut le voir comme la commande CD mais à l’intérieur d’un Dockerfile. C’est la seule façon de le faire car l’instruction **RUN cd /my-app** ne fonctionne pas. En effet, chaque instruction RUN se base sur le dernier WORKDIR lors du build. Ainsi, l’instruction suivante ne se basera pas sur le répertoire **/my-app** mais sur le dernier répertoire spécifié par WORKDIR (par défaut le répertoire racine).

_WORKDIR ./my-app_

**EXPOSE :** Permet d’exposer un port de l’intérieur du conteneur vers l’extérieur. Par défaut, ce port interne sera le même que celui accessible en dehors du conteneur mais au démarrage d’un conteneur, on a la possibilité de mapper. Ainsi, on peut faire en sorte que le port 80 à l’intérieur du conteneur devienne 8080 à l’extérieur.

_EXPOSE 80_

Il en existe d’autres que l’on peut retrouver ici : [https://docs.docker.com/engine/reference/builder/#environment-replacement](https://docs.docker.com/engine/reference/builder/#environment-replacement)

### Compiler un Dockerfile

Une fois écrit, nous devons compiler notre Dockerfile en une image. Pour cela, il nous suffit de nous rendre dans le dossier où se trouve notre Dockerfile et de taper : _docker build -t mon-image:1.0.0 ._

Le paramètre **-t mon-image:1.0.0** permet de donner un nom et un tag à l’image que nous créons. Le paramètre qui suit, le point (**.**) correspond au chemin relatif où se trouve le Dockerfile à compiler. Dans notre cas il est dans le répertoire courant.

Nous avons désormais accès à notre image dans notre Docker. Elle apparaît d’ailleurs lorsque l’on fait _docker images_.

### Système de cache des Dockerfiles

Les Dockerfiles possèdent un système de cache qui leur permet de rendre plus rapide le build d’un Dockerfile modifié. Lors du processus de build, chaque ligne du fichier est mise en cache et devient un **layers**. Ainsi, lorsque l’on rebuild un Dockerfile, il se servira de ses **layers** pour ne pas devoir build à nouveau les lignes mises en cache. Par exemple, si l’on modifie la quatrième ligne, le build se servira du cache des 3 premières **layers** (trois premières lignes build), puis buildera chaque ligne à partir de la quatrième (tout en mettant en cache les nouveaux **layers**).

## Vision globale du processus Docker

Si vous avez lu le premier article sur Docker, vous devez sûrement vous souvenir du schéma récapitulatif que nous avons vu après avoir abordé les notions de conteneurs, images et Dockerfile. Je vous propose d’y intégrer les commandes : ![processusdocker.png](https://assets.axopen.com/assets/uploads/processusdocker_daafeb5d9f.png)

Avec docker-build, on peut passer d’un Dockerfile à une image. En faisant _docker run <nom_image>_, on créé un conteneur à partir d’une image.

## Le docker-compose

Jusqu’à présent, nous nous sommes contentés de lancer des conteneurs par le biais du shell. Bien que cette opération ne soit pas compliquée à réaliser, vous conviendrez qu’elle peut s’avérer longue et répétitive lorsque l’on possède plusieurs conteneurs avec différents paramètres. C’est là que le **docker-compose.yml** entre en jeu ! Il est une alternative aux commandes.

### Notre premier docker-compose

Le docker-compose est un fichier qui permet de lister l’ensemble des conteneurs que l’on souhaite déployer. Ce fichier est écrit au format yml.

Voici un exemple de docker-compose : ![Commandedocker11.png](https://assets.axopen.com/assets/uploads/Commandedocker11_fbca0ac162.png)

Décortiquons-le. La partie qui nous intéresse se trouve dans **service**, et liste l’ensemble des conteneurs qui doivent être lancés : ici, il n’y a que **db**.

1. On créé un conteneur à partir de la version **8.0.17** de l’image **mysql** que l’on a préalablement récupéré du Dockerhub via la commande _docker pull mysql:8.0.17_
2. Ce conteneur s’appelle **database** (c’est le nom qui apparaît lorsque l’on fait _docker ps_). Ce nom lui est donné par la propriété **container_name**.
3. On précise le contenu des variables d’environnement que l’image attend afin de pouvoir configurer les identifiants de connexion.

Vous devez sans doute vous demander quelle est la différence entre **db** et le nom donné par la propriété **container_name**. Le **container_name** correspond au nom que le conteneur possède dans la liste des conteneurs, là où **db** correspond au nom de domaine que le conteneur possède au sein du **DNS Docker**. Ainsi, si un conteneur souhaite accéder à la base de données, il lui suffira de rentrer comme adresse http **db**. Vous commencez à entrevoir la puissance de Docker ? 😊

### Propriétés disponibles du docker-compose

Il y a plein de propriétés que l’on peut mettre au sein de la déclaration d’un conteneur dans le docker-compose que nous n’allons pas détailler dans cet article. En voici une liste non exhaustive :

- **ports** : Permet de faire le mappage entre les ports à l’intérieur du conteneur et les ports qui leur correspondent à l’extérieur du conteneur, c’est-à-dire dans le système Docker.
- **volumes :** Permet d’utiliser un répertoire virtuel qui ne se supprime pas lorsqu’un conteneur est supprimé. C’est l’une des manières utilisées pour persister les données d’un conteneur.
- **build :** Permet de renseigner le Dockerfile sur lequel le conteneur se base. Au lancement du docker-compose, le Dockerfile va être build et transformé en une image qui va être utilisée par le conteneur.
- **depends_on :** On y mentionne la liste des conteneurs dont un conteneur a besoin pour fonctionner. Le conteneur est démarré uniquement lorsque l’ensemble de ses dépendances le sont.

### Lancer et arrêter un docker-compose

Pour lancer un docker-compose.yml, il suffit de se rendre dans le répertoire où il se trouve et de taper _docker-compose up_.

La commande _docker-compose down_ permet quant à elle d’arrêter et de supprimer l’ensemble des conteneurs qui ont été instanciés par le docker-compose.

## Conclusion sur Docker et les conteneurs

Et voilà, avec ce que l’on a vu dans ces deux articles, vous êtes désormais capable de déployer vos premiers conteneurs ! Nous n’avons effleuré qu’une partie infime de ce que Docker propose, alors si vous voulez en savoir plus, n’hésitez pas à aller vous renseigner. Voici quelques liens que l’on vous conseille pour aller plus loin :

La documentation officielle : [https://docs.docker.com/](https://docs.docker.com/)

Une vidéo très complète de freeCodeCamp : [https://www.youtube.com/watch?v=fqMOX6JJhGo](https://www.youtube.com/watch?v=fqMOX6JJhGo)

À très bientôt !