--- title: Howto HAProxy category: web HA --- * HAProxy 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, 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 mondiaux l'utilisent (Twitter, Github, Reddit, Airbnb, etc.)/ ## Installation ~~~ # apt install haproxy ~~~ ## Configuration 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 ~~~ ## Configuration avancée ### Exemple d'une configuration avec frontend/backend HTTP ~~~ 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 ~~~ ### SSL ~~~ # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # 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 ssl-default-bind-options no-sslv3 ~~~ ### Exemple avec plusieurs backends et du « sticky session » ~~~ 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 ~~~ ### 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 ~~~ 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.) grâce à différents scripts (inspirés de ce [vieux blog post](http://sysbible.org/2008/12/04/having-haproxy-check-mysql-status-through-a-xinetd-script/)). `/etc/xinetd.d/mysqlchk` : ~~~ service mysqlchk { flags = REUSE socket_type = stream port = 80 wait = no user = root server = /root/mysqlchk log_on_failure += USERID disable = no only_from = 192.0.2.0/27 } ~~~ `/root/mysqlchk` : ~~~ 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` : ~~~ #!/bin/sh exec /usr/lib/nagios/plugins/check_mysql --check-slave -u debian-sys-maint -p PASSWORD -w 5 -c 60 ~~~ ### 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 ~~~