Guillaume Azerad

Ingénieur DevOps senior avec expertise Docker/Ansible/Gitlab et capacité de développeur full stack (Go, PHP, Javascript, Python)

Guillaume Azerad
Guillaume Azerad

Ingénieur DevOps senior avec expertise Docker/Ansible/Gitlab et capacité de développeur full stack (Go, PHP, Javascript, Python)

Mettre à l'échelle automatiquement une application avec un cloud hybride Azure - partie 1 : l'infrastructure


Publié le: 2024-08-24
Temps de lecture: 28 min
Modifié le: 2024-09-21
Existe aussi en: Anglais

Cloud hybride

Introduction

Lorsqu’on déploie une application (par exemple web et publique) sur une infrastructure privée, la question de la mise à échelle se pose immédiatement : que va-t-on faire en cas de pic de charge ? Dimensionner de façon statique son infrastructure pour supporter la charge pose un problème de coût évident, avec des ressources inutilisées/sous-utilisées l’essentiel du temps.

Le cloud public offre la possibilité de mettre à disposition des ressources à la demande, permettant ainsi une mise à l’échelle (scalabilité) élastique de son infrastructure pour s’adapter aux besoins de son application.

On peut toutefois rechigner à déplacer intégralement le déploiement de son application sur un cloud public : par exemple, si les pics de charge sont très occasionnels, l’infrastructure privée fait très correctement le travail la plupart du temps et pour un coût éventuellement moindre. C’est alors qu’intervient la notion de cloud hybride : interfacer une infrastructure on-premise avec une infrastructure cloud publique seulement activée lors des pics d’activité de l’application.

Cloud hybride - pic de charge

Nous allons voir dans cette suite d’article comment détecter les pics d’activité grâce à la supervision, puis déclencher la création de ressources sur le cloud Azure associé à l’infrastructure on premise dans un même VPN, avec une charge répartie grâce à un load balancer. Pour le premier de la série, c’est la création de l’infrastructure cloud hybride que nous allons détailler et la répartition de charge.

Cas d’utilisation

Nous considérons ici le cas d’une application web déployée sur une VM du réseau local. Nous allons choisir pour notre exemple la très simple application crccheck/hello-world disponible sur Docker Hub et qui n’affiche qu’une page à l’URL racine, configurable de la sorte avec Docker Compose.

service:
  application:
    image: crccheck/hello-world
    container_name: application
    ports:
      - "80:8000"

Voici ci-dessous l’infrastructure locale initiale :

Infrastructure initiale

Les utilisateurs sont dans le réseau privé puisque, pour des raisons de commodité, nous n’avons pas exposé l’application sur internet, mais cela pourrait tout à fait être le cas.

L’objectif ici est de créer de une à deux VM au maximum sur le cloud Azure lorsqu’on atteint un pic de charge sur la VM locale, et de répartir les connexions entrantes entre elles. Réciproquement, lorsque la charge redevient “normale”, les ressources créées sur Azure doivent être supprimées et les connexions redirigées vers la VM locale uniquement.

De la sorte, nous n’utilisons de ressources sur le cloud que lorsque nous en avons besoin, ce qui constitue un des avantages de l’implémentation d’un cloud hybride.

Concernant l’évaluation de la charge sur la VM locale, nous allons considérer l’utilisation de CPU :

  • CPU > 70% sur les 5 dernières minutes => création de VM Azure (jusqu’à deux maximum)
  • CPU < 30% sur la dernière heure avec présence d’au moins un pic > 70% (par pas de 5 minutes) => suppression de VM Azure

Nous allons donc voir comment implémenter une solution permettant de :

  • connecter les deux réseaux (privé et Azure)
  • superviser les métriques système de la VM locale et détecter les pics de CPU
  • mettre à l’échelle en créant/supprimant automatiquement des ressources sur le cloud Azure
  • répartir la charge grâce à l’utilisation d’un load balancer

Dans cet article, nous allons décrire l’architecture réseau permettant de satisfaire ces objectifs, l’infrastructre Azure hybride associée au réseau initial et la répartition de charge entre les deux.

Architecture réseau

