Equilibreur de charge en local

Il est très facile de créer des équilibreurs de charge lorsqu'on travaille sur des clouds publics. Mais cette facilité n'est souvent pas disponible lorsque vous êtes sur un cloud privé, à moins que vous n'ayez des équipements spécifiques comme un F5 par exemple.

Voici un exemple de solution d'équilibrage de charge hautement disponible construite avec HAProxy Community Edition et Keepalived.

schéma de la solution

Création du lab

On commence par provisionner le lab. J'utilise ici Multipass pour créer les machines virtuelles, mais vous pouvez utilisez n'importe quelle autre solution si vous préférez.

 1# Machines virtuelles pour l'équilibreur de charge
 2multipass launch --name lb1 --mem 512M --cpus 1
 3multipass launch --name lb2 --mem 512M --cpus 1
 4# Machines virtuelles pour l'application
 5multipass launch --name app1 --mem 512M --cpus 1
 6multipass launch --name app2 --mem 512M --cpus 1
 7
 8# Aperçu de notre lab
 9$ multipass list
10Name                    State             IPv4             Image
11app1                    Running           192.168.205.6    Ubuntu 20.04 LTS
12app2                    Running           192.168.205.7    Ubuntu 20.04 LTS
13lb1                     Running           192.168.205.4    Ubuntu 20.04 LTS
14lb2                     Running           192.168.205.5    Ubuntu 20.04 LTS

L'application est une application web de démo déployée sur une machine virtuelle, mais elle pourrait être tout autre chose : une application conteneurisée, le contrôleur d'API Kubernetes, etc. HAProxy est capable de gérer n'importe quel flux TCP.

Installation de l'application de démo

D'abord, nous devons installer une application web sur les deux nœuds d'application. La solution d'équilibrage de charge distribuera les requêtes à ces serveurs. Nous nous appuierons sur la page d'accueil de NGINX pour l'application de démonstration. Dans la pratique, cela pourrait être n'importe quelle application ou API.

Installer Nginx sur les deux serveurs applicatifs pour héberger notre application de démo. Cela facilitera nos tests d'équilibrage de charge/

 1# Ouvre un shell sur la première instance
 2multipass shell app1
 3
 4# Installe Nginx
 5sudo apt update
 6sudo apt install nginx -y
 7
 8# Ouvre un shell sur la seconde instance
 9multipass shell app2
10
11# Installe Nginx
12sudo apt update
13sudo apt install nginx -y

Customiser la page d'accueil par défaut sur chacun des serveurs pour ajouter le nom de la machine virtuelle dans le message d'accueil.

1<!-- Adapter le message pour afficher le nom su serveur -->
2<title>Welcome to app1!</title>

Installation de HAProxy

Maintenant nous pouvons installer le premier composant de notre solution d'équilibrage de charge: HAProxy.

Installer HAProxy sur chaque machine virtuelle lb avecv la commande suivante:

1sudo apt install haproxy -y

Modifier le fichier de configuration /etc/haproxy/haproxy.cfg sur chacun des serveurs avec la configuration suivante. Laisser la section globale dans l'état.

 1defaults
 2    log     global
 3    mode    http
 4    option  httplog
 5    option  dontlognull
 6    timeout connect 5000
 7    timeout client  50000
 8    timeout server  50000
 9    errorfile 400 /etc/haproxy/errors/400.http
10    errorfile 403 /etc/haproxy/errors/403.http
11    errorfile 408 /etc/haproxy/errors/408.http
12    errorfile 500 /etc/haproxy/errors/500.http
13    errorfile 502 /etc/haproxy/errors/502.http
14    errorfile 503 /etc/haproxy/errors/503.http
15    errorfile 504 /etc/haproxy/errors/504.http
16
17frontend app
18    bind :::80
19    mode http
20    default_backend app
21
22backend app
23    balance roundrobin
24    mode http
25    option tcp-check
26    server app1 192.168.205.6:80 check
27    server app2 192.168.205.7:80 check
Note

Notez l'usage de l'option tcp-check qui permet de bénéficier de la fonctionnalité de health check.

Redémarrer HAProxy sur chaque serveur lb :

1sudo service haproxy restart

A ce stade, si vous envoyez plusieurs requêtes sur l'un des serveurs lb, vous devriez voir que HAProxy les répartit sur les serveurs app.

Installation de Keepalived

Nous allons maintenant utiliser Keepalived pour créer une IP virtuelle qui représentera le point d'entrée unique de notre équilibreur de charge.

Installer Keepalived sur chaque serveur lb:

1sudo apt install keepalived -y

