Répartiteur de charge à multiples niveaux avec Linux

Vincent Bernat

Une so­lu­tion cou­rante pour four­nir un ser­vice hau­te­ment dis­po­nible et évo­lu­tif consiste à in­sé­rer une couche d’équi­li­brage de charge pour ré­par­tir les re­quêtes des uti­li­sa­teurs vers les ser­veurs fron­taux1. Nous avons ha­bi­tuel­le­ment plu­sieurs at­tentes à l’égard d’une telle couche :

Évo­lu­ti­vité
Elle per­met à un ser­vice de mon­ter en charge en pous­sant le tra­fic vers des ser­veurs nou­vel­le­ment pro­vi­sion­nés. Elle est éga­le­ment ca­pable de s’étendre si elle de­vient le gou­lot d’étran­glement.
Dis­po­ni­bi­lité
Elle four­nit la haute dis­po­ni­bi­lité au ser­vice. Si un ser­veur de­vient in­dis­po­nible, le tra­fic est ra­pi­de­ment re­di­rigé vers un autre ser­veur. Elle doit éga­le­ment être elle-même hau­te­ment dis­ponible.
Flexi­bi­lité
Elle gère aussi bien les connexions de courtes et de longues du­rées. Elle est suf­fi­sam­ment flexible pour of­frir toutes les fonc­tion­na­li­tés gé­né­ra­le­ment at­ten­dues d’un ré­par­ti­teur de charge comme TLS ou le rou­tage HTTP.
Opé­ra­bi­lité
Avec un peu de co­opé­ra­tion, tout chan­ge­ment prévu est trans­pa­rent : mise à ni­veau des fron­taux, ajout ou sup­pres­sion de fron­taux ou chan­ge­ment de to­po­lo­gie de la couche de ré­par­ti­tion elle-même.

Le pro­blème et ses so­lu­tions sont bien connus. Parmi les ar­ticles ré­cem­ment pa­rus sur le su­jet, « In­tro­duc­tion to mo­dern net­work load-ba­lan­cing and proxying » donne un aperçu de l’état de l’art. Google a pu­blié « Ma­glev: A Fast and Re­liable Soft­ware Net­work Load Ba­lan­cer » dé­cri­vant en dé­tail leur so­lu­tion in­terne2. Ce­pen­dant, le lo­gi­ciel as­so­cié n’est pas dis­po­nible. Fon­da­men­ta­le­ment, la construc­tion d’une so­lu­tion d’équi­li­brage de charge consiste à as­sem­bler trois com­posants :

  • rou­tage ECMP ;
  • ré­par­ti­tion L4 (sans état) ;
  • ré­par­ti­tion L7 (avec état).

Dans cet ar­ticle, je dé­cris une so­lu­tion à mul­tiples ni­veaux uti­li­sant Li­nux et des com­po­sants open-source. Cela offre une base pour construire une couche d’équi­li­brage de charge prête à la pro­duction.

Mise à jour (05.2018)

Fa­ce­book vient de pu­blier Ka­tran, un ré­par­ti­teur de charge L4 uti­li­sant XDP et eBPF ainsi que du ha­chage co­hé­rent. Il pour­rait s’in­sé­rer dans la confi­gu­ra­tion dé­crite ci-dessous.

Mise à jour (08.2018)

GitHub vient de pu­blier GLB Di­rec­tor, un ré­par­ti­teur de charge L4 avec un al­go­rithme de ha­chage sé­lec­tion­nant un couple de ré­par­ti­teurs L7. À l’aide d’un mo­dule Net­fil­ter, le pre­mier membre re­di­rige le flux vers le se­cond lorsqu’il ne trouve pas d’en­trée cor­res­pon­dante dans sa table de connexions.

Mise à jour (12.2021)

Ci­lium 1.10+ uti­lise une ap­proche si­mi­laire à celle dé­crite dans cet ar­ticle pour gé­rer les ser­vices dans une grappe Ku­ber­netes. Je­tez un œil sur la do­cu­men­ta­tion sur com­ment uti­li­ser Ku­ber­netes sans kube-proxy ainsi que celle sur com­ment confi­gurer BGP.

Dernier niveau : répartition L7#

