Utiliser docker
Dans cet article, nous allons utiliser docker et montrer son efficacité pour déployer des environnements rapidement ! Notre objectif sera le suivant : Exposer un serveur web.
Sommaire
Prérequis
Installer docker
Il faut ajouter le dépôt communautaire au gestionnaire de dépendances d’Alpine :
echo http://dl-cdn.alpinelinux.org/alpine/latest-stable/community > /etc/apk/repositories
Ensuite il faut mettre à jour le gestionnaire de dépendances :
apk update
Puis on installe docker :
apk add docker
Démarrer Docker
On va démarrer le service docker :
service docker start
On ajoute le démarrage du service au boot de la machine :
rc-update add docker boot
On vérifie si le service est effectif :
service docker status
Résultat
Puis on redémarre la machine :
reboot
Mais docker, a quoi ça sert ?
C’est un outil formidable pour monter des environnements d’exécutions et des environnements préfabriqués. Vous pouvez imaginer ça comme un superviseur de machine virtuelle, mais sans interface graphique ! Voici une illustration pour mieux comprendre :
Informations utiles
Première image
Les règles de la construction de l’image sont les suivantes :
- On souhaite se baser sur une image alpine existante.
- Une copie du script qui affiche la chaine de caractère "HELLO WORL" va être copiée dans l'image.
- L'image docker va éxecuter le script helloworld.sh lorsqu'elle est déployée.
Création du dossier
On créer un dossier de travail pour l’image docker :
mkdir -p /docker/demo && cd /docker/demo
Création du script
On initialise un script (helloworld.sh) qui va afficher la chaine de caractères “Hello World” :
nano helloworld.sh
helloworld.sh
#!bin/ash
echo "HELLO WORLD"
Création du Dockerfile
Créons le fichier de construction pour docker :
nano Dockerfile
Structure du dossier /docker/demo :
.
├── Dockerfile
└── helloworld.sh
Dockerfile
## On récupère une image depuis docker, qui tire une image d'alpine en version 3.7 que docker expose.
FROM alpine:3.7
## On exécute à l'intérieur de cette image (alpine 3.7) un update des dépendances
RUN apk update
## On copie le script qui va nous permettre d'écrire "HELLO WORLD" de la machine (./helloworld.sh, correspond à notre script, / correspond au répertoire de destination à l'intérieur de l'image), donc, on copie le fichier helloworld.sh à la racine de l'image alpine 3.7.
## SOURCE | ## DESTINATION
COPY ./helloworld.sh /
## On rends le fichier sh exécutable (qui a été copié à la racine de l'image alpine).
RUN chmod +x /helloworld.sh
## On renseigne que le point d'entrée de l'image, lorsqu'on la lance, est notre script, on verra plus tard à quoi sert cette ligne.
ENTRYPOINT ["/helloworld.sh"]
Build de l'image
Comme l’image docker est préparée, on va pouvoir la build :
docker build . -t helloworld
Résultat
Sending build context to Docker daemon 3.072kB
Step 1/5 : FROM alpine:3.7
---> 6d1ef012b567
Step 2/5 : RUN apk update
---> Running in 95b11e5c9184
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
v3.7.3-184-gffd32bfd09 [http://dl-cdn.alpinelinux.org/alpine/v3.7/main]
v3.7.3-194-gcddd1b2302 [http://dl-cdn.alpinelinux.org/alpine/v3.7/community]
OK: 9054 distinct packages available
Removing intermediate container 95b11e5c9184
---> 1e018627922f
Step 3/5 : COPY ./helloworld.sh /
---> 41bddbbf579d
Step 4/5 : RUN chmod +x /helloworld.sh
---> Running in 207b215d50d2
Removing intermediate container 207b215d50d2
---> a8a0f4dbcde1
Step 5/5 : ENTRYPOINT ["/helloworld.sh"]
---> Running in 00fa98653d16
Removing intermediate container 00fa98653d16
---> 5babeaf05c5e
Successfully built 5babeaf05c5e
Successfully tagged helloworld:latest
Vérification de l'image
Notre image est créée avec le tag “helloworld“, listons les images docker pour vérifier :
docker image ls
Résultat
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld latest 5babeaf05c5e 5 minutes ago 5.43MB
alpine 3.7 6d1ef012b567 2 years ago 4.21MB
Exécuter l'image
Pour exécuter l’image dans l’environnement docker :
docker run helloworld
Résultat
HELLO WORLD
Explications
Lors de la commande “docker run“, comme l’ENTRYPOINT qui est configuré dans le Dockerfile est le script helloworld.sh, c’est celui-ci qui a été appelé par notre image et qui a renvoyé la chaine de caractères “HELLO WORLD“. Pour bien comprendre, voici un schéma descriptif :
Quand on utilise la commande docker run, notre image est instanciée dans le bloc "Image lancée". On a physiquement un nouveau système de fichiers qui est émulé à l'intérieur (mis à droite du bloc) qu'on appelle container dans le monde docker. C'est d'ailleurs cet aspect qui rends docker si puissant, car depuis un système on peut instancier un ou plusieurs autres systèmes, dont des systèmes préfabriques/pré-paramétrés lors de notre docker build (ou directement récupéré d'internet).
Exposer un serveur web
La demande initiale était d’exposer un serveur web, on va donc construire une nouvelle image :
mkdir -p /demo/docker/exposewebserver && cd /demo/docker/exposewebserver
Pour cela on créer un nouveau Dockerfile :
Dockerfile
## On récupère une image alpine version 3.7 depuis Docker
FROM alpine:3.7
## On mets à jour les dépendances.
RUN apk update
## On ajoute nginx (pour simplifier le montage du serveur)
RUN apk add nginx
## On crée le dossier pour éviter que NGINX râle
RUN mkdir /run/nginx
## On expose le port 80 de l'image docker instanciée
EXPOSE 80
## On quitte l'image sur un signal stop (CTRL+C) ou docker stop
STOPSIGNAL SIGQUIT
## On lance nginx
CMD ["nginx", "-g", "daemon off;"]
Résultat
Sending build context to Docker daemon 2.048kB
Step 1/7 : FROM alpine:3.7
---> 6d1ef012b567
Step 2/7 : RUN apk update
---> Using cache
---> 1e018627922f
Step 3/7 : RUN apk add nginx
---> Running in 52ef92245914
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/2) Installing pcre (8.41-r1)
(2/2) Installing nginx (1.12.2-r4)
Executing nginx-1.12.2-r4.pre-install
Executing busybox-1.27.2-r11.trigger
OK: 6 MiB in 15 packages
Removing intermediate container 52ef92245914
---> 0410d2a8d1c8
Step 4/7 : RUN mkdir /run/nginx
---> Running in cd978d869c98
Removing intermediate container cd978d869c98
---> 805dd2bdf6e6
Step 5/7 : EXPOSE 80
---> Running in 0adeebdda85c
Removing intermediate container 0adeebdda85c
---> a9f7695180e5
Step 6/7 : STOPSIGNAL SIGQUIT
---> Running in 04ebe5a0cdd2
Removing intermediate container 04ebe5a0cdd2
---> b1687008888c
Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
---> Running in e32041734a37
Removing intermediate container e32041734a37
---> 486bad5a0d0d
Successfully built 486bad5a0d0d
Successfully tagged mynginx:latest
Vérifions que l’image s’est bien créée :
docker image ls
Résultat
REPOSITORY TAG IMAGE ID CREATED SIZE
mynginx latest 486bad5a0d0d 53 seconds ago 8.02MB
helloworld latest 5babeaf05c5e 13 hours ago 5.43MB
alpine 3.7 6d1ef012b567 2 years ago 4.21MB
On lance le serveur web :
docker run mynginx
Contacter le serveur
Le port exposé par le container est le port 80 (via l’instruction EXPOSE 80), si on tape l’adresse IP de la machine virtuelle dans le navigateur (http://172.19.193.132 dans mon cas), on devrait atterrir sur notre serveur web exposé dans le container docker :
Ajustements
Pour corriger le problème, on va ajuster la commande “docker run” pour y ajouter certains paramètres :
docker run -p 80:80 mynginx # -p [portMachineHote]:[portContainerDocker]
Nouvel essai
Ressayons de charger notre page (http://172.19.193.132 dans mon cas ) :
Exécuter le container en tâche de fond
La commande “docker run” se lance dans le terminal et le bloque, ce n’est pas très pratique pour continuer à gérer sa machine, on souhaite exécuter notre container docker en tâche de fond :
docker run -d -p 80:80 mynginx
Résultat
Lister le container en exécution
Comme on a lancé le container docker en tâche de fond, on doit avoir la possibilité de le lister :
docker ps
Résultat
Stopper un container
Pour stopper le container, on va se servir de son identifiant :
docker kill b92dac652469
Un petit problème...
La commande qu’on a lancé précédemment (docker run -d -p 80:80 mynginx) comporte un problème :
A chaque lancement, un nom dynamique sera crée et va polluer le contexte d’exécution de docker.
Solution
On va rajouter des arguments à la commande :
docker run -d -p 80:80 --name="CustomNginx" --restart=always mynginx
L'argument --restart indique la politique de redémarrage que le container doit avoir.
Instancier un deuxième serveur
On souhaite instancier un nouveau serveur web, mais cette fois exposé sur le port 81 de la machine hôte :
docker run -d -p 81:80 --name "CustomNginxPort81" --restart=always mynginx
Contacter le deuxième serveur
Pour le second serveur, on va taper sur la même adresse que pour le premier en modifiant le port de destination (http://172.19.198.132:81) :
url
Résultat
Lister les containers
Pour le second serveur, on va taper sur la même adresse que pour le premier en modifiant le port de destination (http://172.19.198.132:81) :
docker ps
Résultat
Conseils
C’est un conseil qu’on donne pour tous vos besoins : essayez de chercher une image docker qui correspond déjà à votre idée sur : https://hub.docker.com/
Informations utiles
Lors de la commande docker run, si on associe un nom d’image, docker essaiera d’abord de télécharger l’image depuis internet (seulement au premier lancement, puisqu’après elle est enregistrée dans l’environnement d’exécution de docker en tant qu’image). Par exemple, essayons d’installer l’image officielle de nginx :
docker run -d -p 80:80 --name=OfficialNginx --restart=always nginx
Résultat
Administrer avec docker
On va afficher les containers en cours d’exécution :
docker ps
Résultat
Ensuite on va lister nos images pour voir l’état de notre contexte d’exécution :
docker image ls
Résultat
Commande utiles
docker volume # Permets de créer un volume docker
docker network # Permets de créer un réseau docker
docker container prune # Permets de nettoyer tous les containers docker non utilisés
docker image prune # Permets de nettoyer toutes les images docker non utilisées
docker volume prune # Permets de nettoyer tous les volumes docker non utilisés
docker network prune # Permets de nettoyer tous les réseaux docker non utilisés
Images courantes
Ci-dessous une liste d’images qui sont très utilisées dans le monde de docker (la liste est non exhaustive) :
alpine:latest # Serveur alpine pré-configuré
nginx:latest # Serveur web pré-configuré
redis:latest # Serveur de mise en cache pré-configurée
mysql:latest # Serveur avec base de données pré-configurée
debian:latest # Serveur debian pre-configuré
....
Utilisation avancée
Pour les plus déterminés d’entre vous, il est possible de faire communiquer des containers docker entre eux et de les mettre dans le même réseau. Les applications modernes qui utilisent docker sont souvent architecturées de cette façon :
- Base de données/cache (souvent redis) = 1 container.
- Router les requêtes HTTP/PROXY (souvent nginx) = 1 container.
- Code de l’application (à la guise des devs) = 1 container.