Avant de le configurer, nous avons besoin de collecter des informations sur les interfaces réseau. On installe le package net-tools pour avoir la commande ifconfig, elle nous permettra de trouver le nom de l'interface qui correspond à l'IP:

 1$ sudo apt install net-tools -y
 2$ ifconfig
 3enp0s2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 4        inet 192.168.205.4  netmask 255.255.255.0  broadcast 192.168.205.255
 5        inet6 fe80::cfe:19ff:fea2:c7f6  prefixlen 64  scopeid 0x20<link>
 6        inet6 fdb2:f63f:a0b:91f4:cfe:19ff:fea2:c7f6  prefixlen 64  scopeid 0x0<global>
 7        ether 0e:fe:19:a2:c7:f6  txqueuelen 1000  (Ethernet)
 8        RX packets 67904  bytes 92365027 (92.3 MB)
 9        RX errors 0  dropped 0  overruns 0  frame 0
10        TX packets 9319  bytes 1457132 (1.4 MB)
11        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
12
13lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
14        inet 127.0.0.1  netmask 255.0.0.0
15        inet6 ::1  prefixlen 128  scopeid 0x10<host>
16        loop  txqueuelen 1000  (Local Loopback)
17        RX packets 285  bytes 32750 (32.7 KB)
18        RX errors 0  dropped 0  overruns 0  frame 0
19        TX packets 285  bytes 32750 (32.7 KB)
20        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Dans notre cas il s'agit de enp02. Nous allons la déclarer comme interface dans Keepalived (par défaut c'est eth0). Modifier la configuration en conséquence dans le fichier /etc/keepalived/keepalived.conf:

 1global_defs {
 2   notification_email {
 3     myemail@email.server.com
 4   }
 5   notification_email_from myemail@email.server.com
 6   smtp_server mail.server.com
 7   smtp_connect_timeout 30
 8}
 9vrrp_script chk_haproxy {
10  script "killall -0 haproxy"
11  interval 2
12  weight 2
13}
14vrrp_instance VI_1 {
15  interface enp0s2
16  state MASTER
17  virtual_router_id 51
18  priority 101
19  virtual_ipaddress {
20    192.168.205.100
21  }
22  track_script {
23    chk_haproxy
24  }
25}

Appliquer la même configuration sur le second noeud lb mais en changeant:

  • state: BACKUP à la place de MASTER
  • priority: 100 au lieu de 101

Démarrer Keepalived sur les deux noeuds lb:

1sudo service keepalived start

Maintenant, vous devriez être capable de pinger l'IP virtuelle représentant le point d'entrée de l'équilibreurs de charge. Toutes les requêtes envoyées à cette IP virtuelle sont par défaut envoyées au nœud maître de l'équilibreurs de charge, qui distribue ensuite le trafic sur les deux nœuds applicatifs.

Vérifions en faisant un ping sur l'IP virtuelle:

1# Ping sur l'IP virtuelle de Keepalived
2$ ping 192.168.205.100
3PING 192.168.205.100 (192.168.205.100): 56 data bytes
464 bytes from 192.168.205.100: icmp_seq=0 ttl=64 time=0.506 ms
564 bytes from 192.168.205.100: icmp_seq=1 ttl=64 time=0.610 ms
6^C
7--- 192.168.205.100 ping statistics ---
82 packets transmitted, 2 packets received, 0.0% packet loss
9round-trip min/avg/max/stddev = 0.506/0.558/0.610/0.052 ms

Tests

Requêtes HTTP

Essayons maintenant d'envoyer des requêtes HTTP dessus pour voir ce qu'il se passe:

 1# Première requête -> app1
 2$ curl 192.168.205.100:80
 3<!DOCTYPE html>
 4<html>
 5<head>
 6<title>Welcome to nginx!</title>
 7<style>
 8    body {
 9        width: 35em;
10        margin: 0 auto;
11        font-family: Tahoma, Verdana, Arial, sans-serif;
12    }
13</style>
14</head>
15<body>
16<h1>Welcome to app1 !</h1>
17<p>If you see this page, the nginx web server is successfully installed and
18working. Further configuration is required.</p>
19
20<p>For online documentation and support please refer to
21<a href="http://nginx.org/">nginx.org</a>.<br/>
22Commercial support is available at
23<a href="http://nginx.com/">nginx.com</a>.</p>
24
25<p><em>Thank you for using nginx.</em></p>
26</body>
27</html>
28
29# Seconde requête -> app2
30$ curl 192.168.205.100:80
31<!DOCTYPE html>
32<html>
33<head>
34<title>Welcome to nginx!</title>
35<style>
36    body {
37        width: 35em;
38        margin: 0 auto;
39        font-family: Tahoma, Verdana, Arial, sans-serif;
40    }
41</style>
42</head>
43<body>
44<h1>Welcome to app2 !</h1>
45<p>If you see this page, the nginx web server is successfully installed and
46working. Further configuration is required.</p>
47
48<p>For online documentation and support please refer to
49<a href="http://nginx.org/">nginx.org</a>.<br/>
50Commercial support is available at
51<a href="http://nginx.com/">nginx.com</a>.</p>
52
53<p><em>Thank you for using nginx.</em></p>
54</body>
55</html>