Com­men­çons par le der­nier ni­veau. Son rôle est de four­nir la haute dis­po­ni­bi­lité, en trans­fé­rant les re­quêtes vers les fron­taux sains, ainsi que l’évo­lu­ti­vité, en ré­par­tis­sant équi­ta­ble­ment les re­quêtes. Tra­vaillant dans les couches su­pé­rieures du mo­dèle OSI, il peut éga­le­ment of­frir des ser­vices sup­plé­men­taires, comme la ter­mi­nai­son TLS, le rou­tage HTTP, la ré­écri­ture des en­têtes, la li­mi­ta­tion du dé­bit des uti­li­sa­teurs non au­then­ti­fiés, etc. Il peut ti­rer parti d’al­go­rithmes com­plexes d’équi­li­brage de charge. En tant que pre­mier point de contact avec les ser­veurs fron­taux, il doit fa­ci­li­ter les main­te­nances et mi­ni­mi­ser l’im­pact lors des chan­ge­ments quo­tidiens.

Répartiteurs de charge L7
Le dernier niveau de la solution de répartition de charge est un ensemble d’équilibreurs de charge L7 recevant les connexions des utilisateurs et les transférant vers les frontaux.

Il ter­mine éga­le­ment les connexions TCP des clients. Cela dé­couple l’étage de ré­par­ti­tion des ser­veurs fron­taux avec les avan­tages suivants :

  • les connexions vers les fron­taux peuvent être main­te­nues ou­vertes pour ré­duire l’uti­li­sa­tion des res­sources et la latence ;
  • les re­quêtes peuvent être ré­es­sayées de ma­nière trans­pa­rente en cas de dé­faillance ;
  • les clients peuvent uti­li­ser un pro­to­cole IP dif­fé­rent des serveurs ;
  • les fron­taux n’ont pas à se sou­cier de la dé­cou­verte de la MTU du che­min, des al­go­rithmes de conges­tion TCP, de la ges­tion de l’état TIME-WAIT ou d’autres dé­tails de bas niveau.

De nom­breux lo­gi­ciels conviennent pour cette couche et il existe une lit­té­ra­ture abon­dante sur la fa­çon de les confi­gu­rer. Vous pou­vez re­gar­der HAProxy, En­voy ou Trae­fik. Voici un exemple de confi­gu­ra­tion pour HAProxy :

# Point d'entrée de la répartition de charge L7
frontend l7lb
  # Écoute à la fois en IPv4 et IPv6
  bind :80 v4v6
  # Redirige tout sur un ensemble de serveurs frontaux
  default_backend servers
  # Vérification de la bonne santé
  acl dead nbsrv(servers) lt 1
  acl disabled nbsrv(enabler) lt 1
  monitor-uri /healthcheck
  monitor fail if dead || disabled

# Serveurs frontaux en IPv6 avec tests HTTP et via un agent
backend servers
  balance roundrobin
  option httpchk
  server web1 [2001:db8:1:0:2::1]:80 send-proxy check agent-check agent-port 5555
  server web2 [2001:db8:1:0:2::2]:80 send-proxy check agent-check agent-port 5555
  server web3 [2001:db8:1:0:2::3]:80 send-proxy check agent-check agent-port 5555
  server web4 [2001:db8:1:0:2::4]:80 send-proxy check agent-check agent-port 5555

# Faux serveur gérant la disponibilitant du répartiteur de charge lui-même
backend enabler
  server enabler [::1]:0 agent-check agent-port 5555

Cette confi­gu­ra­tion est très som­maire mais per­met d’illus­trer deux no­tions clés pour l’opé­rabilité :

  1. Les fron­taux sont tes­tés à la fois au ni­veau HTTP (avec check et option httpchk) et via un agent auxi­liaire (avec agent-check). Ce der­nier per­met de pla­cer un ser­veur en main­te­nance pour ef­fec­tuer une mise en pro­duc­tion pro­gres­sive. Sur chaque fron­tal, un pro­ces­sus écoute sur le port 5555 et ré­pond avec le sta­tut du ser­vice (UP, DOWN, MAINT). Un simple socat fait l’affaire3 :

    socat -ly \
      TCP6-LISTEN:5555,ipv6only=0,reuseaddr,fork \
      OPEN:/etc/lb/agent-check,rdonly
    

    Dans /etc/​lb/​agent-check, UP in­dique que le ser­vice est en mode no­mi­nal. Si le test HTTP est aussi po­si­tif, HAProxy en­verra des re­quêtes vers ce nœud. Si vous de­vez le mettre en main­te­nance, écri­vez MAINT et at­ten­dez que les connexions en cours se ter­minent. Uti­li­sez READY pour an­nu­ler ce mode.

  2. Le ré­par­ti­teur de charge lui-même four­nit un point de diag­nos­tic (/healthcheck) pour les ni­veaux su­pé­rieurs. Il re­tourne une er­reur 503 si au­cun fron­tal n’est dis­po­nible ou si le ser­veur enabler est in­di­qué comme in­dis­po­nible via l’agent. Le même mé­ca­nisme que pour les ser­veurs fron­taux clas­siques peut alors être uti­lisé pour si­gna­ler l’in­dis­po­ni­bi­lité de cet équi­li­breur de charge.

