Après quatre ans et trois mois, et pas moins de 26 versions de développement, la version réputée stable de HAProxy devient la 1.5. Même si HAProxy est avant tout un répartiteur de charge HTTP et TCP, les possibilités offertes par la version 1.5 en font le véritable couteau suisse du Web à haute charge.
Il est utilisé, entre autres, par de nombreux sites d’audience mondiale, tels que Twitter, Instagram, GitHub, Reddit… Cette version apporte de nombreuses nouveautés, dont la très attendue prise en charge de l’offloading SSL.
La version 1.5.0 a été rapidement suivie de quelques versions correctives. Nous en sommes à la 1.5.3, disponible depuis le 25 juillet dernier.
Sommaire
-
Listes des nouveautés
- Inspection du contenu
- Prise en charge du chiffrement (SSL)
- Gestion étendue de l’IPv6 et des sockets UNIX
- HTTP keep‐alive de bout en bout
- Compression des réponses HTTP
- Amélioration des ACL
- Les maps
- Compteurs d’activité et tables
- Vérification de la santé des serveurs améliorée
- Version 2 du proxy protocol
- Prise en charge de systemd
- Nouveau cycle de développement
- Exemple d’utilisation
Listes des nouveautés
Inspection du contenu
HAProxy 1.5 sait désormais capturer n’importe quelle partie d’une requête ou d’une réponse, y compris le contenu (payload) et les arguments passés dans l’URL (url_param
). Cette fonctionnalité est essentielle car elle est à la base de beaucoup d’autres nouveautés.
Pour l’administrateur système, le bénéfice est énorme : il peut désormais intervenir à n’importe quel moment de la transaction.
Prise en charge du chiffrement (SSL)
Jusqu’à présent, pour utiliser du chiffrement TLS avec HAProxy, il fallait soit utiliser le mode TCP, soit utiliser une version modifiée de STunnel. Désormais, HAProxy prend en charge le chiffrement SSL côté client comme côté serveur.
listen https_proxy
bind :443 ssl crt /etc/haproxy/site.pem
mode http
server web1 10.0.0.1:443 ssl verify check
server web2 10.0.0.2:443 ssl verify check
Il gère de nombreuses extensions au protocole TLS, telles que SNI, NPN/ALPN et OCSP, la validation des certificats côté serveur, les certificats clients.
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.crt verify required
Gestion étendue de l’IPv6 et des sockets UNIX
Il est désormais possible d’utiliser indifféremment l’IPv4, l’IPv6 ou encore les sockets UNIX, côté client comme côté serveur.
listen mysql_proxy
bind /var/tmp/mysql.sock user mysql mode 666
mode tcp
option mysql-check user haproxy post-41
server mysql 192.168.10.100:3306 check maxconn 200
server mysql_slave fe80:482:cafe::a200:e8ff:fe65:a:3306 check backup
HTTP keep‐alive de bout en bout
Le keep‐alive consiste à faire passer successivement plusieurs requêtes HTTP dans la même requête TCP. En général, dans une architecture Web un peu complète, on dispose de plusieurs services ayant des rôles particuliers. Par exemple, un service d’authentification, un service pour les contenus dynamiques, un autre pour les ressources statiques, etc. HAProxy doit alors inspecter chaque requête pour déterminer vers quel service l’envoyer. Jusqu’à présent, le keep‐alive de bout en bout n’autorisait que l’inspection de la première requête. Et l’administrateur passait des heures à se demander pourquoi telle requête n’arrivait pas au bon endroit.
C’est pourquoi l’option http-server-close
, permettant du keep‐alive côté client uniquement, était souvent utilisée.
Ce problème est désormais résolu.
Outre l’overhead réseau, le keep‐alive côté serveur est important car certains serveurs Web n’utilisent pas de chunk si celui‐ci est désactivé (ce problème est toutefois géré par l’option http-pretend-keepalive
).
Compression des réponses HTTP
Il est désormais possible d’utiliser HAProxy pour la compression des réponses HTTP. Les algorithmes gzip et deflate sont pris en charge. Elle peut être activée globalement ou localement.
compression gzip
compression type text/html text/plain
HAProxy gère toute une série de cas pour lesquels la compression n’est pas pertinente, notamment les réponses déjà compressées. Toutefois, il est possible, via l’option compression offload
, de demander aux serveurs de ne jamais compresser. Dans ce cas, l’entête Accept-Encoding
est réécrit à la volée.
Amélioration des ACL
Une conséquence directe de l’amélioration des possibilités de capture est qu’il est désormais possible de créer des listes de contrôle d’accès (ACL) sur l’ensemble des données capturables.
acl hello payload(0,6) -m bin 48656c6c6f0a
Il est également possible d’utiliser des variables :
http-request redirect code 301 location www.%[hdr(host)]%[req.uri] \
unless { hdr_beg(host) -i www }
Les maps
On pouvait déjà passer à HAProxy un fichier contenant une série de valeurs (par exemple une liste d’IP à autoriser). Il est désormais possible d’utiliser des fichiers clés‐valeurs avec les mots clés map_*
.
Par exemple, pour faire de la géolocalisation, il fallait jusqu’à présent écrire une série d’ACL :
acl src A1 -f A1.subnets
acl src A2 -f A2.subnets
acl src AD -f AD.subnets
acl src AE -f AE.subnets
acl src AF -f AF.subnets
acl src AG -f AG.subnets
acl src AI -f AI.subnets
acl src AL -f AL.subnets
acl src AM -f AM.subnets
acl src AN -f AN.subnets
ACL pour lesquelles chaque fichier contenait l’ensemble des sous‐réseaux d’un pays. Désormais, il est possible de faire un seul fichier sous la forme range pays :
223.228.0.0/14 IN
223.232.0.0/13 IN
223.240.0.0/13 CN
223.248.0.0/14 CN
223.252.0.0/17 AU
223.252.128.0/19 CN
223.252.160.0/24 CN
223.252.161.0/26 CN
223.252.161.64/26 HK
223.252.161.128/25 CN
223.252.162.0/23 CN
223.252.164.0/22 CN
223.252.168.0/21 CN
223.252.176.0/20 CN
223.252.192.0/18 CN
Puis, il suffit d’écrire :
http-request set-header X-Country %[src,map_ip(geoip.lst)]
Compteurs d’activité et tables
Il est désormais possible d’utiliser des tables pour comptabiliser l’activité d’un client pour des besoins aussi divers que de se souvenir d’une décision de routage ou bannir les indélicats.
Par exemple, pour éviter qu’un serveur se fasse bombarder de requêtes toujours identiques (brute force, spam) :
# Si on a une requête POST, on stocke dans la table un hash créé à partir de l'IP source et de l'url
stick-table type binary len 20 size 5m expire 1h store http_req_rate(10s)
tcp-request content track-sc1 base32+src if METH_POST
# Si on a un ratio supérieur à 10 requêtes en 10 secondes alors on bannit le client
acl bruteforce_detection sc1_http_req_rate gt 10
http-request deny if bruteforce_detection
Ces tables peuvent être contrôlées et vidées par le socket de contrôle :
echo show table |nc -U /tmp/haproxy.sock
# table: monserveur, type: binary, size:5242880, used:468
echo show table monserveur |nc -U /tmp/haproxy.sock |tail
0x8019d9f4c: key=FD0D68C950F7E432000000000000000000000000 use=0 exp=2956851 http_req_rate(10000)=0
0x8017f8e0c: key=FD19C86D50F7E432000000000000000000000000 use=0 exp=2206531 http_req_rate(10000)=0
0x8019d970c: key=FE3651B30209AF8C000000000000000000000000 use=0 exp=820386 http_req_rate(10000)=0
0x801bcbccc: key=FE3651B356D4F7D1000000000000000000000000 use=0 exp=2985182 http_req_rate(10000)=0
0x8017fed4c: key=FE3651B35A08E3FA000000000000000000000000 use=0 exp=1701016 http_req_rate(10000)=0
0x801be15cc: key=FE3651B36DDCDC20000000000000000000000000 use=0 exp=363622 http_req_rate(10000)=0
0x801bfba8c: key=FE3651B3973CC657000000000000000000000000 use=0 exp=1302278 http_req_rate(10000)=0
0x8017f8ecc: key=FEE742DC8D6942B3000000000000000000000000 use=0 exp=693453 http_req_rate(10000)=0
0x801bdff4c: key=FEE742DC9200499B000000000000000000000000 use=0 exp=3415931 http_req_rate(10000)=0
echo clear table monserveur |nc -U /tmp/haproxy.sock
echo show table |nc -U /tmp/haproxy.sock
sock
# table: monserveur, type: binary, size:5242880, used:1
Vérification de la santé des serveurs améliorée
Pour prendre des décisions de routage des requêtes, il est important de savoir quels serveurs sont en bonne santé. Outre les checks HTTP, largement paramétrables, HAProxy dispose en natif de tests de vie pour les protocoles mysql
, pgsql
, redis
, ssl
et smtp
. Il est toutefois désormais possible d’écrire des tests de vie personnalisés. Voici un exemple utilisant php-fpm
:
option tcp-check
# FCGI_BEGIN_REQUEST
tcp-check send-binary 01 # version
tcp-check send-binary 01 # FCGI_BEGIN_REQUEST
tcp-check send-binary 0001 # request id
tcp-check send-binary 0008 # content length
tcp-check send-binary 00 # padding length
tcp-check send-binary 00 #
tcp-check send-binary 0001 # FCGI responder
tcp-check send-binary 0000 # flags
tcp-check send-binary 0000 #
tcp-check send-binary 0000 #
# FCGI_PARAMS
tcp-check send-binary 01 # version
tcp-check send-binary 04 # FCGI_PARAMS
tcp-check send-binary 0001 # request id
tcp-check send-binary 0045 # content length
tcp-check send-binary 03 # padding length: padding for content % 8 = 0
tcp-check send-binary 00 #
tcp-check send-binary 0e03524551554553545f4d4554484f44474554 # REQUEST_METHOD = GET
tcp-check send-binary 0b055343524950545f4e414d452f70696e67 # SCRIPT_NAME = /ping
tcp-check send-binary 0f055343524950545f46494c454e414d452f70696e67 # SCRIPT_FILENAME = /ping
tcp-check send-binary 040455534552524F4F54 # USER = ROOT
tcp-check send-binary 000000 # padding
# FCGI_PARAMS
tcp-check send-binary 01 # version
tcp-check send-binary 04 # FCGI_PARAMS
tcp-check send-binary 0001 # request id
tcp-check send-binary 0000 # content length
tcp-check send-binary 00 # padding length: padding for content % 8 = 0
tcp-check send-binary 00 #
tcp-check expect binary 706f6e67 # pong
Par ailleurs, les vérifications peuvent être déléguées à un agent externe. Cela permet de configurer dynamiquement le poids du serveur dans le pool.
L’agent doit retourner soit une valeur entre 0 % et 100 % représentant le poids relatif au poids initialement donné au serveur, soit un mot clé ready (prêt), drain (n’accepte plus de nouvelles connexions mais traite celles en cours), maint (passe en maintenance), down, up… Pour mettre cela en place, il faut utiliser le mot clé agent-check.
Typiquement, cela peut être un script lancé par inetd
. Par exemple, pour adapter le poids du serveur en fonction de la charge :
#!/bin/sh
SYSCTL="/sbin/sysctl -n"
NCPU=$(${SYSCTL} hw.ncpu)
LOAD=$(${SYSCTL} vm.loadavg | awk '{print $2}')
LOAD=${LOAD%.*}
if [ ${LOAD} -ge ${NCPU} ]; then
PERCENT="100"
else
PERCENT=$((${LOAD}*100/${NCPU}))
fi
echo "$PERCENT%"
On paramètre ensuite inetd.conf
de la façon suivante :
dec-notes stream tcp nowait nobody /usr/local/bin/agent-check
Enfin, la configuration pour HAProxy est :
server server1 192.168.64.50:3389 weight 100 check agent-check agent-port 3333
Version 2 du proxy protocol
Le proxy protocol est un protocole simple qui permet à l’adresse IP d’un client d’être conservée lorsqu’une requête passe de serveurs en serveurs. Si en HTTP, l’en‐tête X-Forwarded-For
est largement utilisée à cette fin, il fallait un mécanisme plus universel, utilisable notamment en mode TCP. Initialement, ce protocole a été conçu pour utiliser HAProxy conjointement avec STunnel. Il est désormais pris en charge par Elastic Load Balancing, ExaProxy, Exim, Gunicorn, HAProxy, NGINX, Postfix, stud et stunnel.
La version 2 modifie profondément la nature du protocole, puisqu’on passe d’une version humainement lisible à une version binaire.
Par exemple, avec Postfix, il faudra dans HAProxy utiliser la configuration ci‐après :
mode tcp
option smtpchk
server postfix 127.0.0.1:10024 send-proxy check
Une configuration Postfix (master.cf
) sera aussi nécessaire :
postscreen_upstream_proxy_protocol = haproxy
Prise en charge de systemd
HAProxy ne peut pas recharger sa configuration. Pour le redémarrer, on doit tuer l’ancien processus et en lancer un nouveau. Toutefois, afin de ne pas fermer les sessions brutalement, il existe l’option -st
qui permet au nouveau processus de remplacer l’ancien proprement.
Ce comportement ne convenant pas à systemd, qui ne sait pas remplacer un processus par un autre, l’option -Ds
et le démon haproxy-systemd-wrapper
permettent désormais de fonctionner avec ce système d’initialisation.
Nouveau cycle de développement
Willy Tarreau explique dans un long courriel ses plans pour les prochaines versions de HAProxy, ainsi que les leçons à tirer de la version 1.5, dont le développement a été particulièrement long.
Selon lui, il faut arrêter de promettre telle ou telle fonctionnalité pour telle version, et fonctionner en périodes, à la mode Linux. Ainsi le développement de la version 1.6 s’arrêtera en mars 2015, pour une sortie en mai ou juin de la même année.
Il indique toutefois les directions qu’il souhaite prendre :
-
multi‐processus : meilleure synchronisation des états et des vérifications. Vue l’arrivée de ces monstres de latence que sont SSL et la compression, une architecture gérant la concurrence serait sans doute bénéfique. Il est toutefois exclu d’ajouter des mutex partout dans le code.
- reconfiguration à chaud : dans les environnements dynamiques, on est souvent contraint de relancer HAProxy. Même si de nombreuses possibilités sont désormais offertes par le socket de contrôle, il serait intéressant d’aller plus loin dans ce sens.
- DNS : actuellement HAProxy ne résout les noms d’hôte qu’au démarrage. Il serait intéressant, en particulier pour les utilisateurs de EC2, que cela puisse être fait dynamiquement.
- cache d’objets en mémoire : il ne s’agit pas de concurrencer Varnish, mais d’implémenter un petit cache afin de réduire le trafic entre HAProxy et les serveurs d’origine des objets. Plutôt un cache avec un ratio de hits de 50 % sans maintenance, qu’un cache efficace à 90 % mais demandant beaucoup d’attention.
- amélioration de la réutilisation des connexions : dans certains cas, il est particulièrement intéressant de conserver les connexions réseau en vie, même si cela ouvre de nouvelles problématiques comme la supervision des connexions.
- HTTP/2 : Willy Tarreau étant particulièrement investi dans le groupe de travail http-bis qui spécifie entre autres HTTP/2, c’est donc sans surprise qu’il place l’implémentation de cette nouvelle version du protocole en tête de ses priorités.
Exemple d’utilisation
Imaginons un site Web servi par un parc de serveurs sur lesquels nous voulons mettre en place de l’équilibrage de charge (load balancing) et de la tolérance de panne. Une bonne idée est alors de créer un cluster de HAProxy actif/actif avec au moins deux machines en utilisant le round‐robin DNS et un mécanisme de failover tel que CARP ou keepalived.
Nous allons commencer par une configuration basique à laquelle nous ajouterons peu à peu des fonctionnalités.
Paramètres globaux
On commence par définir quelques généralités comme les journaux (logs), le maximum de connexions, le comportement du démon.
global
log /var/run/log local0 notice
maxconn 4096
uid 99
gid 99
daemon
chroot /var/empty
Paramètres par défaut
On ajoute ensuite, une section par défaut qui contiendra l’ensemble des paramètres qui, s’ils ne sont pas surchargés, s’appliqueront à nos proxys/mandataires.
defaults
# par défaut on logue avec ce qui est défini
# dans la section globale
log global
# on peut définir très finement les différents timeout
# le temps qu'une connexion client peut rester idle
timeout client 40s
# le temps entre une réponse et le début de la requête
# suivante dans une connexion keepalive
timeout http-keep-alive 30s
# Le temps entre le début d'une requête et la fin des entêtes
timeout http-request 10s
# Workaround pour certains navigateurs
# bogués avec le timeout http-request
errorfile 408 /dev/null
# Le temps durant lequel on attend une réponse du serveur
timeout server 60s
# Le temps qu'une connexion peut rester en attente
# d'un slot disponible
# timeout queue 1500
# le temps que met un client a établir une connexion tcp
timeout connect 5s
# Combien de fois on réessaye de se connecter à un serveur
retries 3
# On redistribue les sessions en cas d'échec de
# connexion avec un serveur
option redispatch
# Par défaut on fait du http
mode http
# Par défaut le mot check signifie check http
option httpchk
# On ajoute l'entête X-Forwarded-Forr
option forwardfor
# On utilise l'algorithme roundrobin
balance roundrobin
# On logue des sessions http
option httplog
# On ne logue pas les connexions sur lesquelles
# il ne se passe rien
option dontlognull
# Aucun proxy ne pourra avoir plus de 2048 connexions ouvertes
# Soit la moitié des connexions acceptables
maxconn 2048
Le proxy/mandataire
On déclare ensuite notre proxy de la façon la plus simple possible, on ajoutera les fioritures après :
listen http_proxy
bind :80
server web1 192.168.42.11:80
server web2 192.168.42.12:80
server web3 192.168.42.13:80
Il est nommé http_proxy, écoute sur le port 80. Il équilibre les connexions suivant l’algorithme round‐robin vers trois serveurs nommés web1, web2 et web3. D’autres algorithmes de répartition sont bien évidement disponibles :
- static-rr est une variante de round‐robin sans limitation du nombre de serveurs (4 095 pour round‐robin), mais sans prise en compte du poids ;
- leastconn, adapté aux sessions longues (LDAP, TSE…), c’est le serveur qui a le moins de connexions actives (pondéré par son poids) qui est choisi ;
-
first, le premier serveur est utilisé jusqu’à
maxconn
, puis on passe au suivant, ce qui est idéal pour pouvoir éteindre des serveurs aux heures creuses ; - d’autres algorithmes basés sur un hash de l’IP, l’URL, un paramètre de la requête, un en‐tête HTTP, ou le cookie RDP.
Le paramètre balance
défini dans la section globale, pourrait être surchargé ici.
Savoir ce qu’il se passe
Il y a plusieurs défauts sur notre configuration :
- les logs ne fonctionnent pas car HAProxy étant « chrooté », il ne peut pas accéder au socket UNIX ;
- si un serveur Web tombe, HAProxy n’est pas au courant et nous non plus.
Concernant les logs, il suffit de modifier la section globale :
log localhost local0 notice
Il faudra ensuite configurer son serveur de log préféré pour pouvoir récupérer les logs en UDP sur l’interface loopback.
Pour vérifier l’état de santé des serveurs il suffit d’ajouter le mot clé check à la fin de la déclaration des serveurs :
server web1 192.168.42.11:80 check
Dans ce cas, HAProxy lancera une requête OPTIONS /
sur le serveur toutes les 2 secondes. On peut chercher à faire plus fin, comme, par exemple, une requête vers une URL applicative se connectant à une base de données. En bref, ne pas vérifier simplement l’état de santé du serveur mais également celui de l’application. Dans ce cas, il faut compléter l’option httpchk
:
listen http_proxy
bind :80
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
http-check expect string pong
server web1 192.168.42.11:80 check inter 30s
server web2 192.168.42.12:80 check inter 30s
server web3 192.168.42.13:80 check inter 30s
Du coup, on va aller chercher sur chaque serveur l’URL /ping
de l’hôte www.example.com toutes les 30 secondes et l’on attendra que le serveur nous réponde avec une page contenant le mot « pong ».
Nos serveurs sont donc bien surveillés par HAProxy, mais pour notre part — exception faite des logs — nous sommes aveugles. Pour remédier à cela, nous allons activer le socket de contrôle ainsi que l’interface d’administration. La première s’active dans la section globale :
stats socket /tmp/haproxy_prod_admin.sock user root group nagios mode 660 level admin
On peut ensuite l’utiliser avec un programme comme socat
par exemple. Elle est également utilisée par des utilitaires comme HAtop ou l’excellente sonde Nagios de Polymorf :
socat /tmp/haproxy.sock readline
prompt
> help
Unknown command. Please enter one of the following commands only :
clear counters : clear max statistics counters (add 'all' for all counters)
clear table : remove an entry from a table
help : this message
prompt : toggle interactive mode with prompt
quit : disconnect
show info : report information about the running process
show stat : report counters for each proxy and server
show errors : report last request and response errors for each proxy
show sess [id] : report the list of current sessions or dump this session
show table [id]: report table usage stats or dump this table's contents
get weight : report a server's current weight
set weight : change a server's weight
set table [id] : update or create a table entry's data
set timeout : change a timeout setting
set maxconn : change a maxconn setting
set rate-limit : change a rate limiting value
disable : put a server or frontend in maintenance mode
enable : re-enable a server or frontend which is in maintenance mode
shutdown : kill a session or a frontend (eg:to release listening ports)
Quant à la seconde, elle s’active soit dans un proxy à part, soit dans un proxy existant. Celle‐ci donne l’état actuel des proxies, ainsi qu’un certain nombre de statistiques.
listen http_proxy
bind :80
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
http-check expect string pong
server web1 192.168.42.11:80 check inter 30s
server web2 192.168.42.12:80 check inter 30s
server web3 192.168.42.13:80 check inter 30s
stats enable
stats uri /admin?stats
stats realm Haproxy\ Statistics
stats auth admin:s3cR3T
Routage plus complexe
Pour l’instant notre schéma de fonctionnement est très simple. S’il doit se complexifier, il deviendra utile de séparer les blocs listen
en frontend
et backend
.
Cela nous donne :
frontend http_proxy
bind :80
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:s3cR3T
default_backend web_servers
backend web_servers
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
http-check expect string pong
server web1 192.168.42.11:80 check inter 30s
server web2 192.168.42.12:80 check inter 30s
server web3 192.168.42.13:80 check inter 30s
Ainsi, si nous souhaitons offrir la possibilité d’utiliser le site en version HTTPS, il nous suffit d’ajouter un second frontal :
frontend https_proxy
bind :443 ssl crt /etc/certificates/website.pem
reqadd X-Forwarded-proto:\ https
default_backend web_servers
Si derrière on utilise Apache, ajoutons dans sa configuration la ligne suivante :
SetEnvIfNoCase X-Forwarded-Proto HTTPS HTTPS=on
Cela permettra à nos utilisateurs de retrouver les bonnes variables d’environnement (en PHP par exemple : $_SERVER["HTTPS"]
).
Si l’on veut reconnaître un utilisateur authentifié grâce au cookie MYSSO, il est alors facile de le rediriger systématiquement vers la version chiffrée du site :
frontend http_proxy
bind :80
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:s3cR3T
acl is_logged cook MYSSO -m found
redirect scheme https if is_logged
default_backend web_servers
Bien évidemment, on peut souhaiter qu’ils s’identifient sur une page chiffrée, voire sur des serveurs séparés :
frontend http_proxy
bind :80
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:s3cR3T
acl is_logged cook MYSSO -m found
acl to_login url_beg /login
redirect scheme https if is_logged or to_login
default_backend web_servers
À noter que pour former la condition, le or
doit être spécifié alors qu’un and
aurait été implicite.
Enfin, nous voulons que l’accès aux statistiques et au backoffice du site soit sécurisé par une vérification du certificat client. Nous allons commencer par créer un back‐end particulier donnant accès au serveur de back office. Il contiendra en plus l’accès aux statistiques que nous supprimons du coup du front‐end public :
backend very_secure
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:s3cR3T
server admin 192.168.9.42:80
Le front‐end public devient :
frontend http_proxy
bind :80
acl is_logged cook MYSSO -m found
acl to_login url_beg /login
redirect scheme https if is_logged or to_login
default_backend web_servers
Sur la partie HTTPS, nous allons donc vérifier la présence d’un certificat client et, s’il est présent et valable, donner l’accès au back office et aux statistiques HAProxy. Pour cela, il faut fournir à HAProxy l’autorité de certification (ca-file
) et éventuellement une liste de révocation (crl-file
) :
frontend https_proxy
bind :443 ssl crt /etc/certificates/website.pem ca-file /etc/certificates/website.ca verify optional crl-file /etc/certificates/website.crl
reqadd X-Forwarded-proto:\ https
acl client_ok ssl_fc_has_crt
acl want_admin if url_beg /admin
acl want_admin if url_beg /haproxy
use_backend very_secure if want_admin and client_ok
default_backend web_servers
La négociation SSL échouera si le certificat présenté est invalide ou révoqué, mais pas s’il est absent (mot‐clé optional
).
Coller au serveur et gérer les pannes
Pour diverses raisons, on peut vouloir que le client ne change pas de serveur durant la durée de sa session. La plus mauvaise de ces raisons est que les sessions applicatives ne sont pas partagées, la meilleure étant que cela permet d’effectuer des trucs sympathiques, comme des mises à jour applicatives sans interruption de service ou encore de faciliter le débogage.
La façon la plus simple de faire est d’insérer un cookie. Si celui‐ci est présent, HAProxy essayera d’utiliser le serveur visé :
backend web_servers
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
http-check expect string pong
cookie SRV insert
server web1 192.168.42.11:80 check inter 30s cookie web1
server web2 192.168.42.12:80 check inter 30s cookie web2
server web3 192.168.42.13:80 check inter 30s cookie web3
Ainsi, pour savoir sur quel serveur vous êtes, il suffit de regarder la valeur du cookie SRV.
C’est aussi le moment d’introduire la notion de poids. On peut en effet moduler l’importance de chaque serveur au sein du répartiteur de charge (load‐balancer) :
backend web_servers
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
http-check expect string pong
cookie SRV insert
server web1 192.168.42.11:80 check inter 30s cookie web1 weight 10
server web2 192.168.42.12:80 check inter 30s cookie web2 weight 10
server web3 192.168.42.13:80 check inter 30s cookie web3 weight 20
Le poids peut être piloté par le socket de contrôle. Par exemple, pour passer le poids du serveur web2 à zéro :
echo "set weight web_servers/web2 0" | nc -U /tmp/haproxy_prod_admin.sock
Un poids de 0 ne signifie pas que le serveur est tombé, mais qu’il n’accepte plus de nouvelle session (laissant ainsi finir tranquillement celles en cours).
On peut également spécifier un serveur de secours (backup) :
backend web_servers
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
option allbackup
http-check expect string pong
cookie SRV insert
server web1 192.168.42.11:80 check inter 30s cookie web1 weight 10
server web2 192.168.42.12:80 check inter 30s cookie web2 weight 10
server web3 192.168.42.13:80 check inter 30s cookie web3 weight 20
server maintenance1 192.168.42.21:80 backup check inter 30s
server maintenance2 192.168.42.22:80 backup check inter 30s
L’option allbackup
spécifie que lorsque tous les serveurs sont tombés, tous les serveurs de secours (et pas seulement le premier disponible) doivent être utilisés.
Bien sûr, le backup est utile pour servir les pages de maintenance, mais également pour se replier en cas de problème logiciel. Ainsi, par exemple, si nous souhaitons utiliser Varnish pour servir les fichiers statiques, nous pouvons faire quelque chose comme :
acl url_static url_reg -i ^\/(.*)\.(js|jpg|JPG|jpeg|gif|png|ico|txt|css|pdf)(\?.*)?
use_backend varnish if url_static
dans les front‐ends, puis :
backend varnish
option httpchk GET /ping HTTP/1.1\r\nHost:\ www.example.com
option allbackup
http-check expect string pong
server varnish 127.0.0.1:8080 check inter 30s
server web1 192.168.42.11:80 check inter 30s backup
server web2 192.168.42.12:80 check inter 30s backup
server web3 192.168.42.13:80 check inter 30s backup
Ainsi, en cas d’indisponibilité du serveur Varnish, les fichiers sont servis par les serveurs principaux.
Et ainsi de suite
Lorsqu’on en prend l’habitude, HAProxy devient vite un outil indispensable. Il offre une telle souplesse, que ce soit en mode TCP ou HTTP, qu’il est difficile de rencontrer des situations pour lesquelles on ne puisse pas imaginer de solutions l’utilisant. D’autant plus qu’il excelle en termes de performance. Autrement dit : c’est bon, mangez‐en !
Aller plus loin
- Site officiel (878 clics)
- Changements (140 clics)
- Dépot Git (91 clics)
- Guide de configuration (364 clics)
# bravo !
Posté par Nico C. . Évalué à 10.
Effectivement, c'est du beau et du bon !
Merci beaucoup pour ce magnifique "piece of software" !
[^] # Re: bravo !
Posté par wysman . Évalué à -2.
Il va falloir que je test ça !
# HAProxy + Nginx + Varnish + Webserver
Posté par desktop.ready . Évalué à 7.
Très intéressante dépêche, merci !
Si cela peut aider d'autres personnes, voici quelques bonnes explications sur comment HAProxy s'intègre avec Varnish et Nginx :
Ordering: 1. nginx 2. varnish 3. haproxy 4. webserver?
Mon plus gros problème actuellement c'est d'avoir une bonne démarche pour optimiser tout cela.
Il y a tellement de configurations matérielles différente, de paramètres, d'options et de logiciels qui se recoupent plus ou moins, que c'est difficile d'optimiser en ayant une démarche/méthodologie claire.
Je pensais partir sur une solution simple et je suis arrivé à un PostgreSQL + Memcached + Django + uWSGI + Nginx + Varnish (et peut-être HAProxy à venir). Le plus dur est d'identifier les goulots d'étranglement et d'améliorer certains points sans en dégrader d'autres.
Des fois je me dis que cela ressemble plus à de l'alchimie qu'à une science ! ;-)
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par Joris Dedieu (site web personnel) . Évalué à 5.
Utiliser un logiciel bien adapté pour chaque tâche est souvent plus simple qu'un gros serveur qui fait tout.
Rien n’empêche de piloter son travail par des tests. C'est une démarche encore bien rare au niveau de l'administration système en général et de l’hébergement web en particulier.
La première question à se poser est celle de savoir comment je détermine si mon application fonctionne. Quels sont les scenarii de visite ? quels sont les temps souhaités d'affichage des différentes pages ? quel est le nombre de visiteurs attendu ? quel est le volume maximal de données attendu ?
A partir de ces données tu dois écrire au moins deux types de tests :
La bonne exécution des tests ne comprend pas seulement leur réussite, mais leur réussite dans le temps impartie.
A partir de là si tu travailles en preprod avec des données abondantes, tu es blindé.
En second point, l'analyse des logs doit être systématique. Chaque erreur doit être comprise, reproduite et corrigée.
Enfin, il faut prévoir tous les cas de panne. Qu'est-ce que je fais en cas du :
Chaque cas, doit faire l'objet à minima d'une procédure au mieux d'un traitement automatisé.
La même démarche doit s'appliquer à la sécurité.
Maintenant comme je te le disais, dans le monde du web, les gens voulant travailler ainsi sont bien rares ou on une taille telle que cela se justifie pleinement. Dans la plupart des cas, il faut se démerder au pifomètre sans même quelqu'un qui connait le code et dans se cas, effectivement, il vaut mieux avoir du métier.
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par desktop.ready . Évalué à 3.
Merci pour cette réponse fort instructive.
Pour mon cas person (site amateur) je cherchais plus une démarche progressive et didactique histoire de bien comprendre les tenants et aboutissants de l'optimisation.
Par exemple j'ai commencé par mesurer les accès à la DB, puis joué avec quelques caches intermédiaires Django-DB (Memcached par exemple) pour voir l'effet.
Ensuite j'ai configuré Nginx et tenté d'optimiser les fichiers statiques.
Ensuite j'ai configuré uWSGI et joué avec plusieurs paramètres pour bien comprendre comment l'adapter à la configuration matérielle.
Enfin j'ai introduit Varnish et regardé l'impact sur les performances.
Malheureusement je n'ai pas trouvé de pages où on explique proprement ce genre de démarche progressive et didactique. Peut-être que ce n'est pas possible à cause du nombre de paramètres qui peuvent tout changer.
En tout cas cela m'a montré tout le travail derrière un résultat qui nous semble simple à priori : la page s'affiche vite !
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par xcomcmdr . Évalué à 2.
s/scenarii/scénarios
"Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par Zenitram (site web personnel) . Évalué à 0.
moinssé mais il a raison…
http://fr.wiktionary.org/wiki/sc%C3%A9narii
"Forme italianisante et archaïque du pluriel de scénario. Note d’usage : Le pluriel usuel est scénarios. On notera que ce dernier est préconisé par la rectification orthographique de 1990. "
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par zul (site web personnel) . Évalué à -2.
Ce qui prouve une fois de plus l'absurdité du système de moinssage :)
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par barmic . Évalué à 5.
La réforme de 90 n'est pas obligatoire. Pour le reste il est dis que c'est une forme un peu vieillotte, pas qu'elle n'est pas conforme. L'imparfait du subjonctif n'est pas usuel, il n'est point faux pour autant (la forme "ne … point" aussi n'est guère usuelle).
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par CHP . Évalué à 1.
Plussé mais tu as tort…
Scenarii n'est pas incorrect, juste un peu vieillot.
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par DerekSagan . Évalué à 1.
Si les gens veulent écrire scenarii par pseudo-pédantisme ou pour faire exotique pourquoi pas, ce n'est pas incorrect. Le vrai problème c'est la flopée de rigolos qui écrivent scénarii avec l'accent français et le pluriel italien. :-)
[^] # Re: HAProxy + Nginx + Varnish + Webserver
Posté par tyoup . Évalué à 2. Dernière modification le 01 septembre 2014 à 08:35.
C'est plus ou moins pire que les gens qui écrivent data avec un s ?
# Nginx ou HAProxy + Varnish
Posté par Spack . Évalué à 3.
Pour un load balancer qui fait aussi mise en cache, vers quelle solution vous tourneriez vous ?
Nginx, à l'avantage d'offrir pas mal de fonctionnalités concernant la répartition de charge et permet la mise en cache assez simplement. En bidouillant un peu, on peut y ajouter la répartition de charge sur les connexions TCP.
De l'autre côté, HAProxy et Varnish sont deux mastodontes dans leur domaine mais ce dernier nécessite GCC pour sont bon fonctionnement.
gcc -c hackme.c hackme
???En bref, qui pour quel usage ?
[^] # Re: Nginx ou HAProxy + Varnish
Posté par Joris Dedieu (site web personnel) . Évalué à 6.
Tout d'abord, il ne faut pas oublier que Varnish seul est également une option. Il fait également répartiteur de charge. Il a besoin de la libgcc pour compiler sa conf. Je ne trouve pas que ce soit vraiment un problème. Quelqu'un qui peut uploder et compiler un exploit, pouvant également uploader un binaire statique.
Pour répondre à ta question, je dirais que tout dépend avant tout de celui que tu connais le mieux et du temps que tu es prêt à passer pour apprendre.
Nginx offre de nombreuses possibilités en particulier parce qu'il est scriptable en lua et qu'il dispose de nombreux modules. CloudFlare par exemple l'utilise massivement. Mais il faut souvent le patcher pour ceci ou pour cela. Par exemple support du verbe PURGE.
Varnish est très performant niveau cache, en particulier grâce à la possibilité de pousser une conf dynamiquement et d'utiliser les ESI.
Haproxy offre des possibilités en terme de répartition de charge et de gestion des connexions que les autres n'ont clairement pas.
Bref a mon avis, ce qui est important est de très bien connaitre les softs que tu met en oeuvre. Donc celui avec lequel tu as le plus d'affinité conviendra très bien.
# Félicitations !
Posté par Sébastien Rohaut . Évalué à 4.
Je te félicite, Willy, ainsi que tous les contributeurs, pour l'excellence de ce produit, et la superbe réactivité face aux échanges que certains (comme moi) ont eu avec toi (cas du include :) ).
Nous utilisons HAProxy pour des sites web, et certes, il faut prendre le temps d'appréhender le produit et sa configutation qui peut être autant simple que très complexe, mais quel bonheur de se débarrasser ainsi de matériels chers et parfois bien trop complexes (chiants) à configurer !
Nous utilisons le trio keepalived (pour le VRRP), quagga (pour OSPF) et HAProxy, le tout automatisé via ansible, et nous avons maintenant la parfaite maitrise de nos LB, de bout en bout.
Encore merci !
# configuration un peu laborieuse
Posté par mathieu mathieu (site web personnel) . Évalué à 1.
J'utilise dans mon travail haproxy + varnish dans un environnement VPC sous AWS/EC2.
Ce qui m'énerve avec la conf haproxy est que l'on ne peut pas mettre un interval/mask d'ips pour une liste de serveur.
Ayant des autoscalers dans des /24, je suis contraint de faire 255 lignes dans des fichiers de conf: c'est lourd!
J'ai vu des hacks/patchs qui ont tenté gérer des masks, mais apportaient toujours trop de régression et n'ont jamais été intégrés à la branche master.
Sinon ca fait le job.
# Excellent article
Posté par tarreau willy (site web personnel) . Évalué à 4.
Merci pour cet excellent article. Il répond mieux aux questions des utilisateurs que je ne parviens à le faire. A l'avenir je les redirigerai vers cet article pour savoir ce qui a changé dans la 1.5 :-)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.