Nous avons pu constater que l'équilibreur de charge répartissait bien les requêtes sur nos serveurs applicatifs.

Panne d'un noeud lb

On peut tester la résilience de notre équilibreur de charge, voyons se qui se passe si le noeud MASTER rencontre un problème :

 1# Stop de lb1 (MASTER)
 2$ multipass stop lb1
 3
 4# L'IP virtuelle a été associée à lb2 (BACKUP)
 5$ multipass list
 6Name                    State             IPv4             Image
 7app1                    Running           192.168.205.6    Ubuntu 20.04 LTS
 8app2                    Running           192.168.205.7    Ubuntu 20.04 LTS
 9lb1                     Stopped           --               Ubuntu 20.04 LTS
10lb2                     Running           192.168.205.5    Ubuntu 20.04 LTS
11                                          192.168.205.100
12
13# Redémarrage de lb1 (MASTER)
14$ multipass start lb1
15
16# L'IP virtuelle est de nouveau associée au noeud lb1 (MASTER)
17$ multipass list     
18Name                    State             IPv4             Image
19app1                    Running           192.168.205.6    Ubuntu 20.04 LTS
20app2                    Running           192.168.205.7    Ubuntu 20.04 LTS
21lb1                     Running           192.168.205.4    Ubuntu 20.04 LTS
22                                          192.168.205.100
23lb2                     Running           192.168.205.5    Ubuntu 20.04 LTS

L'IP virtuelle est associée au noeud MASTER par défaut, elle bascule sur le noeud BACKUP uniquement en cas de défaillance de MASTER.

Panne sur un noeud applicatif

Nous allons simuler une panne sur l'un des deux noeuds applicatifs et vérifier que HAProxy arrête de lui envoyer des requêtes pour éviter que l'utilisateur n'ait des erreurs.

On arrête le noeud app1:

1multipass stop app1

Envoyez plusieurs requêtes HTTP, vous verrez que toutes les requêtes sont bien envoyées sur le seul noeud restant: app2. C'est grâce à la fonctionnalité de health check que HAProxy a détecté la défaillance et modifié dynamiquement la liste des serveurs cibles pour retirer app1.

Rédémarrons le noeud app1:

1multipass start app1

En envoyant des requêtes, on constate que HAProxy utilise de nouveau les deux serveurs applicatifs.

Test de charge

Nous allons générer de la charge pour voir comment notre solution d'équilibrage de charge se comporte. Pour cela, nous utiliserons la solution ab avec la configuration suivante:

  • Concurrency: 50
  • Requests: 300 000

Voici les résultats obtenus:

Résultat du test de charge

Consommations CPU des machines virtuelles au cours du test

Qu'est ce que ça dit ?
  • Notre équilibreur de charge fonctionne !
  • It fonctionne même bien malgré l'utilisation de machines virtuelles avec peu de ressources (1 coeur, 512 Mb de RAM)
  • La courbe des deux serveurs applicatifs est pratiquement identique, la solution a bien réparti la charge
  • Le noeud lb MASTER a encaissé la charge sans sourciller, le noeud lb BACKUP est resté tranquille

Conclusion

Nous avons construit une solution d'équilibrage de charge opérationnelle et hautement disponible pour notre application.

  • On peut augmenter la capacité de traitement en ajoutant de nouveaux nœuds applicatifs, il suffira alors d'adapter la configuration de HAProxy.
  • On peut également augmenter la capacité de traitement de l'équilibreurs de charge lui-même en ajoutant de nouveaux nœuds lb avec la même configuration Keepalived.

Il pourra être nécessaire de créer plusieurs instances de la solution si :

  • Vous souhaitez avoir différentes expositions
  • Vous voulez utiliser les mêmes ports plusieurs fois
  • Il y a beaucoup d'autres fonctionnalités supplémentaires dans HAProxy et Keepalived qui peuvent vous intéresser, consultez leur documentation pour plus de détails.

N'hésitez pas à m'envoyer vos retours ou suggestions éventuelles pour améliorer cet article. Merci.