De plus, la di­rec­tive send-proxy per­met d’uti­li­ser le pro­to­cole proxy afin de trans­mettre les adresses IP réelles des clients. Ce pro­to­cole fonc­tionne éga­le­ment pour les connexions non-HTTP et est sup­porté par de nom­breux ser­veurs, y com­pris nginx :

http {
  server {
    listen [::]:80 default ipv6only=off proxy_protocol;
    root /var/www;
    set_real_ip_from ::/0;
    real_ip_header proxy_protocol;
  }
}

En l’état, cette so­lu­tion n’est pas com­plète. Nous avons dé­placé le pro­blème de dis­po­ni­bi­lité et d’évo­lu­ti­vité ailleurs. Com­ment ré­par­tir les de­mandes entre les équi­li­breurs de charge ?

Premier niveau : routage ECMP#

Sur la plu­part des ré­seaux IP mo­dernes, il existe des che­mins re­don­dants entre les clients et les ser­veurs. Pour chaque pa­quet, les rou­teurs doivent choi­sir une branche. Lorsque le coût as­so­cié à chaque tra­jet est égal, les flux en­trants4 sont ré­par­tis entre les des­ti­na­tions dis­po­nibles. Cette ca­rac­té­ris­tique peut être uti­li­sée pour ré­par­tir les connexions entre les équi­li­breurs de charge dis­ponibles :

Routage ECMP
Le routage ECMP est utilisé comme premier étage. Les flux sont répartis entre les équilibreurs de charge L7 disponibles. Le routage est sans état et asymétrique. Les serveurs frontaux ne sont pas représentés.

Il y a peu de contrôle sur la ré­par­ti­tion des flux, mais le rou­tage ECMP ap­porte la pos­si­bi­lité de faire évo­luer ho­ri­zon­ta­le­ment les deux ni­veaux. Une mise en œuvre cou­rante d’une telle so­lu­tion est d’uti­li­ser BGP, un pro­to­cole de rou­tage pour échan­ger des routes entre les équi­pe­ments du ré­seau. Chaque ré­par­ti­teur de charge an­nonce aux rou­teurs aux­quels il est connecté les adresses IP qu’il dessert.

En sup­po­sant que vous avez déjà des rou­teurs avec BGP, ExaBGP est une so­lu­tion flexible pour per­mettre aux ré­par­ti­teurs de charge d’an­non­cer leur dis­po­ni­bi­lité. Voici un exemple de confi­guration :

# Test de disponibilité en IPv6
process service-v6 {
  run python -m exabgp healthcheck -s --interval 10 --increase 0 --cmd "test -f /etc/lb/v6-ready -a ! -f /etc/lb/disable";
  encoder text;
}

template {
  # Patron pour un routeur IPv6
  neighbor v6 {
    router-id 192.0.2.132;
    local-address 2001:db8::192.0.2.132;
    local-as 65000;
    peer-as 65000;
    hold-time 6;
    family {
      ipv6 unicast;
    }
    api services-v6 {
      processes [ service-v6 ];
    }
  }
}

# Premier routeur
neighbor 2001:db8::192.0.2.254 {
  inherit v6;
}

# Second routeur
neighbor 2001:db8::192.0.2.253 {
  inherit v6;
}

