wiki/HowtoHaproxy.md

335 lines
11 KiB
Markdown
Raw Normal View History

2016-09-26 15:12:17 +02:00
---
2016-10-29 23:53:56 +02:00
title: Howto HAProxy
category: web HA
2016-09-26 15:12:17 +02:00
---
2016-12-30 13:44:35 +01:00
* Documentation (txt) : <http://haproxy.1wt.eu/download/1.5/doc/configuration.txt>
* Documentation (html) : <https://www.haproxy.com/doc/hapee/1.5/onepage/>
2016-11-04 02:42:04 +01:00
2016-12-28 12:08:17 +01:00
[HAProxy](http://www.haproxy.org/) est un puissant *load balancer* pour les protocoles TCP/HTTP/HTTPS. Il gère la répartition de charge et la tolérance de panne. Son principal auteur est [Willy Tarreau](http://1wt.eu/#wami), un développeur actif du noyau Linux. HAProxy est écrit en langage C, il est optimisé pour Linux, mais tourne également sous BSD. Des sites web importants l'utilisent comme Twitter, Github, Reddit, Airbnb, etc.
2016-09-26 15:12:17 +02:00
## Installation
~~~
# apt install haproxy
~~~
## Configuration
2016-11-04 02:42:04 +01:00
La configuration se passe dans le fichier `/etc/haproxy/haproxy.cfg` :
~~~
global
log 127.0.0.1 local5 debug
defaults
mode http
listen www
bind *:80
balance roundrobin
option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www.example.com
stats uri /haproxy-stats
stats auth foo:bar
server www00 192.0.2.1:80 maxconn 50 check inter 10s
server www01 192.0.2.2:80 maxconn 50 check inter 10s
~~~
On note l'activation des logs en *debug* ce qui permet de voir **toutes** les requêtes.
Attention, il faut donc que le démon `syslog` ait un paramétrage sur la facilité `local5` (ou autre selon votre configuration).
Pour `rsyslog` cela se fait ainsi dans `rsyslog.conf` :
~~~
local5.* -/var/log/haproxy.log
~~~
2016-09-26 15:12:17 +02:00
On vérifie qu'il n'y a pas d'erreur de syntaxes :
~~~
haproxy -c -V -f /etc/haproxy/haproxy.cfg
~~~
2016-11-04 02:42:04 +01:00
## Configuration avancée
### Exemple d'une configuration avec frontend/backend HTTP
2016-09-26 15:12:17 +02:00
~~~
global
log /dev/log local5
log /dev/log local5 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
default-server port 80 maxconn 250 on-error fail-check slowstart 60s inter 1m fastinter 5s downinter 10s weight 100
listen stats
bind *:8080
stats enable
stats uri /haproxy
stats show-legends
stats show-node
stats realm Auth\ required
stats auth foo:bar
stats admin if TRUE
frontend myfront
option forwardfor
maxconn 800
bind 0.0.0.0:80
default_backend myback
backend myback
balance roundrobin
server web01 192.0.2.1:80 check observe layer4 weight 100
server web02 192.0.2.2:80 check observe layer4 weight 100
server web03 192.0.2.3:80 check observe layer4 weight 100
~~~
2017-08-09 22:58:19 +02:00
La visualisation des statistiques peut aussi se faire via la console :
~~~
hatop -s /var/run/haproxy/admin.sock
~~~
2016-11-04 02:42:04 +01:00
### SSL
2016-09-26 15:12:17 +02:00
~~~
global
2016-11-04 02:42:04 +01:00
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
2016-09-26 15:12:17 +02:00
2016-11-04 02:42:04 +01:00
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
2017-01-06 14:48:34 +01:00
ssl-default-bind-options no-sslv3 no-tls-tickets
ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
ssl-default-server-options no-sslv3 no-tls-tickets
2016-09-26 15:12:17 +02:00
~~~
2017-01-06 14:48:34 +01:00
Dans un frontend il faut ensuite faire un "binding" avec des arguments pour le SSL :
~~~
frontend fe_https
bind 0.0.0.0:443 ssl crt /etc/ssl/haproxy/example_com.pem
2017-01-12 11:40:12 +01:00
http-request set-header X-Forwarded-Proto: https
default_backend myback
2017-01-06 14:48:34 +01:00
~~~
Le fichier `example_com.pem` doit contenir le certificat ainsi que la clé privée et éventuellement des paramètres Diffie-Hellman (tout au format PEM).
Il est possible d'indiquer plusieurs fois `crt /chemin/vers/fichier.pem` pour avoir plusieurs certificats possibles. HAProxy utilisera alors le mécanisme de SNI. Si on indique plutôt un dossier (par exemple `/etc/ssl/haproxy/`) tous les fichiers trouvés seront chargé par ordre alphabétique.
Pour chaque fichier PEM trouvé, HAProxy cherchera un fichier `.ocsp` du même nom. Il peut être vide ou contenir une réponse OCSP valide (au format DER). Cela active le mécanisme de « OCSP stapling »
Touts les détails de configuration pour l'attribut `crt` sont consultables sur <http://cbonte.github.io/haproxy-dconv/1.5/configuration.html#5.1-crt>
2017-01-12 11:40:12 +01:00
#### Terminaison SSL
Si HAProxy doit faire la **terminaison SSL et dialoguer en clair** avec le backend, on se contente de transmettre la requête.
~~~
backend myback
balance roundrobin
server web01 192.0.2.1:80 check observe layer4 weight 100
server web02 192.0.2.2:80 check observe layer4 weight 100
~~~
2017-01-12 11:46:07 +01:00
Si HAProxy doit faire la **terminaison SSL et maintenir une communication chiffrée** avec le backend, on doit le spécifier dans le backend (avec l'argument `ssl verify [none|optional|required]`, car le port 443 ne suffit pas à forcer le ssl).
2017-01-12 11:40:12 +01:00
~~~
backend myback
balance roundrobin
2017-01-12 11:46:07 +01:00
server web01 192.0.2.1:443 ssl verify none check observe layer4 weight 100
server web02 192.0.2.2:443 ssl verify none check observe layer4 weight 100
2017-01-12 11:40:12 +01:00
~~~
2017-01-12 11:46:07 +01:00
La vérification du SSL sur le backend est à voir en fonction des besoins de sécurité.
2016-11-04 02:42:04 +01:00
### Exemple avec plusieurs backends et du « sticky session »
2016-09-26 15:12:17 +02:00
~~~
global
[...]
defaults
[...]
frontend http-in
bind *:8080
# On définit des ACL qui associe un Host: HTTP à un backend
acl is_domain1 hdr_end(host) -i domain1.example.com
acl is_domain2 hdr_end(host) -i domain2.example.com
use_backend domain1 if is_domain1
use_backend domain2 if is_domain2
default_backend domain1
backend domain1
# Avec cette directive, HAProxy ajoute automatiquement un cookie SERVERID aux réponses HTTP,
# et l'utilise pour sélectionner le bon serveur lors de la prochaine requête
cookie SERVERID insert indirect
balance roundrobin
# Pour ce serveur, la valeur du cookie SERVERID sera "web01" (directive "cookie")
server web01 192.0.2.1:80 cookie web01 check
# Pour ce serveur, la valeur du cookie SERVERID sera "web02"
server web02 192.0.2.2:80 cookie web02 check
backend domain2
cookie SERVERID insert indirect
balance roundrobin
server web01 192.0.2.1:80 cookie web01 check
server web02 192.0.2.2:80 cookie web02 check
~~~
2016-11-04 02:42:04 +01:00
### Exemple en mode TCP
~~~
listen memcached 127.0.0.1:11211
option tcp-check
server nosql00 192.0.2.3:11211 check
server nosql01 192.0.2.4:11211 check backup
~~~
### Exemple pour MySQL
Il existe 2 modes principaux pour un proxy MySQL :
* le mode simple, qui effectue un test de connexion au serveur MySQL ;
* le mode avancé, qui exécute des tests poussés (et personnalisés) pour valider le bon fonctionnement du serveur.
#### Mode simple
HAProxy fourni une option "mysql-check".
Il va alors faire une connexion identifiée au serveur MySQL, puis la fermer et vérifiée dans les infos renvoyées que tout semble correct.
Ce mode ne nécessite pas d'outillage supplémentaire et nous le recommandons lorsqu'HAProxy agit seulement comme un proxy et pas comme un load-balancer ou pour de la tolérance de panne.
~~~
listen mysql 127.0.0.1:3306
mode tcp
option mysql-check user haproxy_check
server sql00 192.0.2.1:3306 check
~~~
#### Mode avancé
La version avancée consiste à utiliser un check http pour déterminer l'état du serveur.
2016-11-04 02:42:04 +01:00
~~~
listen mysql 127.0.0.1:3306
option httpchk
server sql00 192.0.2.1:3306 check port 8306
server sql01 192.0.2.2:3306 check port 8306 backup
~~~
On note l'option **httpchk** qui va permettre de faire un check en HTTP et vérifier des conditions avancées (réplication OK, etc.).
Un moyen simple (inspirés de ce [vieux blog post](http://sysbible.org/2008/12/04/having-haproxy-check-mysql-status-through-a-xinetd-script/)) est de créer un script qui sera déclenché par **xinetd**.
2016-11-04 02:42:04 +01:00
On ajoute un service à xinetd, dans `/etc/xinetd.d/mysqlchk` :
2016-11-04 02:42:04 +01:00
~~~
service mysqlchk {
flags = REUSE
socket_type = stream
port = 8306
2016-11-04 02:42:04 +01:00
wait = no
user = root
server = /root/mysqlchk
log_on_failure += USERID
disable = no
only_from = 192.0.2.0/27
}
~~~
On crée le script a exécuter dans `/root/mysqlchk` :
2016-11-04 02:42:04 +01:00
~~~{.bash}
2016-11-04 02:42:04 +01:00
MYSQL_HOST="127.0.0.1"
MYSQL_PORT="3306"
MYSQL_USERNAME="mysqlchk"
MYSQL_PASSWORD="PASSWORD"
TMP_FILE="/tmp/mysqlchk.out"
ERR_FILE="/tmp/mysqlchk.err"
/usr/bin/mysql --host=$MYSQL_HOST --port=$MYSQL_PORT --user=$MYSQL_USERNAME \
--password=$MYSQL_PASSWORD -e"show databases;" > $TMP_FILE 2> $ERR_FILE
uptime=`cat /proc/uptime | cut -f 1 -d .`
if [ "$(/bin/cat $TMP_FILE)" != "" ] && ( /root/nrpe-check-mysql-slave.sh >/dev/null || [ $uptime -gt 3600 ] ); then
# mysql is fine, return http 200
/bin/echo -e "HTTP/1.1 200 OK\r\n"
/bin/echo -e "Content-Type: Content-Type: text/plain\r\n"
/bin/echo -e "\r\n"
/bin/echo -e "MySQL is running.\r\n"
/bin/echo -e "\r\n"
else
# mysql is fine, return http 503
/bin/echo -e "HTTP/1.1 503 Service Unavailable\r\n"
/bin/echo -e "Content-Type: Content-Type: text/plain\r\n"
/bin/echo -e "\r\n"
/bin/echo -e "MySQL is *down*.\r\n"
/bin/echo -e "\r\n"
fi
~~~
`/root/nrpe-check-mysql-slave.sh` :
~~~{.bash}
2016-11-04 02:42:04 +01:00
#!/bin/sh
2016-11-04 02:42:04 +01:00
exec /usr/lib/nagios/plugins/check_mysql --check-slave -u debian-sys-maint -p PASSWORD -w 5 -c 60
~~~
Il est également possible d'utiliser tout programme ou script, pourvu qu'au final il puisse être accessible en HTTP.
2016-09-26 15:12:17 +02:00
### HTTP basic authentication
Pour mettre en place une authentification HTTP basique au niveau d'HAProxy, définir dans la section globale une liste d'utilisateur, soit avec un mot de passe en clair soit avec un mot de passe chiffré :
~~~
userlist NomDeMaUserList
user user1 insecure-password passwordEnClair
user user2 password $6$passwordSHA512
[…]
~~~
Dans le backend concerné rajouter :
~~~
auth AuthOkayPourMonSite http_auth(NomDeMaUserList)
http-request auth realm Texte if !AuthOkayPourMonSite
2017-01-12 14:39:54 +01:00
~~~
## Debug
~~~
# echo "show info" | socat stdio unix-connect:/run/haproxy/admin.sock
# echo "show acl" | socat stdio unix-connect:/run/haproxy/admin.sock
# echo "show acl #<ID>" | socat stdio unix-connect:/run/haproxy/admin.sock
2016-09-26 15:12:17 +02:00
~~~