Introduction à Docker

Veuillez noter : Le guide suivant ne peut pas être mis en œuvre dans un produit d’hébergement Web mutualisé, mais uniquement sur un serveur non géré.
Aperçu
Les applications Web modernes se composent souvent de plusieurs composants qui doivent être installés individuellement. Cela nécessite souvent des modifications du système Linux sous-jacent. Des dépendances supplémentaires doivent être installées sur le système. L’installation d’une application sur un système Linux peut prendre plusieurs heures. Une fois l’installation terminée, une application peut être profondément intégrée au système Linux et plus encapsulée. Pour contourner ce problème, un serveur virtuel distinct peut être configuré pour chaque application Web complexe. Cependant, cela pose les problèmes suivants :
Pas de déduplication : Si plusieurs images sont installées sur un serveur, par exemple, basées sur une image Linux Debian, toutes les données de l’image doivent être stockées plusieurs fois.
Compatibilité : L’image doit être compatible avec la solution de virtualisation utilisée. La portabilité est donc limitée.
Virtualisation complète : Il s’agit d’une virtualisation complète, où même les pilotes de périphériques, par exemple, pour les cartes réseau, sont entièrement virtualisés. Cela entraîne un surcoût supplémentaire.
Pour résoudre ce problème, Docker a été développé. Des concepts et termes importants de l’écosystème Docker incluent :
Une image est le disque dur du conteneur. Une image peut être composée de plusieurs couches. Celles-ci sont créées lors de la création du conteneur et ne peuvent plus être modifiées par le conteneur en cours d’exécution. Chaque couche ne stocke que les modifications par rapport à la couche précédente. Lorsqu’un conteneur est démarré sur la base d’une image, une couche de conteneur est créée, qui peut être décrite individuellement par le conteneur respectif. Les images peuvent être construites sur d’autres images. Grâce à des couches et des images imbriquées, une déduplication des données stockées peut être réalisée. Une image n’est pas utilisée pour le stockage persistant des données. Par exemple, lors de la mise à jour du logiciel, l’ensemble de l’image est remplacé. Elle est donc considérée comme sans état.
Un conteneur est une instance de l’image. Il dispose d’un environnement Linux paravirtualisé mis à disposition par l’hôte.
Un volume sert à stocker des données persistantes d’un conteneur, telles que le contenu d’une base de données. Si une mise à jour du conteneur doit être effectuée ultérieurement, l’image du conteneur peut être échangée, tandis que les données restent intactes.
Le moteur est le processus côté serveur sur lequel l’environnement Docker est exécuté.
Un registre Docker permet de partager des images avec une organisation ou également publiquement. Dans l’installation Docker standard, Docker Hub est intégré en tant que registre par défaut.
Un dépôt Docker est un ensemble d’images Docker qui peuvent être stockées dans un registre. Un dépôt peut également contenir plusieurs versions différentes d’une image.
Le Dockerfile est la recette d’un conteneur. Il contient les instructions pour créer l’image d’un conteneur. À chaque instruction, une nouvelle couche est créée à l’intérieur du conteneur, de sorte qu’en cas de modification du Dockerfile, l’ensemble de l’image n’a pas besoin d’être recréé. Dans un conteneur Debian, par exemple, il peut contenir des commandes
aptqui installent des paquets.
Cas d’utilisation de Docker
Docker est adapté comme environnement de développement. Les développeurs peuvent disposer d’un environnement cohérent pour développer des applications basées sur Linux via des conteneurs Docker. Les conteneurs Docker, qui contiennent effectivement un Linux, peuvent également être lancés de manière identique sur d’autres plates-formes, comme Windows ou macOS.
Le logiciel développé peut être directement utilisé en production sous forme de conteneurs.
Docker facilite la documentation du logiciel installé. Dans le Dockerfile ou Docker Compose, les instructions pour créer un conteneur sont enregistrées. Ces recettes peuvent être gérées et versionnées dans un système de contrôle de version, tel que git.
Docker est bien adapté pour démarrer des applications Web sur des serveurs individuels qui nécessiteraient par ailleurs une installation complexe. Elles fonctionnent ainsi dans un environnement isolé et peuvent être facilement supprimées du serveur respectif si nécessaire.
Dans le cadre de l’intégration continue, des conteneurs Docker peuvent être intégrés dans des pipelines à l’aide d’outils tels que Travis CI, Jenkins ou Wercker pour compiler et tester des applications avant que le déploiement ne soit automatisé via Docker.
Docker est également adapté pour le déploiement de grandes applications évolutives basées sur des microservices. Grâce à la grande flexibilité de Docker, des conteneurs peuvent être rapidement lancés de manière cohérente sur plusieurs serveurs.
Quand ne pas utiliser Docker
Docker n’est pas adapté pour les applications avec une interface graphique de bureau, car il a été principalement conçu pour les applications Web et en ligne de commande.
L’utilisation de Docker peut entraîner de légers impacts sur les performances. Il n’est donc pas adapté aux applications hautement performantes.
Étant donné qu’il s’agit d’une paravirtualisation, aucune modification du noyau Linux ne peut être effectuée. Le chargement de modules du noyau n’est pas possible dans les conteneurs Docker.
Docker Compose
Pour créer et gérer plusieurs conteneurs faisant partie d’un service, Docker Compose a été inventé comme un outil simple. Dans un fichier docker-compose.yml, les conteneurs individuels et leurs relations, par exemple, volumes, réseaux et redirections de port, peuvent être définis. Docker Compose peut également créer des conteneurs basés sur des Dockerfiles.
L’utilisation de Docker Compose sera expliquée dans un exemple suivant. À la fin de l’article, vous trouverez une collection d’importantes commandes pour Docker Compose.
Grandes environnements Docker
Pour gérer de grands environnements Docker, divers outils et solutions supplémentaires sont disponibles. Voici un bref aperçu de ces solutions. Une solution connue est, par exemple, Kubernetes. Kubernetes prend en charge les tâches suivantes :
- Création de services d’application qui s’étendent sur plusieurs conteneurs.
- Collecte de données de télémétrie, par exemple pour surveiller la charge et les performances.
- Gestion des permissions d’accès pour la gestion des conteneurs.
- Gestion de la réplication de services pour augmenter la disponibilité.
Rancher est une solution couramment utilisée pour la gestion de Kubernetes.
Mise en pratique : installation de Docker sur un serveur Debian
Il est recommandé d’utiliser la version communautaire de Docker. L’installation est montrée ici pour Debian 10 “Buster”. Un guide détaillé est disponible sur le site Web de Docker. Docker sera installé comme suit, non pas à partir des dépôts officiels Debian, mais directement à partir du dépôt officiel Docker. Cela permet de le maintenir à jour.
Veuillez vous connecter à votre serveur via SSH. Tout d’abord, toutes les versions de Docker précédemment installées doivent être supprimées :
sudo apt update
sudo apt remove docker docker-engine docker.io containerd runc
Veuillez installer les dépendances nécessaires à partir de Debian :
sudo apt install \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
et ajoutez la clé PGP de Docker :
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
Ajoutez ensuite le dépôt Docker :
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
Dans la prochaine étape, Docker peut être installé :
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
Vous pouvez permettre à d’autres utilisateurs sur le système Linux de gérer Docker. Pour cela, l’utilisateur doit être membre du groupe docker. Si le groupe n’existe pas, veuillez le créer avec :
sudo groupadd docker
et ajoutez ensuite l’utilisateur au groupe :
sudo usermod -aG docker <UTILISATEUR>
Pour vérifier si Docker a été correctement installé, un conteneur “Hello World” peut être démarré :
docker run hello-world
Après l’exécution de la commande, l’image requise sera automatiquement téléchargée et démarrée :
max@demoserver ➜ ~ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:fc6a51919cfeb2e6763f62b6d9e8815acbf7cd2e476ea353743570610737b752
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
Si vous voyez ce message, alors vous avez installé Docker avec succès.
Portainer comme exemple d’application Web
Pour montrer comment une application Web peut être démarrée dans Docker, nous allons prendre Portainer comme exemple. Il s’agit d’un outil de gestion basé sur le Web pour Docker.
Nous créons un volume où les données persistantes de Portainer seront stockées avec la commande suivante :
docker volume create portainer_data
Les volumes existants peuvent être affichés avec la commande docker volume ls, ce qui dans notre cas ressemblera à ceci :
root@demoserver:~# docker volume ls
DRIVER VOLUME NAME
local portainer_data
root@demoserver:~#
La commande suivante démarre Portainer :
docker run -d -p 9000:9000 --name=portainer --restart=unless-stopped -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
Les options -p établissent un redirection de port de l’hôte vers le conteneur. La numérotation du port de l’hôte est d’abord spécifiée. L’option --name permet de définir un nom pour le conteneur. Avec l’option --restart, vous pouvez définir quand le conteneur doit être démarré. L’option unless-stopped redémarre uniquement le conteneur s’il n’a pas été arrêté. Avec l’option -v, on spécifie quel volume doit être monté où dans le conteneur. Pour Portainer, nous transmettons également le socket Unix /var/run/docker.sock, ce qui permet au conteneur d’accéder à Docker en cours d’exécution sur l’hôte pour pouvoir le gérer. Enfin, il faut spécifier quelle image doit être utilisée. Dans notre cas, c’est l’image portainer/portainer.
En exécutant la commande, les images requises sont automatiquement téléchargées depuis le serveur et le conteneur est démarré. Avec la commande docker ps, les conteneurs en cours d’exécution peuvent être affichés :
root@demoserver:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e12e5ebbd96a portainer/portainer "/portainer" 6 minutes ago Up About a minute 0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp portainer
root@demoserver:~#
Ici, on peut voir l’ID du conteneur, ainsi que l’image sur laquelle repose le conteneur.
Le navigateur Web peut maintenant accéder à l’interface Web de Portainer, par exemple via http://demoserver.mustermann-domain.de:9000/. Ici, un mot de passe pour l’utilisateur admin de Portainer doit être défini :