Si /etc/​lb/​v6-ready est pré­sent mais que /etc/​lb/​disable est ab­sent, toutes les IP confi­gu­rées sur l’in­ter­face lo sont an­non­cées aux deux rou­teurs. Si les autres ré­par­ti­teurs de charge ont une confi­gu­ra­tion si­mi­laire, les rou­teurs leur dis­tri­buent équi­ta­ble­ment les flux re­çus. Un pro­ces­sus ex­terne doit gé­rer l’exis­tence du fi­chier /etc/​lb/​v6-ready en vé­ri­fiant la bonne santé du ré­par­ti­teur de charge (à l’aide du point /healthcheck par exemple). Un opé­ra­teur peut re­ti­rer un ré­par­ti­teur de charge de la ro­ta­tion en créant le fi­chier /etc/​lb/​disable.

Pour plus de dé­tails concer­nant cette par­tie, je­tez un œil sur « Re­don­dance avec ExaBGP ». Si vous êtes hé­ber­gés dans les nuages, ce tiers est gé­né­ra­le­ment mis en place par votre four­nis­seur sous forme d’une IP « élas­tique » ou d’un ser­vice de ré­par­tition L4.

Mal­heu­reu­se­ment, cette so­lu­tion n’est pas ro­buste lorsqu’un chan­ge­ment, prévu ou non, se pro­duit. No­tam­ment, lors de l’ajout ou de la sup­pres­sion d’un équi­li­breur de charge, le nombre de routes dis­po­nibles pour une des­ti­na­tion change. L’al­go­rithme de ha­chage uti­lisé par les rou­teurs n’est pas co­hé­rent et les flux sont re­dis­tri­bués entre les ré­par­ti­teurs de charge dis­po­nibles, rom­pant les connexions existantes :

Stabilité du routage ECMP 1/2
Le routage ECMP est instable lorsqu’un changement se produit. Un équilibreur de charge supplémentaire est ajouté et chaque flux est acheminé vers un répartiteur différent qui n’a pas les entrées appropriées dans sa table de connexions.

De plus, chaque rou­teur peut choi­sir ses propres che­mins. Quand un rou­teur de­vient in­dis­po­nible, le se­cond peut rou­ter les mêmes flux dif­fé­remment :

Stabilité du routage ECMP 2/2
Un routeur devient indisponible et le routeur restant route différemment ses flux. L’un d’entre eux est acheminé vers un répartiteur de charge différent qui n’a pas l’entrée appropriée dans sa table des connexions.

Si vous pen­sez que ce n’est pas un ré­sul­tat ac­cep­table, no­tam­ment si vous de­vez gé­rer de longues connexions comme le té­lé­char­ge­ment de fi­chiers, le strea­ming vi­déo ou les connexions web­so­cket, vous avez be­soin d’un ni­veau sup­plé­men­taire. Conti­nuez la lecture !

Mise à jour (03.2021)

Cer­tains construc­teurs pro­posent un al­go­rithme de ha­chage ré­si­lient pour contour­ner cette li­mi­ta­tion. Je­tez par exemple un œil sur la do­cu­men­ta­tion chez Ju­ni­per et Cu­mu­lus. Merci à Jessy Vet­ter pour cette in­for­ma­tion ! Cette fonc­tion­na­lité a éga­le­ment été in­tro­duite dans Li­nux 5.12.

Second niveau : répartition L4#

Le deuxième ni­veau est la glue entre le monde sans état des rou­teurs IP et le pays avec état de l’équi­li­brage de charge L7. Il est mis en œuvre grâce à l’équi­li­brage de charge L4. La ter­mi­no­lo­gie peut être un peu confuse ici : ce ni­veau route les da­ta­grammes IP (pas de ter­mi­nai­son TCP) mais l’al­go­rithme de ré­par­ti­tion uti­lise à la fois l’IP de des­ti­na­tion et le port pour choi­sir un ser­veur dis­po­nible dans le ni­veau sui­vant. Le but de cet étage est de s’as­su­rer que tous les membres prennent la même dé­ci­sion d’or­don­nan­ce­ment pour un pa­quet entrant.

Il y a deux pos­si­bilités :

  • ré­par­ti­tion de charge L4 avec syn­chro­ni­sa­tion des états ;
  • ré­par­ti­tion de charge L4 avec ha­chage cohérent.

La pre­mière op­tion aug­mente la com­plexité et li­mite l’évo­lu­ti­vité. Nous ne l’ex­plo­rons pas5. La se­conde op­tion est moins ro­buste aux chan­ge­ments mais cela peut être amé­lioré via une ap­proche hy­bride avec un état local.