Une première solution consiste à créer un réseau virtuel Azure muni d’une adresse IP publique qui lui servira de passerelle internet afin d’être interconnecté au réseau privé.

Le trafic est réparti à l’aide de deux load balancers : un dans le réseau privé qui route vers la VM locale ou la passerelle internet accédant à Azure, l’autre dans le réseau virtuel Azure pour répartir la charge entre les VM créées.

Cloud hybride par internet

Cette proposition est tout à fait fonctionnelle mais utiliser un lien internet peut être problématique, notamment à cause du non-chiffrement des données transférées. C’est pour cela qu’il est préférable de mettre en place un lien VPN entre les deux sites : les données sont chiffrées (par le biais d’une clef partagée), les deux réseaux (privé et cloud) sont désormais unis donc il est possible d’accéder aux VM Azure comme si elles étaient sur le réseau local. Il faut juste faire attention à ce que les plages d’adresses IP du réseau local et du réseau Azure ne se chevauchent pas.

Azure propose le service ExpressRoute pour gérer un lien VPN optimisé entre un réseau local et un réseau privé virtuel Azure. Celui-ci a bien sûr un coût : Metered Data (données mesurées avec frais de transfert) pour une bande passante de 200 Mbps en Europe coûte environ 500$ à 700$ par mois. Unlimited Data (données illimitées) pour une bande passante de 500 Mbps en Europe coûte environ 2000$ à 3000$ par mois.

Idéalement, il serait pertinent de mettre en place une connexion VPN site à site (S2S) si on souhaitait déployer un cloud hybride, de façon à se donner la possibilité d’administrer ses VM locales de la même manière que les VM Azure grâce à Azure Arc.

Nous obtiendrions alors l’architecture suivante :

Cloud hybride par internet

On peut remarquer que, puisque les deux réseaux ont directement accès l’un à l’autre, il n’y a plus besoin que d’un seul load balancer.

Celui-ci pourrait également être déplacé dans le réseau Azure, ce qui offrirait plus de souplesse puisqu’il serait possible de modifier dynamiquement sa configuration lors de la création/suppression des VM.

Le désavantage dans notre cas serait d’effectuer un aller-retour systématique entre les deux réseaux pour se connecter à l’application lorsqu’elle n’est déployée que dans le réseau local (cas le plus courant, hors pic de charge).

Par ailleurs, les contraintes de notre environnement de test (mon réseau domestique) rendent impossible la création d’une passerelle VPN exposant le réseau privé avec une adresse IP publique.

C’est pour cela que dans la suite de l’article, nous allons considérer une connexion VPN point à site (P2S).

Cloud hybride par internet Cloud hybride par VPN

Avec cette installation, il n’est pas possible de déplacer le load balancer dans le réseau virtuel Azure puisqu’il n’a pas connaissance de l’adresse IP de la VM locale pour rediriger le trafic vers elle.

Pour le reste, l’architecture est semblable au cas S2S puisque le load balancer local aura un accès direct aux VM créées sur Azure.

Nous remarquons dans les deux cas la présence d’une passerelle NAT dans le réseau Azure : celle-ci permet aux VM Azure d’accéder à internet tout en se trouvant dans un sous-réseau strictement privé.

Création de l’infrastructure cloud Azure

Création du réseau virtuel cloud

Pour rappel, voici l’infrastructure réseau que nous avons précédemment définie pour notre cloud hybride :

Cloud hybride par VPN

La partie cloud de l’infrastructure consiste donc en un réseau virtuel Azure qui sera exposé avec une adresse IP publique pour pouvoir le relier à notre réseau local par l’intermédiare d’un lien VPN.

Si cela n’est pas déjà fait, il faut créer un compte Azure éventuellement relié à votre compte Windows 365.

Nous supposons aussi qu’Azure CLI est installé afin de pouvoir exécuter des commandes d’administration du réseau virtuel Azure.

Nous pouvons maintenant nous logger en ligne de commande à notre compte Azure. Il existe plusieurs méthodes d’authentification qui nous seront proposées à l’invite de la ligne de commande.

az login

Nous supposons que nous exécutons les commandes dans un environnement Linux (éventuellement WSL ou Git Bash), et nous définissons au fur et à mesure les variables que nous utiliserons.

