- Partie 1: Serveur HTTP statique avec
apache httpd
- Partie 2: Serveur HTTP dynamique avec
express.js
- Partie 3: Proxy inversé avec apache (configuration statique)
- Partie 4: requêtes AJAX avec JQuery
- Partie 5: Proxy inversé avec configuration dynamique
- Parties additionnelles
Cette partie du labo se trouve sur la branche suivante: fb-apache-static
Le but de cette partie est de créer une image Docker permettant de créer un serveur HTTP statique. Nous avons choisi de suivre le webcast en faisant un serveur Apache HTTPD.
Dans le dossier docker-images/apache-php-image
, il y a le Dockerfile
ainsi que le dossier src
contenant tous les éléments à copier lors de la construction de l'image. Actuellement, seule une page html incluant une librairie CSS et contenant une mise en page basique avec une image est copiée. Le Dockerfile
se base sur l'image php:7.2-apache mettant donc à disposition un serveur Apache ainsi que PHP 7.2 et copie le contenu du dossier src
vers le dossier var/www/html
.
La seule différence notable avec le webcast est que notre image embarque une version plus récente bien que cela ne change pas grand chose.
Pour construire l'image docker, il faut simplement se déplacer dans le dossier docker-images/apache-php-image
et lancer la commande suivante:
docker build -t api/apache_php
Pour construire un container et remapper le port 80 sur le port 8080 de la machine à partir de l'image précédamment créée il faut lancer la commande suivante:
docker run -p 8080:80 api/apache_php
Il suffit maintenant d'accèder au serveur depuis un navigateur par exemple: localhost:8080
Cette partie du labo se trouve sur la branche suivante: fb-express-dynamic
Le but de cette partie est de créer une image Docker permettant de créer un serveur HTTP dynamique générant du contenu JSON. Nous avons choisi de suivre le webcast en faisant un serveur Node.js utilisant le framework express.js.
Dans le dossier docker-images/express-image
se trouvent le Dockerfile
ainsi que les éléments à copier. Le Dockerfile
est construit de la manière suivante:
- Il se base sur l'image node mettant à disposition le runtime Node.js.
- Il copie le contenu du dossier
src
dans un dossierserver
. - Il installe tous les modules nécéssaires et démmare le serveur.
Actuellement, les éléments à copier sont les fichiers index.js
permettant de démarrer le serveur et package.json
décrivant le projet Node.js. Le fichier index.js
est basique et marche sur le principe suivant:
// Import d'express et création du serveur
import express from "express";
const app = express();
// Ajout d'un point d'entré à la racine retournant un nombre à virgule
// aléatoire dans l'interval [0,1[ au format JSON
app.get("/", (req, res) => {
res.json({ random: Math.random() });
});
// Le serveur écoute sur le port 3000
app.listen(3000);
Pour construire l'image docker, il faut simplement se déplacer dans le dossier docker-images/express-image
et lancer la commande suivante:
docker build -t api/express_node
Pour construire un container et remapper le port 3000 sur le port 3000 de la machine à partir de l'image précédamment créée il faut lancer la commande suivante:
docker run -p 3000:3000 api/express_node
Il suffit maintenant d'accèder au serveur depuis par exemple un navigateur: localhost:3000
Cette partie du labo se trouve sur la branche suivante: fb-apache-reverse-proxy
Le but de cette partie est de créer une image Docker permettant de créer proxy inverse en utilisant apache permettant d'accèder aux deux serveurs web précédements créés selon l'url demdandée. La configuration des adresses IP sont statiques et seront gérées dynamiquement dans la prochaine partie.
Dans le dossier docker-images/apache-reverse-proxy
se trouvent le Dockerfile
ainsi que les éléments à copier. Le Dockerfile
est construit de la manière suivante:
- Il se base sur l'image php:7.2-apache mettant donc à disposition un serveur Apache ainsi que PHP 7.2.
- Il copie le contenu du dossier
conf
dans un dossieretc/apache2
. - Il lance les commandes
a2enmod proxy proxy_http
eta2ensite 000-* 001-*
qui permettent respectivement d'activer le mode proxy et d'activer les hôtes virtuels des fichiers débutant par 000- et 001- qui sont en réalité les fichiers copiés précédemment.
Actuellement, les éléments à copier sont les fichiers 000-default.conf
, la configuration par défaut vide, et 001-reverse-proxy.conf
, notre configuration de proxy inversé. Le fichier 001-reverse-proxy.conf
marche sur le principe suivant:
<!-- Défini un hôte virtuel sur le port 80 -->
<VirtualHost *:80>
<!-- Défini le nom du serveur sur lequel le reverse proxy va agir -->
ServerName demo.api.ch
<!-- Défini l'URL à rediriger ainsi que le serveur qui va la recevoir -->
ProxyPass "/api/random" "http://172.17.0.3:3000/"
ProxyPassReverse "/api/random" "http://172.17.0.3:3000/"
<!-- Défini l'URL à rediriger ainsi que le serveur qui va la recevoir -->
ProxyPass "/" "http://172.17.0.2:80/"
ProxyPassReverse "/" "http://172.17.0.2:80/"
</VirtualHost>
Pour construire l'image docker, il faut simplement se déplacer dans le dossier docker-images/apache-reverse-proxy
et lancer la commande suivante:
docker build -t api/apache_rp
Il faut d'abord lancer les containers des serveurs statiques et dynamiques, pas besoin de remapper les ports car ils n'ont pas besoin d'être exposés sur la machine elle-même:
docker run api/apache_php
docker run api/express_node
Pour construire un container et remapper le port 80 sur le port 80 de la machine à partir de l'image précédamment créée il faut lancer la commande suivante:
docker run -p 3000:3000 api/apache_rp
Notes importantes:
-
Comme la configuration est statique, il faut s'assurer que les adresses ip définies dans le fichier
001-reverse-proxy.conf
correspondent à celles attribuées aux containers des serveurs précédement créés. -
Il faut éditer le fichier des hôtes de votre machine pour lier l'adresse
172.0.0.1
au nom de serveur défini dans de fichier001-reverse-proxy.conf
soitdemo.api.ch
.
Cette partie du labo se trouve sur la branche suivante: fb-ajax
Le but de cette partie est de créer un script JS permettant de faire une requête HTTP sur le serveur web express
depuis le serveur web apache
et d'afficher le résultat dans une div. Contrairement à la donnée demandant d'utilisier JQuery pour faire une requête AJAX, nous avons choisi d'utiliser les API webs standards (en l'occurence fetch).
Le dockerfile est le même que celui utilisé pour le serveur apache
. La modification réside dans les fichiers à copier qui embarque maintenant le script index.js
. Ce script marche sur le principe suivant:
// Attendre que le DOM soit chargé
window.addEventListener('DOMContentLoaded', () => {
// Récupérer la div qui contiendra le résultat
const random = document.getElementById("random");
// Fonction anonyme et asynchrone pour faire
// une requête HTTP se exécutée toutes les 3 secondes
setInterval(async () => {
// Faire une requête HTTP sur le serveur express
// et récupérer le résultat en format JSON
const res = await fetch("/api/random").then(res => res.json());
// Remplacer le contenu de la div par le résultat de la requête
random.innerText = res.random;
}, 3000);
});
L'image se construit de la même manière que pour le serveur apache
.
La création du container se fait de la même manière que pour le serveur apache
.
Cette partie du labo se trouve sur la branche suivante: fb-dynamic-configuration
Le but de cette partie est de créer une image Docker permettant de créer proxy inverse permettant d'accèder aux deux serveurs web précédements créés selon l'url demdandée. La configuration des adresses IP sont gérées dynamiquement.
Cette partie est implémentée grâce à docker-compose
qui permet de gérer la création de plusieurs containers en même temps.
Le reverse proxy dynamique est implémenté grâce au fait que docker-compose
crée un network entre les containers et leur assigne un hostname, basé sur le nom du service dans le fichier docker-compose.yml
, résolu automatiquement en adresse IP.
Notre configuration est la suivante:
version: "3.9"
services:
reverse-proxy: # Nom du service
build: ./docker-images/apache-reverse-proxy # Emplacement du Dockerfile
container_name: api-reverse-proxy # Nom du container
image: api/reverse-proxy # Nom de l'image créée
ports: # Port forwarding
- "8080:80"
web:
build: ./docker-images/apache-php-image
container_name: api-php-server
image: api/php-server
api:
build: ./docker-images/express-image
container_name: api-express-server
image: api/express-server
Dans notre cas, les services web
et api
seront accessible directement en tant qu'URL (par example http://web/
).
Ce chagement est reflété dans la configuration du reverse proxy:
<VirtualHost *:80>
ServerName demo.api.ch
<!-- Préserve le header Host -->
ProxyPreserveHost on
<!-- Remplacement de l'adresse IP statique par le hostname -->
ProxyPass "/api/random" "http://api:3000/"
ProxyPassReverse "/api/random" "http://api:3000/"
<!-- Remplacement de l'adresse IP statique par le hostname -->
ProxyPass "/" "http://web:80/"
ProxyPassReverse "/" "http://web:80/"
</VirtualHost>
Il suffit de se déplacer dans le dossier docker-images
et de lancer la commande suivante:
docker-compose up
Cette partie du labo se trouve sur la branche suivante: additional-steps
Les parties suivante ont été implémentées grâce à traefik:
- Load balancing: multiple server nodes
- Load balancing: round-robin vs sticky sessions
- Dynamic cluster management
Traefik
gère par défaut le load balancing en mode round-robin et supporte l'ajout et le retrait dynamique de serveurs.
Ces fonctionalités ont été testées manuellement:
- Load balancing
- Multiple server nodes
- Lancement de plusieurs instances avec le paramètre
--scale
dedocker-compose up
- Lancement de plusieurs instances avec le paramètre
- Round-robin
- L'API change de serveur à chaque requête effectuée (visible dans les logs)
- Sticky sessions
- Rafraichir la page du navigateur (visible dans les logs)
- Multiple server nodes
- Dynamic cluster management
- Stopper manuellement les différentes instances des services, tout en effectuant de nouvelles requêtes (visible dans les logs)
La partie Management UI
a été implémentée grâce à portainer.
L'entièreté des configurations sont faite via le fichier docker-compose.yml
.
version: "3.9"
services:
traefik:
image: "traefik:v2.5"
container_name: "traefik"
command:
- "--providers.docker=true" # Active Docker comme provider de services
- "--providers.docker.exposedbydefault=false" # Désactive l'exposition des conainers par défaut
- "--entrypoints.web.address=:80" # Défini le port 80 comme point d'entrée
ports:
- "80:80" # Point d'entrée
- "8080:8080" # Dashboard Traefik
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro" # Accès au socket docker de la machine hôte
web:
build: ./docker-images/apache-php-image
image: api/php-server
labels:
- traefik.enable=true # Expose le service à Traefik
- traefik.http.routers.web.rule=Host(`demo.api.ch`) # Gestion des requête sur l'hôte demo.api.ch
- traefik.http.services.web.loadbalancer.server.port=80 # Port utilisé par le load balancer
- traefik.http.services.web.loadbalancer.sticky.cookie=true # Active le cookie pour sticky session
api:
build: ./docker-images/express-image
image: api/express-server
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=(Host(`demo.api.ch`) && Path(`/api/random`)) # Gestion des requête sur l'hôte demo.api.ch au chemin /api/random
- traefik.http.services.api.loadbalancer.server.port=3000
- traefik.http.routers.api.middlewares=stripprefix # Middlewares
- traefik.http.middlewares.stripprefix.stripprefix.prefixes=/api/random # Middleware pour retirer un partie de l'URL de la requête
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # Accès au socket docker de la machine hôte
- ../portainer-data:/data # Données persistantes de Portainer stockées sur machine hôte
ports:
- 9000:9000 # UI Portainer
Il suffit de se déplacer dans le dossier docker-images
et de lancer la commande suivante:
docker-compose up --scale web=<instances> --scale api=<instances>
Où <instances>
correspond au nombre d'instances désirées pour chaque service.