Nous uti­li­sons IPVS, un ré­par­ti­teur de charge L4 per­for­mant fonc­tion­nant dans le noyau Li­nux. Il est pi­loté par Kee­pa­li­ved qui dis­pose d’un en­semble de tests de dis­po­ni­bi­lité pour dé­tec­ter les pro­blèmes. IPVS est confi­guré pour uti­li­ser Ma­glev, un al­go­rithme de ha­chage co­hé­rent créé par Google. Dans sa fa­mille, c’est un bon al­go­rithme car il ré­par­tit les connexions de ma­nière équi­table, mi­ni­mise les im­pacts consé­cu­tifs à un chan­ge­ment et est par­ti­cu­liè­re­ment ra­pide pour construire sa table de cor­res­pon­dance. En­fin, pour amé­lio­rer les per­for­mances, le der­nier ni­veau (les ré­par­ti­teurs de charge L7) ré­pond aux clients di­rec­te­ment sans im­pli­quer le se­cond ni­veau (les ré­par­ti­teurs de charge L4). Ce mé­ca­nisme est connu sous le nom de di­rect ser­ver re­turn (DSR) ou di­rect rou­ting (DR).

Second niveau : répartition L4
Équilibrage de charge L4 avec IPVS et hachage cohérent liant le premier et le troisième niveau. Les serveurs frontaux ont été omis. Les lignes en pointillés représentent le chemin pris par les paquets retour.

Avec une telle confi­gu­ra­tion, on s’at­tend à ce que les pa­quets ap­par­te­nant à un flux puissent se dé­pla­cer li­bre­ment entre les com­po­sants des deux pre­miers ni­veaux tout en fi­nis­sant sur le même équi­li­breur de charge L7.

Configuration#

Une fois ExaBGP confi­guré comme dé­crit dans la sec­tion pré­cé­dente, nous pou­vons pas­ser à la confi­gu­ra­tion de Kee­palived :

virtual_server_group VS_GROUP_MH_IPv6 {
  2001:db8::198.51.100.1 80
}
virtual_server group VS_GROUP_MH_IPv6 {
  lvs_method TUN  # Mode tunnel pour DSR
  lvs_sched mh    # Algorithme : Maglev
  mh-port         # Prend en compte les ports TCP
  protocol TCP
  delay_loop 5
  alpha           # Les serveurs sont considérés inaccessibles au démarrage
  omega           # Exécute quorum_down à l'arrêt
  quorum_up   "/bin/touch /etc/lb/v6-ready"
  quorum_down "/bin/rm -f /etc/lb/v6-ready"

  # Premier répartiteur de charge L7
  real_server 2001:db8::192.0.2.132 80 {
    weight 1
    HTTP_GET {
      url {
        path /healthcheck
        status_code 200
      }
      connect_timeout 2
    }
  }

  # Tous les autres...
}

Les di­rec­tives quorum_up et quorum_​down dé­fi­nissent les com­mandes à exé­cu­ter quand le ser­vice de­vient res­pec­ti­ve­ment ac­ces­sible et in­ac­ces­sible. Le fi­chier /etc/​lb/​v6-ready est uti­lisé pour si­gna­ler à ExaBGP s’il doit ou non pu­blier l’adresse IP du ser­vice aux routeurs.

De plus, IPVS doit être confi­guré pour rou­ter les pa­quets ap­par­te­nant à un flux traité ini­tia­le­ment par un autre nœud. Il doit éga­le­ment conti­nuer de rou­ter les pa­quets quand une des­ti­na­tion de­vient in­dis­po­nible afin de s’as­su­rer qu’on puisse mettre hors ser­vice pro­pre­ment un ré­par­ti­teur de charge L7.

# Prend aussi en charge les paquets non SYN
sysctl -qw net.ipv4.vs.sloppy_tcp=1
# Ne PAS rerouter une connexion quand une destination
# devient invalide.
sysctl -qw net.ipv4.vs.expire_nodest_conn=0
sysctl -qw net.ipv4.vs.expire_quiescent_template=0

L’al­go­rithme Ma­glev sera dis­po­nible dans Li­nux 4.18, grâce au tra­vail de Inju Song. Pour les noyaux plus an­ciens, j’ai pré­paré un ré­tro­por­tage6. Le sub­sti­tuer par un autre al­go­rithme, tel que sh, rend l’en­semble moins robuste.