Afin de s’assurer que nous sommes bien connectés à l’abonnement que l’on souhaite, nous pouvons lister les abonnements liés à notre compte et choisir celui qui convient.

az account list --all

# Indiquer son id de souscription à Azure
SUBSCR_ID="xxxx-xxx-xxxx-xxxxxx-xxxxxxx" # votre id de souscription Azure
az account set --subscription "$SUBSCR_ID"

Nous allons maintenant créer un groupe de ressources qui permettra d’associer tous les éléments liés à notre infrastructure cloud. Nous lui attribuons ici la région francecentral mais il est tout à fait possible d’en choisir une autre selon le besoin ainsi que les coûts et options proposées (qui peuvent donc différer d’une région à une autre).

RESOURCE_GROUP="TpCloudHybrideVPN"
LOCATION="francecentral"

# Créer un groupe de ressources pour le VPN
az group create --name $RESOURCE_GROUP --location $LOCATION

Nous pouvons alors créer un réseau virtuel qui hébergera l’ensemble de notre infrastructure cloud. Il faut lui associer une plage d’adresse IP (ici 10.1.0.0/16, soit toutes les adresses IP de 10.1.0.0 à 10.1.255.255) et on peut déjà procéder à la création du sous-réseau “Frontend” qui accueuillera les VM (10.1.0.0/24, soit une plage d’IP de 10.1.0.4 à 10.1.0.254)

VNET_NAME="VnetVPN"
IPRANGE="10.1.0.0/16"
SUBNET_NAME="Frontend"
SUBNET="10.1.0.0/24"

# Créer le réseau virtuel avec la plage d'IP souhaitée
az network vnet create --name $VNET_NAME --resource-group $RESOURCE_GROUP \
  --address-prefix $IPRANGE --location $LOCATION \
  --subnet-name $SUBNET_NAME --subnet-prefix $SUBNET

On peut noter qu’Azure réserve un total de 5 adresses IP par sous-réseau : par exemple pour la plage 10.1.0.0/24, l’adresse 10.1.0.0 est réservée pour le réseau, 10.1.0.1 pour la passerelle par défaut, 10.1.0.2 et 10.1.0.3 pour mapper les adresses IP Azure DNS à l’espace réseau virtuel, et enfin 10.1.0.255 pour le broadcast.

Nous réservons dès maintenant une adresse IP publique que nous attribuerons plus tard à la passerelle VPN pour associer le réseau local et le réseau virtuel Azure.

PUBLIC_IP="VnetVPNGwIp"

# Demander une IP publique pour la gateway Azure
az network public-ip create --name $PUBLIC_IP --resource-group $RESOURCE_GROUP \
  --allocation-method Static --sku Standard

Mise en place de la connexion VPN P2S (point-to-site)

Nous allons ici créer la passerelle VPN, lui associer l’adresse IP publique précédemment créée, et installer le client VPN Azure pour pouvoir établir la connexion “point-to-site” à partir de notre réseau local.

Tout d’abord, nous exécutons les commandes CLI suivantes pour la création de la passerelle VPN et de son sous-réseau dédié.

Il faut bien faire attention ici à ce que la plage d’adresses IP du pool VPN client ne se superpose pas à celle du réseau virtuel dédié à l’hébergement de l’infrastructure cloud !

GATEWAY_SUBNET="10.1.255.0/27"
GATEWAY_SUBNET_NAME="GatewaySubnet"
GATEWAY_NAME="VnetVPNGwP2S"
VPN_CLIENT_IP_POOL="172.16.201.0/24"

# Créer le sous-réseau de passerelle (Gateway subnet)
az network vnet subnet create --address-prefix $GATEWAY_SUBNET \
  --name $GATEWAY_SUBNET_NAME --resource-group $RESOURCE_GROUP --vnet-name $VNET_NAME

# Créer la passerelle VPN
az network vnet-gateway create --name $GATEWAY_NAME --public-ip-address $PUBLIC_IP \
  --resource-group $RESOURCE_GROUP --vnet $VNET_NAME \
  --gateway-type Vpn --vpn-type RouteBased --sku VpnGw2 --no-wait \
  --client-protocol IkeV2 OpenVPN \
  --vpn-gateway-generation Generation2 \
  --address-prefix $VPN_CLIENT_IP_POOL