Pour se connecter à Docker, sélectionnez l’option “Local” afin que le socket transmis soit utilisé :

Dans Portainer, vous pouvez désormais gérer graphiquement l’instance Docker :

Si vous souhaitez maintenant arrêter le conteneur en cours d’exécution, vous pouvez le faire en utilisant son ID avec la commande docker stop <ID> :
root@demoserver:~# docker stop 36fc433abcef
36fc433abcef
root@demoserver:~#
De même, le conteneur peut être redémarré avec docker start <ID>. Avec la commande docker rm <ID>, le conteneur sera supprimé.
Un problème dans cet exemple est que le serveur Web sous le port 8000 ne propose qu’un serveur HTTP. Pour une connexion sécurisée à Portainer, une connexion HTTPS chiffrée SSL serait nécessaire. À ce stade, il est courant de configurer un proxy inverse HTTP sur l’ordinateur hôte, par exemple nginx, qui reçoit la connexion chiffrée du client et la transmet au conteneur.
Créer vos propres conteneurs
Pour mieux comprendre Docker, nous montrerons comment créer votre propre image à l’aide de Docker Compose. Dans cet exemple, il s’agit d’un petit serveur Web basé sur le framework Python Flask et d’un serveur de base de données Redis. Avec Docker Compose, il est possible de créer plusieurs conteneurs Docker ensemble.
Pour la nouvelle application Web, nous créons un répertoire, par exemple nommé mon-premier-conteneur. Dans ce répertoire, nous créons le fichier app.py avec le contenu suivant :
# mon-premier-conteneur/app.py
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Mon application exemple a été affichée %s fois.' % redis.get('hits').decode("utf-8")
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=8080)
Pour Python, nous avons également besoin d’un fichier nommé requirements.txt dans lequel les dépendances Python sont spécifiées :
Flask==1.1.1
redis==3.4.1
La véritable “recette” pour le conteneur Docker est stockée dans le fichier Dockerfile :
FROM ubuntu:latest
MAINTAINER Max Mustermann "max@mustermann-domain.fr"
RUN apt-get update -y && \
apt-get install -y python3 python3-pip python3-dev
# Nous copions d'abord juste le requirements.txt pour profiter du cache Docker
COPY ./requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip3 install -r requirements.txt
COPY . /app
ENTRYPOINT [ "python3" ]
CMD [ "app.py" ]
Les conteneurs peuvent ensuite être créés et démarrés avec la commande
docker-compose up --build
L’option --build garantit que le conteneur est reconstruit avec toutes les étapes. Les images requises sont automatiquement téléchargées, et le conteneur est créé et démarré sur cette base. Avec l’option -d, par exemple docker-compose up -d, le conteneur peut être démarré en arrière-plan.
Une fois le conteneur d’exemple démarré, il peut être accédé via l’interface Web sur le port 8080, par exemple via https://demoserver.mustermann-domain.de:8080/. Le texte du programme d’exemple sera affiché : “Mon application exemple a été affichée 5 fois”.
Avec la commande
docker-compose exec web /bin/bash
vous pouvez démarrer un shell bash à l’intérieur du conteneur web.
Avec docker-compose down, le conteneur peut être arrêté.
Collection de commandes importantes pour les débutants
Conteneurs
docker container ls -a- Afficher les conteneurs existants. Avec l’option supplémentaire-a, les conteneurs non en cours d’exécution sont également affichés.docker ps- Montre les conteneurs en cours d’exécutiondocker run -d <NOM_IMAGE>- Créer et démarrer un conteneur basé sur une image. L’option-ddémarre le conteneur en arrière-plan.docker start <ID_CONTENEUR>- Démarre un conteneur donné par son IDdocker stop <ID_CONTENEUR>- Arrête un conteneur en cours d’exécution identifié par son IDdocker kill <ID_CONTENEUR>- Interrompt un conteneur en cours d’exécution et le terminedocker rm <ID_CONTENEUR>- Supprime un conteneur arrêtédocker log -f <ID_CONTENEUR>- Affiche la sortie de journalisation d’un conteneur par son ID.docker attach <ID_CONTENEUR>- ici vous pouvez interagir avec le processus en cours d’exécution dans le conteneur via les entrées et sorties standard. Si ce processus principal du conteneur est arrêté, le conteneur est également arrêté. Cela signifie que le raccourci clavierCtrl+Cou la commandeexitpeuvent éventuellement arrêter le conteneur.docker exec -it <ID_CONTENEUR> /bin/sh- la commande démarre un shell à l’intérieur du conteneur
Images
docker image ls- Affiche les images existantesdocker image prune- Supprime les images non nécessairesdocker image pull <NOM>etdocker image push <NOM>récupèrent ou copient des images depuis ou vers un registre par leur nomdocker image rm <NOM>supprime une image inutilisée
Docker Compose
docker-compose up- démarre les conteneurs qui sont définis dans le fichierdocker-compose.ymldu répertoire actueldocker-compose down- arrête une collection en cours de conteneurs définis dans un fichierdocker-compose.ymldocker-compose ps- Affiche les conteneurs gérés actuellement par Docker Composedocker-compose start <NOM>etdocker-compose stop <NOM>démarrent ou arrêtent les conteneurs identifiés par le nom enregistré dans le fichierdocker-compose.yml