Hlavní navigace

Velká řešení otevřeně: load balancing s HAProxy

Jan Baier

Dnes si na svém „clusteru“ rozšíříme možnosti load-balancingu a zjednodušíme nastavování aplikačních serverů. Použijeme k tomu TCP/HTTP proxy HAProxy, která toho umí opravdu mnoho.

Prerekvizity

Servery jsme zanechali ve stavu, kdy máme zajištěno, že obě naše veřejné adresy budou dostupné alespoň na jednom stroji (samozřejmě za předpokladu, že nemáme smůlu a neselžou oba zároveň). Na každém z našich serverů poběží jedna instance HAProxy, která bude ovšem poslouchat na obou potenciálních adresách (tedy i na té, která patří druhému serveru a není zrovna přítomna na žádném rozhraní).

Abychom umožnili aplikaci poslouchat i na adrese, která zrovna není nakonfigurována, je třeba udělat malý zásah do sysctl.conf a přidat net.ipv4.ip_nonlocal_bind = 1, a pokud jsme tak ještě neučinili, je dobré nezapomenout také na  net.ipv4.ip_forward = 1.

Základy

Konfigurace proxy se skládá z několika bloků:

  • global, ve kterém se nastavují globální věci kolem běhu samotné HAProxy;
  • defaults, kam patří položky, které nechceme opakovat ke každému jednotlivému bloku a které, jak název napovídá, se použijí, pokud nespecifikujeme jinak;
  • frontend, kterých může být několik a slouží jako vstupní brána pro požadavky;
  • backend, opět několikrát, definuje služby, které se poskytují skrze frontendy.

Minimalistická konfigurace by tedy mohla vypadat zhruba takto:

# /etc/haproxy/haproxy.cfg
global
    log /dev/log local0
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon

defaults
    mode http
    option httplog
    option donlognull
    option forwardfor

frontend http
    bind 198.51.100.10:80, 198.51.100.20:80
    default_backend www

backend www
    server www 127.0.0.1:8000

S touto konfigurací získáme proxy server, který poslouchá na standardním HTTP portu (port 80) a plovoucích adresách (v minulém díle jsme si ukázali, jak na to pomocí keepalived), veškerou komunikaci směruje na port 8000 a do požadavku přidává hlavičku X-Forwarded-For s IP adresou klienta (volba  option forwardfor).

TLS terminace a spolupráce s Let's Encrypt

V dnešní době propagace HTTPS může být HAProxy užitečným pomocníkem ve vystavování a servírování certifikátů. Můžeme tak veškerou logiku okolo HTTPS mít na jednom místě a aplikační servery se nemusí vůbec zatěžovat TLS (dříve SSL) provozem (za předpokladu, že mezi proxy a aplikačním serverem máme nějakou bezpečnou linku – dedikovaný port, virtuální port, VPN spoj).

Do nastavení frontendu přidáme direktivu pro poslouchání na HTTPS portu například takto: bind *:443 ssl strict-sni crt /etc/haproxy/certs. Volba strict-sni nařizuje, že spojení bude navázáno jen tehdy, bude-li klient vyžadovat SNI, které odpovídá některému z načtených certifikátů. Složka s certifikáty je za volbou crt, do této složky stačí nakopírovat všechny používané certifikáty (každý v jednom samostatném souboru obsahujícím veřejný certifikát, případné mezilehlé certifikáty a soukromý klíč).

Dále je vhodné v sekci global vyladit nastavení podporovaných šifer pomocí volby ssl-default-bind-ciphers, konkrétní hodnoty nebudu uvádět, neb je stejně nutné je pravidelně kontrolovat a obměňovat na základě aktuálních bezpečnostních doporučení (a dle požadované úrovně kompatibility se starými klienty).

Pokud budeme chtít, aby aplikace za proxy věděly o tom, že komunikace probíhá pomocí zabezpečeného protokolu, můžeme například do požadavku přidat pár dalších hlaviček. V sekci frontend přidáme http-request set-header X-Forwarded-Proto https if { ssl_fc }http-request set-header X-Forwarded-Port %[dst_port].