Afin d’assurer le chiffrement des communications entre le réseau local et le réseau virtuel Azure, nous allons générer un certificat racine qui servira à signer le certificat client utilisé pour établir la connexion VPN P2S sécurisée.

Selon votre configuration, il est possible de générer les certificats racine et client avec Powershell ou Makecert sous Windows, OpenSSL ou strongSwan pour Linux.

Pour notre cas, notre réseau local est hébergé sur un PC Windows 11 utilisant Windows Hyper-V donc nous avons utilisé les consignes Powershell pour générer les deux certificats ci-dessous.

Certificats pour VPN

Nous pouvons donc ajouter le certificat racine P2SRootCert.cer à la passerelle VPN en utilisant de nouveau Azure CLI.

az network vnet-gateway root-cert create \
  --resource-group $RESOURCE_GROUP --gateway-name $GATEWAY_NAME \
  --name P2SRootCert --public-cert-data P2SRootCert.cer

Nous allons maintenant nous rendre sur le portail Azure, chercher le nom de notre passerelle de réseau virtuel VnetVPNGwP2S pour accéder à sa page, et télécharger la configuration pour client VPN.

Certificats pour VPN

Enfin, sur notre réseau local (ici un PC Windows), nous allons installer le client VPN Azure et le configurer à partir du fichier zip que nous venons de télécharger.

Nous pouvons donc voir la connexion VPN dans la liste et nous y connecter.

Certificats pour VPN

Création d’un bastion SSH

De façon à sécuriser l’accès extérieur aux VM, une bonne pratique consiste à créer un bastion : il s’agit d’un serveur de rebond qui sera le seul à être exposé à internet via ssh. De la sorte, la centralisation du point d’entrée à un réseau privé permet de mieux contrôler l’accès externe à l’infrastructure et d’isoler une éventuelle intrusion sur le réseau.

Azure propose son propre service de bastion qui offre de multiples avantages dont celui de gérer les connexions RDP; mais pour des raisons d’économie (nous pouvons nous contenter d’une VM de taille minimale en tant que bastion) et d’apprentissage de l’infrastructure Azure, nous allons créer un sous-réseau et une VM dédiés au bastion.

Concernant les coûts, une VM Linux standard (2 vCPU, 8 GB RAM) coûtera environ 35 $/mois là où les frais d’une instance Azure Bastion s’élèveront à près de 140 $/mois sans même prendre en compte la tarification des données transférées.

Bastion SSH

Nous pouvons voir dans le schema précédent que nous appliquons des règles sur le trafic entrant par l’intermédiaire d’un groupe de sécurité réseau pour n’autoriser le trafic que sur le port 22 (et éventuellement en provenance d’une plage d’adresses IP autorisées).

Voici maintenant les commandes Azure CLI permettant de créer le bastion SSH; nous pouvons remarquer que dans notre cas, nous n’exposons pas le bastion à internet puisque nous ne nous y connectons que par lien VPN. Les commandes liées à un accès public sont donc commentées.

BASTION_SUBNET_NAME="Bastion"
BASTION_SUBNET_RANGE="10.1.1.0/24"

# Pas besoin de créer une adresse publique comme on utilise le VPN
# az network public-ip create --resource-group "$RESOURCE_GROUP" \
#   --name BastionPublicIP --sku Standard --zone 1 2 3

# Créer le sous-réseau dédié
az network vnet subnet create \
  --name $BASTION_SUBNET_NAME \
  --resource-group $RESOURCE_GROUP \
  --vnet-name $VNET_NAME \
  --address-prefixes "$BASTION_SUBNET_RANGE"

# Créer un groupe de sécurité réseau
az network nsg create --resource-group "$RESOURCE_GROUP" --name BastionNSG

# Créer une règle de groupe de sécurité réseau n'autorisant que
# le trafic entrant sur le port 22 (ssh)
az network nsg rule create \
  --resource-group "$RESOURCE_GROUP" --nsg-name BastionNSG --name BastionNSGRuleSSH \
  --protocol 'tcp' --direction inbound \
  --source-address-prefix '*' --source-port-range '*' \
  --destination-address-prefix '*' --destination-port-range 22 \
  --access allow --priority 200