Le DSR est mis en place avec le mode tun­nel. Cette mé­thode est com­pa­tible avec les ré­seaux rou­tés. Les re­quêtes sont en­cap­su­lées vers le nœud choisi à l’aide du pro­to­cole IPIP. Cela ajoute un lé­ger sur­coût et en­traîne des pro­blèmes de MTU. Si pos­sible, uti­li­sez une MTU plus grande entre le se­cond et troi­sième ni­veau7. Dans le cas contraire, au­to­ri­sez ex­pli­ci­te­ment la frag­men­ta­tion des pa­quets IP :

sysctl -qw net.ipv4.vs.pmtu_disc=0

Il faut aussi confi­gu­rer les ré­par­ti­teurs de charge L7 pour ac­cep­ter ce tra­fic en­capsulé8 :

# Configure le tunnel IPIP pour accepter des paquets de n'importe quelle source
ip tunnel add tunlv6 mode ip6ip6 local 2001:db8::192.0.2.132
ip link set up dev tunlv6
ip addr add 2001:db8::198.51.100.1/128 dev tunlv6

Évaluation de la robustesse#

Ainsi confi­guré, le se­cond ni­veau amé­liore la ro­bus­tesse de l’en­semble pour deux raisons :

  1. L’uti­li­sa­tion d’un al­go­rithme de ha­chage co­hé­rent pour choi­sir la des­ti­na­tion ré­duit l’im­pact né­ga­tif d’un chan­ge­ment, prévu ou non, en mi­ni­mi­sant le nombre de flux dé­pla­cés vers une nou­velle des­ti­na­tion. « Consistent Ha­shing: Al­go­rith­mic Tra­deoffs » offre plus de dé­tails sur ce sujet.

  2. IPVS garde lo­ca­le­ment une table des connexions pour les flux connus. Quand un chan­ge­ment n’im­pacte que le der­nier ni­veau, les flux exis­tants conti­nuent d’être rou­tés cor­rec­te­ment en uti­li­sant cette table.

Si nous ajou­tons ou re­ti­rons un ré­par­ti­teur L4, les flux exis­tants ne sont pas im­pac­tés car chaque ré­par­ti­teur prend la même dé­ci­sion grâce au ha­chage cohérent :

Instabilité de la répartition L4 1/3
La perte d’un équilibreur L4 n’a pas d’impact sur les flux existants. Chaque flèche est un exemple de flux. Les points sont des connexions liées à l’équilibreur de charge associé. S’ils s’étaient déplacés vers un autre équilibreur, la connexion aurait été perdue.

Lors de l’ajout d’un ré­par­ti­teur L7, les flux exis­tants ne sont pas im­pac­tés non plus car seules les nou­velles connexions sont rou­tées vers le nou­veau ré­par­ti­teur. Pour les connexions exis­tantes, IPVS uti­lise sa table de connexion lo­cale et conti­nue de rou­ter les pa­quets vers la des­ti­na­tion ori­gi­nale. De ma­nière si­mi­laire, le re­trait d’un ré­par­ti­teur L7 n’im­pacte que les connexions liées à ce­lui-ci. Les autres connexions sont rou­tées cor­rectement :

Instabilité de la répartition L4 2/3
La perte d’un équilibreur L7 n’impacte que les flux qu’il hébergeait.

Seuls des chan­ge­ments si­mul­ta­nés sur les deux ni­veaux mènent à un im­pact no­table. Par exemple, lors de l’ajout d’un équi­li­breur de charge L4 et d’un équi­li­breur de charge L7, seules les connexions dé­pla­cées vers un ré­par­ti­teur L4 sans état et pro­gram­mées vers le nou­veau ré­par­ti­teur se­ront rom­pues. Grâce à l’al­go­rithme de ha­chage co­hé­rent, les autres connexions res­te­ront liées au ré­par­ti­teur L7 adé­quat. Lors d’un chan­ge­ment pla­ni­fié, cette per­tur­ba­tion peut être mi­ni­mi­sée en ajou­tant d’abord les nou­veaux équi­li­breurs L4, en at­ten­dant quelques mi­nutes puis en ajou­tant les nou­veaux équi­li­breurs L7.