Ve chvíli, kdy se rozhodneme, že nic jiného než HTTPS už nebudeme používat, můžeme veškerý HTTP provoz přesměrovat na zabezpečenou variantu pomocí http-request redirect scheme https if !{ ssl_fc } (to lze dělat i na úrovni jednotlivých domén či URL). Na závěr ještě můžeme nastavit HSTS  http-response set-header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload".

Získávání certifikátů je pomocí autority Let's Encrypt dobře zautomatizovatelné. Můžeme použít třeba oficiálního klienta certbot a ověřování ve stand-alone módu. Pro naše účely budu uvažovat, že jej již máme nachystán ve virtuálním stroji s názvem certbot a lokální adresou 192.168.100.1/24. Do HAProxy přidáme následující nový backend

backend letsencrypt
    server certbot 192.168.100.1:80

a pravidlo pro jeho použití ve frontendu use_backend letsencrypt if { path_beg /.well-known/acme-challenge }. Od této chvíle si můžeme vystavit certifikát pro libovolnou doménu nasměrovanou na naši proxy. Zbývá už jen zařídit kopírování certifikátu ve správném formátu z virtuálního stroje na proxy a opětovné načtení konfigurace HAProxy (aby si obnovila seznam certifikátů). To ale zajistí pár dobře napsaných skriptů.

Byla by také škoda nezmínit možnost použití klientského TLS certifikátu pro účely autentizace v aplikaci za proxy. V HAProxy toho lze jednoduše docílit pomocí dalších voleb direktivy bind  – ca-file, verify optional, případně verify required  – v kombinaci s nastavením hlavičky v případě úspěšné kontroly certifikátu  http-request set-header X-SSL-User %{+Q}[ssl_c_s_dn(emailAddress)] if { ssl_c_verify 0 }.

Load-balancing, fail-over a jiné hrátky

Hlavní důvod proč nasadit HAProxy je ale trochu jinde. Umožňuje nám jednoduše a elegantně nastavit více serverů pro stejný backend (tj. aplikaci), hlídat jejich dostupnost a dynamicky rozkládat zátěž. Jak na jednom frontendu nastavit různé backendy dle URL jsme si již ukázali na příkladu Let's Encrypt. Pro úplnost ještě dodám, že pro výběr dle domény můžeme použít filtr hdr_dom(host), tedy například use_backend example if { hdr_dom(host) example.net }. Pokročilejší možnosti jsou k nalezení v manuálových stránkách.

Pokud v sekci backend uvedeme direktivu server vícekrát s různými adresami, bude mezi nimi HAProxy rozkládat zátěž. Algoritmů je několik, od prostého round-robin (který je výchozí), přes použití serveru s nejméně spojeními až k hashování zdrojové adresy (což je metoda, kdy uživatel bude stále připojen ke stejnému serveru, nedojde-li k výpadku). Také je možné přidat volbu check, která způsobí to, že HAProxy bude v pravidelných intervalech kontrolovat dostupnost jednotlivých serverů a v případě ztráty spojení na ně nebude posílat zátěž (a opět je zařadí do provozu, dojde-li k jejich „uzdravení“). Podrobnější nastavení je opět dohledatelné v manuálu.

Nemáme-li zatím žádný rozumný monitoring aplikačních serverů, může nám opět posloužit HAProxy a to hned dvakrát. Dobrý přehled o fungujících serverech nám poskytnou statistiky, které se dají zpřístupnit skrze speciální backend. Pro hlášení problémů pak můžeme využít toho, že HAProxy může poslat e-mail, pokud dojde k vyřazení serveru z backendu.

Na úplný závěr pak ještě dodám, že vše výše zmíněné je využitelné i v TCP módu a je možné takto balancovat i provoz na čtvrté síťové vrstvě.

Našli jste v článku chybu?