# On pourrait limiter la plage d'IP source (pas nécessaire ici car bastion sans IP publique)
# --source-address-prefix $VPN_CLIENT_IP_POOL

# Créer la VM Bastion SSH
az vm create \
  --resource-group "$RESOURCE_GROUP" \
  --name "BastionSSHVM" \
  --image "Debian:debian-12:12:latest" \
  --size "Standard_B1s" \
  --location "$LOCATION" \
  --vnet-name "$VNET_NAME" \
  --subnet "$BASTION_SUBNET_NAME" \
  --public-ip-address "" \
  --nsg "BastionNSG" \
  --admin-username "azureuser" \
  --ssh-key-value ~/.ssh/id_ed25519_azure_bastion.pub \
  --output json \
  --verbose
  # --public-ip-address "BastionPublicIP" \

Configuration du sous-réseau des VM Azure

Après avoir créé le bastion, nous allons donc maintenant pouvoir définir des règles d’accès au sous-réseau hébergeant les VM ainsi que leur accès internet sortant par l’intermédiaire d’une passerelle NAT.

######################################
# Configuration du sous-réseau des VM

FRONTEND_NSG_NAME="FrontendNSG"

# Créer un groupe de sécurité réseau
az network nsg create --resource-group "$RESOURCE_GROUP" --name $FRONTEND_NSG_NAME

# Créer une règle de groupe de sécurité réseau n'autorisant
# le trafic entrant sur le port 22 (ssh) qu'en provenance
# du sous-réseau du Bastion SSH
az network nsg rule create \
  --resource-group "$RESOURCE_GROUP" --nsg-name $FRONTEND_NSG_NAME --name AllowSSHFromBastion \
  --protocol Tcp  --direction inbound \
  --source-address-prefix $BASTION_SUBNET_RANGE --source-port-range '*' \
  --destination-address-prefix '*' --destination-port-range 22 \
  --access allow --priority 100 \
  --description "Allow SSH from Bastion subnet"

az network nsg rule create \
  --resource-group $RESOURCE_GROUP --nsg-name $FRONTEND_NSG_NAME --name DenySSHFromOthers \
  --priority 200 --source-address-prefixes '*' --destination-port-ranges 22 \
  --direction Inbound --access Deny --protocol Tcp \
  --description "Deny SSH from all other sources"

# Associer le NSG au sous-réseau "Frontend"
az network vnet subnet update \
  --vnet-name $VNET_NAME --name $SUBNET_NAME --resource-group $RESOURCE_GROUP --network-security-group $FRONTEND_NSG_NAME

######################
# Passerelle NAT

# Créer une adresse IP publique
az network public-ip create --resource-group "$RESOURCE_GROUP" \
  --name NATPublicIP --sku Standard --zone 1 2 3

# Création de la passerelle NAT
az network nat gateway create \
    --resource-group $RESOURCE_GROUP \
    --name NATGateway \
    --public-ip-addresses NATPublicIP \
    --idle-timeout 10 \
    --location $LOCATION

# Configurer le service NAT pour le sous-réseau $SUBNET_NAME
az network vnet subnet update \
    --name $SUBNET_NAME \
    --resource-group $RESOURCE_GROUP \
    --vnet-name $VNET_NAME \
    --nat-gateway NATGateway

Attention : le service Azure NAT Gateway est payant dès son initialisation

Affichage des ressources sur le portail Azure

Toutes les ressources créées sont visibles dans le groupe de ressource TPCloudHybrideVPN que l’on a créé au tout début :

Liste des ressources

Répartition de charge entre le réseau local et le cloud Azure

Description du besoin

Pour rappel, l’objectif du cloud hybride consiste à créer de une à deux VM sur l’infrastructure Azure en cas de pic de charge, et nous avions proposé le schéma réseau suivant avec un load balancer dans le réseau local.

Load balancer local

Nous aurions pu choisir de placer le load balancer dans le réseau cloud Azure, d’autant qu’Azure Load Balancer offre la possibilité d’ajouter/supprimer dynamiquement des serveurs cibles, ce qui correspondait parfaitement à notre besoin.

