Nous allons nous initier à Docker à travers un exemple. L’objectif étant de construire deux serveurs web avec Nginx qui seront en backend. En frontal se trouvera un loadbalancer avec HAProxy dont le rôle est de répartir la charge du trafic HTTP sur les deux serveurs web.

Nous allons utiliser les commandes de base de Docker. L’infrastructure est très simple, mais pour bien visualiser les choses voici un petit schéma complémentaire.

Load balancer

Pour installer Docker et vous initier, je vous recommande le guide officiel du projet. La documentation est complète.

Commençons par lancer notre premier serveur web. Pour cela on utilise la commande suivante :

$ docker run -d nginx:alpine
a775f215bf0c3d309dc58cbe9c13f741496a712affd43e58b572d1bd0cc2a021

Détaillons la commande :

  • -d est pour lancer le conteneur en mode démon ce qui le fera tourner en arrière plan.
  • nginx:alpine est le nom de l’image que l’on souhaite utiliser. Ici c’est l’image Nginx utilisant la distribution Alpine.

Pour information, Alpine est une distribution Linux très légère disposant du strict minimum. Un conteneur sous Alpine prendra donc bien moins de place qu’un conteneur sous une distribution plus classique. Si l’on avait spécifié uniquement « nginx » c’est une image sous base Debian qui aurait été utilisée. À vous de faire votre choix, mais n’oublier pas que l’un des atouts de la conteneurisation est d’utiliser le moins d’espace possible.

L’image nginx est une image officielle disponible sur le hub de Docker. Lors du lancement d’une image via « docker run », Docker vérifie que l’image n’est pas déjà disponible en local. Dans le cas contraire, l’image est téléchargée du Hub Docker. Il existe une multitude d’images en fonction des besoins.

On peut vérifier que notre conteneur est lancé avec la commande « docker ps » :

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a775f215bf0c nginx:alpine "nginx -g 'daemon ..." 2 minutes ago Up 2 minutes 80/tcp, 443/tcp zealous_clarke

On voit que le conteneur exécute la commande « nginx » en mode démon permettant de lancer le serveur web. Les ports du conteneur exposés par défaut sont le 80 et 443. Nous n’avons pas défini de nom à notre conteneur lors de sa création. Docker est alors sympa, car il se charge de définir un nom aléatoire qui est ici « zealous_clarke ».

Pour arrêter notre conteneur, un simple « docker stop nom_du_conteneur » suffit.

Par défaut Nginx tourne sur le port 80. Si l’on souhaite accéder au port 80 de notre conteneur facilement, il est nécessaire de faire une redirection de port vers notre machine hôte.

$ docker run -d -p8080:80 nginx:alpine
1b0f889b7617c772945c8c438578d159745be900a60636729d9fa01025097173

Nous redirigeons le port 80 de notre conteneur vers le port 8080 de notre machine faisant hôte. Il est alors possible d’accéder au serveur web sans même connaître l’adresse ip du conteneur.

$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Nous avons vu très brièvement quelques bases mais revenons à notre exemple. Lançons deux conteneurs Nginx en les nommant web1 et web2.

$ docker run -d --name web1 nginx:alpine
$ docker run -d --name web2 nginx:alpine

On vérifie que nos deux conteneurs sont en fonctionnement.

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11b95b3eabe5 nginx:alpine "nginx -g 'daemon ..." 1 second ago Up 1 second 80/tcp, 443/tcp web2
93b8b9385974 nginx:alpine "nginx -g 'daemon ..." 27 seconds ago Up 26 seconds 80/tcp, 443/tcp web1

balance

Passons maintenant à la partie « load balancer ». L’image « haproxy » fournie par Docker ne contient pas de fichier de configuration pour le service « haproxy ». Il faut donc obligatoirement fournir ce fichier pour que « haproxy » puisse se lancer correctement. Pour cela créons un fichier de configuration « haproxy.cfg » dans un répertoire de nôtre machine hôte.

$ vi /home/liced/haproxy/haproxy.cfg
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice

defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
default-server port 80 maxconn 250 on-error fail-check slowstart 60s inter 1m fastinter 5s downinter 10s weight 100

frontend monfront
option forwardfor
bind 0.0.0.0:80
default_backend monback