Instabilité de la répartition L4 3/3
Un équilibreur de charge L4 et un équilibreur de charge L7 reviennent à la vie. L’algorithme de hachage cohérent garantit que seul un cinquième des connexions existantes serait déplacé vers le nouvel équilibreur L7. Certains d’entre eux continuent d’être acheminés par le répartiteur L4 d’origine qui connait la destination correcte, ce qui atténue l’impact.

De plus, IPVS route cor­rec­te­ment les mes­sages ICMP vers les mêmes ré­par­ti­teurs L7 que les flux as­so­ciés9. Cela per­met à la dé­cou­verte de la MTU du che­min de fonc­tion­ner cor­rec­te­ment sans uti­li­ser de tech­niques pal­liatives.

Niveau 0 : répartition de charge via le DNS#

Il est éga­le­ment pos­sible d’ajou­ter un équi­li­brage de charge DNS à l’en­semble. Ceci est utile si votre ins­tal­la­tion est ré­par­tie sur plu­sieurs centres de don­nées, plu­sieurs ré­gions ou si vous vou­lez di­vi­ser une large ferme de ré­par­ti­tion de charge en mor­ceaux plus pe­tits. Il n’est pas des­tiné à rem­pla­cer le pre­mier ni­veau car il ne par­tage pas les mêmes ca­rac­té­ris­tiques : la ré­par­ti­tion de charge est dés­équi­li­brée (elle n’est pas ba­sée sur les flux) et la gué­ri­son après une panne est lente.

Répartition de charge globale
Une solution complète de répartition de charge sur deux centres de données.

gdnsd est un ser­veur DNS fai­sant au­to­rité avec des tests de dis­po­ni­bi­lité in­té­grés. Il peut ser­vir des zones au for­mat RFC 1035 :

@ SOA ns1 ns1.example.org. 1 7200 1800 259200 900
@ NS ns1.example.com.
@ NS ns1.example.net.
@ MX 10 smtp

@     60 DYNA multifo!web
www   60 DYNA multifo!web
smtp     A    198.51.100.99

L’en­re­gis­tre­ment spé­cial DYNA re­tourne des en­trées A et AAAA après avoir consulté le gref­fon spé­ci­fié. Ici, le gref­fon multfifo im­plé­mente une sur­veillance en mode actif/actif des adresses IP de la ferme :

service_types => {
  web => {
    plugin => http_status
    url_path => /healthcheck
    down_thresh => 5
    interval => 5
  }
  ext => {
    plugin => extfile
    file => /etc/lb/ext
    def_down => false
  }
}

plugins => {
  multifo => {
    web => {
      service_types => [ ext, web ]
      addrs_v4 => [ 198.51.100.1, 198.51.100.2 ]
      addrs_v6 => [ 2001:db8::198.51.100.1, 2001:db8::198.51.100.2 ]
    }
  }
}

En mode no­mi­nal, une re­quête A re­ce­vra en ré­ponse à la fois 198.51.100.1 et 198.51.100.2. Si un test de dis­po­ni­bi­lité échoue, l’en­semble re­tourné est mis à jour. Il est éga­le­ment pos­sible de re­ti­rer une IP vo­lon­tai­re­ment en mo­di­fiant le fi­chier /etc/​lb/​ext. Par exemple, en met­tant le contenu sui­vant, 198.51.100.2 ne fera plus par­tie des réponses :

198.51.100.1 => UP
198.51.100.2 => DOWN
2001:db8::c633:6401 => UP
2001:db8::c633:6402 => UP

Tous les fi­chiers de confi­gu­ra­tion pour la mise en place de chaque ni­veau sont dis­po­nibles dans un dé­pôt Git. Si vous vou­lez re­pro­duire cette confi­gu­ra­tion à une échelle plus pe­tite, il est pos­sible de fu­sion­ner le se­cond et le troi­sième ni­veau, soit avec des es­paces de nom, soit avec une confi­gu­ra­tion spé­ci­fique de type lo­cal­node. Même si vous n’avez pas be­soin de ces ser­vices di­rects, vous de­vriez gar­der le der­nier ni­veau : alors que les ser­veurs fron­taux vont et viennent, les équi­li­breurs de charge L7 ap­portent de la sta­bi­lité, ren­dant l’en­semble plus robuste.