Cependant, avec l’architecture réseau VPN point-to-site que nous avons dû choisir, il n’est pas possible à un load balancer situé dans le réseau Azure de joindre une VM du réseau local. De plus, il n’aurait pas forcément été pertinent de contraindre chaque connexion à l’application de transiter par le reseau distant Azure si, pour l’essentiel du temps, elle n’était déployée que sur la VM locale.

L’enjeu consiste donc ici pour le load balancer local à prendre en compte dynamiquement les VM créées sur le cloud Azure en cas de pic de charge. Nous partons du principe que les VM disposeront toujours de la même adresse IP (10.1.0.100 et 10.1.0.101).

Load balancer Nginx avec health check passif

Une première solution consiste à faire en sorte que le load balancer procède à la désactivation temporaire de la route vers un des serveurs si les requêtes qui lui sont envoyées échouent.

Nous pouvons utiliser pour cela la fonctionnalité de health check passif proposée dans la version gratuite de Nginx.

Dans ce cas, il faut fournir les deux paramètres suivants dans la configuration du serveur cible du load balancer :

  • fail_timeout : définit le temps pendant lequel un certain nombre de tentatives infructueuses doivent se produire pour que le serveur soit marqué comme indisponible, ainsi que sa période d’indisponibilité
  • max_fails : définit le nombre de tentatives infructueuses qui doivent se produire pendant la période fail_timeout pour que le serveur soit marqué comme indisponible

Afin d’impacter le moins possible les utilisateurs avec des erreurs d’affichage de la page web lorsqu’une requête est envoyée à un serveur indisponible, nous allons limiter à une seule tentative le paramètre max_fails et définir une période d’indisponibilité suffisamment longue (5 minutes par exemple).

Il suffit pour cela de créer le fichier de configuration suivant (que nous nommerons load_balancer.conf), puis de le monter comme volume du conteneur Nginx défini dans Docker Compose. Celui-ci contient également la définition du SSL avec un certificat et la redirection http vers https.

load_balancer.conf

upstream myapp {
    # Serveur principal de l'application dans le réseau local
    server 192.168.10.5;
    # VM créées sur Azure en cas de pic de charge
    server 10.1.0.100 max_fails=1 fail_timeout=120s;
    server 10.1.0.101 max_fails=1 fail_timeout=120s;
}

server {
  listen 80;
  listen [::]:80;
  server_name myapp.perso.com;
  return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    client_max_body_size 100M;
    client_body_in_file_only on;

    server_name myapp.perso.com;

    ssl_certificate /etc/ssl/certs/myapp.crt;
    ssl_certificate_key /etc/ssl/private/myapp.key;

    access_log /var/log/nginx/myapp.access.log;
    error_log /var/log/nginx/myapp.error.log debug;

    location / {
        proxy_pass http://myapp;
    }
}

docker-compose.yml

services:
  load_balancer:
    image: nginx:1.27.0
    container_name: load_balancer
    ports:
      - "80:80"
      - "443:443"
    networks:
      - frontend
    volumes:
      - ./nginx/conf.d/load_balancer.conf:/etc/nginx/conf.d/load_balancer.conf:ro
      - ./certs/perso.com.crt:/etc/ssl/certs/myapp.crt:ro
      - ./certs/perso.com.key:/etc/ssl/private/myapp.key:ro
      - ./nginx/log:/var/log/nginx
      - /etc/localtime:/etc/localtime:ro

De la sorte, tant que les deux VM Azure ne sont pas actives, elles sont retirées comme cibles du load balancer pendant une durée de 5 minutes à la première tentative infructueuse de connexion.

Ce comportement n’est toutefois pas véritablement satisfaisant puisque certains utilisateurs auront une page d’erreur lorsqu’ils seront redirigés vers les VM Azure non existantes tant que le load balancer n’aura pas désactivé la route correspondante.

C’est pourquoi nous allos voir comment implémenter un health check actif avec Trafik.

Nginx propose dans sa version payante Nginx Plus un health check actif lui aussi.

Health check actif avec le load balancer Traefik