backend monback
balance roundrobin
server web01 web1:80 check observe layer4 weight 100
server web02 web2:80 check observe layer4 weight 100

Je vous laisse le soin de vous familiariser avec haproxy si vous le souhaitez. Ce qui nous intéresse est les deux lignes relatives à nos deux serveurs web en gras. Sachez juste qu’on y met les noms des deux serveurs web qui permettront à haproxy de savoir vers quelles destinations rediriger les requêtes HTTP. Nous verrons plus tard comment le conteneur haproxy peut connaître les adresses de web1 et web2.

Lançons notre conteneur haproxy :

$ docker run --name lb -d -p8080:80 --link web1 --link web2 -v /home/liced/haproxy:/usr/local/etc/haproxy haproxy:alpine

Voyons en détail la commande :

  • –name lb permet de nommer notre conteneur lb.
  • -d permet de lancer le conteneur en mode démon. Il tournera en arrière-plan.
  • –link web1 et –link web2 permet de lier le conteneur lb à nos deux conteneurs web. Cela va modifier le fichier hosts du conteneur en ajoutant les alias web1 et web2 associés à leur ip. Ainsi lb sera capable de communiquer avec web1 et web2 automatiquement sans que nous ayons à manipuler la moindre adresse.
  • -v permet de partager un répertoire de la machine hôte vers le conteneur. Notre fichier de configuration se retrouve alors dans le répertoire de configuration de haproxy.
  • haproxy:alpine est l’image sous base de distribution Alpine.

Nos 3 conteneurs sont alors actifs :

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1cb102389c7 haproxy:alpine "/docker-entrypoin..." 8 minutes ago Up 8 minutes 0.0.0.0:8080->80/tcp lb
11b95b3eabe5 nginx:alpine "nginx -g 'daemon ..." 49 minutes ago Up 49 minutes 80/tcp, 443/tcp web2
93b8b9385974 nginx:alpine "nginx -g 'daemon ..." 49 minutes ago Up 49 minutes 80/tcp, 443/tcp web1

Connectons-nous sur le conteneur lb actuellement actif avec la commande docker exec :

$ docker exec -it lb /bin/sh

En gros, cela vous permet de lancer un shell dans votre conteneur et de pouvoir interagir avec lui grâce à vos commandes favorites.

Voici les paramètres en détail :

  • exec permet d’exécuter une commande dans un conteneur actif.
  • -it permet de lancer un terminal et de le rendre interactif.
  • /bin/sh spécifie le type de shell que l’on souhaite utiliser. Par défaut, la distribution Alpine ne dispose que de sh, d’autres distributions comme Debian permettent de lancer bash.

On peut voir que le fichier hosts du conteneur lb a été mis à jour automatiquement avec les adresses des conteneurs web1 et web2. C’est grâce à l’option « link » utilisée lors du lancement que hosts est à jour.

/ # cat /etc/hosts
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
ff00::0    ip6-mcastprefix
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters
172.17.0.2    web1 93b8b9385974
172.17.0.3    web2 11b95b3eabe5
172.17.0.4    a1cb102389c7

Bref, normalement notre petite infrastructure est fonctionnelle. Sous la machine hôte on peut le vérifier avec un curl, un wget ou directement avec un navigateur que l’on accède bien à nos serveurs web :

$ curl http://localhost:8080

On récapitule :

  • docker run nom_image pour lancer un conteneur avec l’option -d si l’on souhaite le lancer en arrière-plan.
  • docker pull nom_image pour récupérer une image du hub docker
  • docker ps pour visualiser les conteneurs actifs avec l’option -a pour voir les anciens conteneurs.
  • docker exec -it nom_conteneur /bin/sh pour se connecter à un conteneur et interagir avec lui via un shell.
  • docker stop pour arrêter un conteneur.
  • docker rm nom_conteneur pour supprimer un conteneur.

Voici une brève introduction à Docker via quelques commandes basiques. Utilisé à bon escient Docker révolutionne la manière de construire ses applications en s’affranchissant totalement des différents types d’infrastructures souvent incompatibles entre elles et en facilitant les transitions entre développement et production. C’est ce qu’on appelle le « DevOps ».

Bien sûr, il est possible de créer ces mêmes conteneurs de manière automatique grâce à un système de templates via les Dockerfiles et Docker Compose qui fera l’objet d’un autre article.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Post Navigation