Dans cet article, je vais configurer une répartition de charge qui va dispatcher le trafic vers 3 serveurs web nginx. Ces 4 serveurs sont tous hébergés sur des conteneurs docker.
Environnement
- Ubuntu 19.10
- Docker version 19.03.2
Attention : Toutes les commandes docker lancées ici le sont en tant qu’utilisateur root. Faites attention à ce que vous faites !
Situation cible
Le schéma ci-dessous indique la configuration cible.
Serveurs web nginx
La première étape consiste à créer un fichier index.html
différent dans votre répertoire de travail, pour chaque serveur web, afin de pouvoir les identifier facilement lors des tests.
~/Documents/reverse-proxy $ mkdir www1 ~/Documents/reverse-proxy $ echo "<h1>Hello 1</h1>" > www1/index.html ~/Documents/reverse-proxy $ mkdir www2 ~/Documents/reverse-proxy $ echo "<h1>Hello 2</h1>" > www2/index.html ~/Documents/reverse-proxy $ mkdir www3 ~/Documents/reverse-proxy $ echo "<h1>Hello 3</h1>" > www3/index.html ~/Documents/reverse-proxy $ tree ./ ./ ├── www1 │ └── index.html ├── www2 │ └── index.html └── www3 └── index.html
Une fois que les fichiers sont crées, on peut lancer les conteneurs. Les conteneurs utilisés sont les conteneurs nginx de base.
nginx1
~/Documents/reverse-proxy $ docker container run --rm \ --name nginx1 \ -v /home/fox/Documents/reverse-proxy/www1:/usr/share/nginx/html:ro \ -d \ -p 8081:80 \ nginx
nginx2
~/Documents/reverse-proxy $ docker container run --rm \ --name nginx2 \ -v /home/fox/Documents/reverse-proxy/www2:/usr/share/nginx/html:ro \ -d \ -p 8082:80 \ nginx
nginx3
~/Documents/reverse-proxy $ docker container run --rm \ --name nginx3 \ -v /home/fox/Documents/reverse-proxy/www3:/usr/share/nginx/html:ro \ -d \ -p 8083:80 \ nginx
Options utilisées
docker container run
: Permet de lancer le conteneur.--rm
: indique à docker de supprimer le conteneur ainsi que son système de fichiers.--name
: donne un nom au conteneur. Si cette option n’est pas indiquée, un nom aléatoire sera donné. -v
: indique les volumes à monter dans le conteneur. Dans ce cas-là, je monte en lecture (option ro
à la fin) le répertoire www1
, www2
ou www3
de mon hôte local à l’emplacement /usr/share/nginx/html
du conteneur. Ce répertoire est le répertoire par défaut permettant de stocker les fichiers HTML servis par nginx. Il est important de mettre le chemin absolu pour identifier les répertoires locaux. -d
: permet de détacher le conteneur du terminal actuel. Pour se connecter au conteneur par la suite, on utilise la commande docker container exec -it <nom_du_conteneur> /bin/bash
.-p
: mappe le port du host local sur le port du conteneur.nginx
: indique quel modèle de conteneur utiliser.
Vérifications
Une fois que les 3 conteneurs sont lancés, on peut vérifier qu’ils sont bien lancés :
~fox/Documents/reverse-proxy $ docker container ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3daa11c28579 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8083->80/tcp nginx3 0cc187ff28b4 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8082->80/tcp nginx2 9239a53b4480 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8081->80/tcp nginx1
On va également tester leur bon fonctionnement en se rendant aux l’URL http://127.0.0.1:8081, http://127.0.0.1:8082 et http://127.0.0.1:8083 afin d’accéder respectivement à nginx1, nginx2 et nginx3. On devrait voir s’afficher les pages suivantes
Répartiteur de charge nginx
La répartition de charge du trafic est également effectuée par un conteneur docker nginx. Pour les serveurs web, le fichier de configuration par défaut était suffisant, mais ce n’est pas le cas pour le loadbalancer. Il va donc falloir créer un fichier de configuration spécifique, et le rendre accessible au conteneur. En effet, ce n’est pas conseillé de modifier la configuration directement dans le conteneur, car celle-ci serait perdue en cas de redémarrage de celui-ci.
Fichier de configuration nginx
Les lignes importantes à modifier/ajouter sont les lignes 6 à 10 et 16 à 20.
Les premières permettent d’indiquer les serveurs destination de la répartition de charge. si aucune option n’est ajoutée avant la liste des serveurs, ils vont être choisis en mode round robin, donc les uns après les autres dans la liste. Il est possible une répartition selon le nombre de connexion (option least_conn;
) ou encore un hash IP en fonction de l’adresse IP source utilisée (option ip_hash;
). Il est également possible d’assigner un poids à chaque serveur, en indiquant l’option weight=x
sur la même ligne que la déclaration du serveur.
Remarque : Pour récupérer l’adresse IP de chaque conteneur docker nginx, on peut utiliser la commande suivante :
~/Documents/reverse-proxy $ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <nom_du_conteneur>
Une fois les adresse IP récupérées, on peut les indiquer dans le fichier de configuration nginx.
events { worker_connections 1024; } http { upstream backend { server 172.17.0.2; server 172.17.0.3; server 172.17.0.4; } server { listen 80 default_server; location / { proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://backend; } } log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; include /etc/nginx/conf.d/*.conf; }
Ce fichier de configuration est mis dans un répertoire rp
pour être utilisé par le conteneur.
~/Documents/reverse-proxy $ tree ./ ./ ├── rp │ └── nginx.conf ├── www1 │ └── index.html ├── www2 │ └── index.html └── www3 └── index.html
Une fois que le fichier de configuration est correctement crée, on peut lancer le conteneur.
~/Documents/reverse-proxy $ docker container run --rm \ --name rp \ -v /home/fox/Documents/reverse-proxy/rp/nginx.conf:/etc/nginx/nginx.conf:ro -d \ -p 80:80 \ nginx
Les options utilisées ici sont les mêmes que pour le lancement des serveurs web, à l’exception près qu’on ne mappe plus un répertoire sur un répertoire, mais un fichier sur un autre fichier.
Informations sur les conteneurs
Nom du conteneur | Adresse IP | Port en écoute |
host | 172.17.0.1 | N.A. |
nginx1 | 172.17.0.2 | 8081 |
nginx2 | 172.17.0.2 | 8082 |
nginx3 | 172.17.0.2 | 8083 |
rp | 172.17.0.2 | 80 |
Vérifications
Une fois que le loadbalancer est lancé, on peut a nouveau vérifier que tous nos conteneurs fonctionnent bien.
~fox/Documents/reverse-proxy $ docker container ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fe66cd87549f nginx "nginx -g 'daemon of…" 3 hours ago Up 3 hours 0.0.0.0:80->80/tcp rp 3daa11c28579 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8083->80/tcp nginx3 0cc187ff28b4 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8082->80/tcp nginx2 9239a53b4480 nginx "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:8081->80/tcp nginx1
Maintenant, en se rendant à l’URL http://127.0.0.1, on peut observer qu’à chaque actualisation, on tombe sur un autre serveur web.
Conclusion
La prochaine étape est de mettre en place un monitoring de ces conteneurs. On génèrera du trafic vers le répartiteur de charge afin de vérifier que celui-ci répartit bien le trafic de manière équitable entre les serveurs (ou d’une autre manière, en fonction de la configuration).
Sources
- https://docs.docker.com/engine/reference/run/#clean-up—rm
- https://docs.docker.com/engine/reference/commandline/exec/
- http://nginx.org/en/docs/http/load_balancing.html