Le health check actif consiste cette fois-ci pour le load balancer à initier lui-même des tentatives de connexion vers ses serveurs cibles afin de déterminer s’il convient de les désactiver ou non.

De la sorte, l’utilisateur n’est plus impacté par la perte d’un des serveurs puisque sa connexion sera toujours redirigée vers un des serveurs actifs (tant qu’il y en a au moins un, bien sûr).

Traefik propose une telle solution dans sa version gratuite.

Notons cependant que contrairement à Nginx qui propose de multiples stratégies de répartition de charge, Traefik n’offre quant à lui que le round robin.

Le load balancer Traefik donne la possibilité de ne se configurer que dans Docker Compose lorsque le trafic est redirigé vers d’autres conteneurs de la même instance Docker, en utilisant notamment les labels pour identifier les conteneurs.

Dans notre cas, le load balancer a pour cible des serveurs distants donc nous aurons recours à une configuration dynamique ayant pour provider le fichier que voici.

dynamic_config.yaml

http:
  routers:
    my-router:
      rule: "Host(`myapp.perso.com`)"
      service: my-service
      entryPoints:
        - web

    my-router-https:
      rule: "Host(`myapp.perso.com`)"
      service: my-service
      entryPoints:
        - websecure
      tls: {}

  services:
    my-service:
      loadBalancer:
        servers:
          - url: "http://192.168.10.5"
          - url: "http://10.1.0.100"
          - url: "http://10.1.0.101"
        healthCheck:
          path: "/"
          interval: "10s"
          timeout: "3s"

tls:
  certificates:
    - certFile: "/etc/ssl/certs/perso.com.crt"
      keyFile: "/etc/ssl/private/perso.com.key"

Nous pouvons remarquer que :

  • la configuration offre deux routes pour http et https à partir du nom d’hôte myapp.perso.com qui dirigent le flux vers les points d’entrée respectifs web et websecure. Nous verrons ensuite comment effectuer la redirection http -> https.
  • la partie SSL et certificats est définie dans ce fichier
  • le load balancer est défini dans la partie services avec les 3 serveurs cibles et surtout le détail du health check où on indique l’intervalle de test, le timeout et le chemin testé dans l’URL

Maintenant, nous allons détailler le service Docker Compose correspondant.

docker-compose.yml

services:
  load_balancer: 
    image: traefik:v3.1
    container_name: load_balancer
    ports:
      - "8000:80"
      - "4443:443"
      - "9080:8080"
    networks:
      - frontend
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=:4443
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --providers.file.filename=/etc/traefik/dynamic_config.yaml
      - --api.insecure=true
    volumes:
      - ./traefik/dynamic_config.yaml:/etc/traefik/dynamic_config.yaml
      - ./certs/perso.com.crt:/etc/ssl/certs/perso.com.crt
      - ./certs/perso.com.key:/etc/ssl/private/perso.com.key

Quelques remarques :

  • ports : dans notre cas, nous exposons des ports différents de 80 et 443 pour http et https afin de ne pas interférer avec d’autres applications. Ceci nous oblige à expliciter le port https vers lequel est effectuée la redirection (4443 ici), sinon le paramètre de commande - --entrypoints.web.http.redirections.entrypoint.to=:4443 ne serait pas nécessaire.
  • les points d’entrée pour http et https sont définis
  • le provider est bien indiqué comme étant le fichier dynamic_config.yaml précédemment défini
  • nous exposons le dashboard Traefik sur le port externe 9080 sans https (donc avec le paramètre --api.insecure=true)
  • les certificats et le fichier de configuration dynamiques sont montés comme volumes

Une fois l’environnement Docker Compose démarré avec la commande docker compose up -d, nous pouvons voir la page de l’application à l’adresse https://myapp.perso.com:4443 (avec une redirection vers cette URL si on indique http://myapp.perso.com:8000).

Application web page

Et cette fois-ci, nous n’avons aucune erreur même si un seul des 3 serveurs fonctionne comme dans notre cas où les VM Azure ne sont pas activées. Le dashboard Traefik permet de constater que le load balancer a correctement retiré les VM Azure de sa configuration.

Dashboard Traefik

Sommaire