Haproxy - mała rzecz, a cieszy
HAProxy to pakiet wolnego oprogramowania, który najczęściej pełni rolę reverse-proxy, zapewniając load-balancing i high-availability serwerów aplikacji.
Klienci (np. przeglądarki) nie łączą się bezpośrednio z serwerami aplikacji, lecz właśnie z reverse-proxy, które stosując dodatkowe reguły przekazuje żądania do serwerów aplikacji i odpowiedzi z powrotem do klienta.
Projekt jest aktywnie rozwijany od 2002 roku i coraz częściej wykorzystywany z powodu popularyzacji systemów rozproszonych, w szczególności architektur opartych o mikroserwisy. Używają go takie tuzy jak Digital Ocean, Github, Dropbox, Instagram, czy StackOverflow. Jest też komponentem platformy kontenerowej OpenShift.
Pakiet jest dostępny dla wszystkich popularnych dystrybucji Linuxa, a także w postaci obrazu Dockerowego.
Standardowe zastosowania HAProxy.
Zapewnienie Load Balancingu
Jedno z podstawowych zastosowań HAProxy to software’owy load-balancer. Mając kilka węzłów naszej aplikacji chcielibyśmy rozdzielać ruch pomiędzy nimi. W konfiguracji HAProxy deklarujemy obiekt zwany backendem, który reprezentuje klaster naszych serwerów aplikacji. Następnie deklarujemy obiekt zwany frontendem, na który będą kierowani klienci oraz reguły kierowania ruchu z frontendu do backendu:
frontend my-load-balancer
bind 172.19.0.1:81
default_backend my-application-servers
backend my-application-servers
balance roundrobin
server my-app-server-1 172.19.0.2:8080
server my-app-server-2 172.19.0.3:8080
server my-app-server-3 172.19.0.4:8080
Powyższa konfiguracja powoduje że połączenia na endpoint 172.19.0.1:81 będą przekierowywane na jeden z trzech podanych serwerów w sposób równomierny.
Zapewnienie High Availability
HAProxy potrafi monitorować stan serwerów zadeklarowanych w sekcji backend i zaprzestać kierowania ruchu na serwery, które przestały poprawnie funkcjonować. W konfiguracji serwera możemy określić m.in. jak często HAProxy ma sprawdzać status serwera, ile razy weryfikacja musi się nie udać, żeby serwer został uznany za dysfunkcyjny oraz ile razy po wykluczeniu serwera weryfikacja musi się powieść, żeby serwer znów został uznany za “zdrowy”. Przykładowo:
server my-app-server-1 172.19.0.2:8080 check inter 5s fall 3 downinter 1m raise 5
oznacza że:
- status serwera ma być sprawdzany co 5 sekund (inter 5s)
- serwer ma zostać wykluczony jeśli nie powiodą się 3 kolejne sprawdzenia (fall 3)
- wykluczony serwer ma być sprawdzany co minutę (downinter 1m)
- serwer ma zostać ponownie uznany za sprawny jeśli powiedzie się kolejne 5 sprawdzeń (raise 5)
TLS/SSL
Jeżeli chodzi o obsługę szyfrowanych połączeń TLS/SSL, HAProxy może działać w jednym z trybów:
- zwykłe proxy
- terminacja szyfrowania
- ponowne szyfrowanie ruchu
Zwykłe proxy
W tym trybie HAProxy zwyczajnie przekazuje strumień bajtów z frontendu do backendu, nie wnikając w to, że ruch jest szyfrowany.
Terminacja szyfrowania
W tym trybie HAProxy otrzymuje szyfrowany ruch z frontendu, odszyfrowuje go (z użyciem klucza prywatnego) i przekazuje do backendu w postaci niezaszyfrowanej. Przykładowa konfiguracja:
frontend my-terminating-load-balancer
bind 172.19.0.1:443 ssl crt /etc/ssl/certs/my-certs.pem
default_backend my-application-servers
backend my-application-servers
balance roundrobin
server my-app-server-1 172.19.0.2:8080
server my-app-server-2 172.19.0.3:8080
Ponowne szyfrowanie ruchu
W tym trybie HAProxy otrzymuje szyfrowany ruch z frontendu, odszyfrowuje go (z użyciem klucza prywatnego), a następnie szyfruje ponownie i przekazuje do backendu. Przykładowa konfiguracja:
frontend my-terminating-load-balancer
bind 172.19.0.1:443 ssl crt /etc/ssl/certs/my-certs.pem
default_backend my-secure-application-servers
backend my-secure-application-servers
balance roundrobin
server my-secure-app-server-1 172.19.0.2:8443 ssl
server my-secure-app-server-2 172.19.0.3:8443 ssl
W odróżnieniu od trybu zwykłego proxy, dzięki odszyfrowaniu HAProxy może manipulować żądaniami HTTP, które przekierowuje.
Session affinity
HAProxy pozwala na taką konfigurację, że requesty od danego użytkownika zawsze będą kierowane na ten sam serwer aplikacji. Taka potrzeba zachodzi m.in. kiedy używamy lokalnych sesji użytkownika, które istnieją tylko na tym serwerze aplikacji, na którym zostały utworzone. Przykładowa konfiguracja, która opiera się na prefiksie ciasteczka sesyjnego:
frontend my-sticky-load-balancer
bind 172.19.0.1:81
default_backend my-application-servers
backend my-application-servers
cookie JSESSIONID prefix nocache
server my-app-server-1 172.19.0.2:8080 cookie node1
server my-app-server-2 172.19.0.3:8080 cookie node2
Przy takiej konfiguracji wszystkie requesty, które niosą ciasteczko sesyjne z prefiksem “node1” będą kierowane na pierwszy serwer aplikacji.
ACL
HAProxy pozwala konfigurować reguły (ACL - Access Control List), które określają w jaki sposób poszczególne żądania mają być obsługiwane. Przykładowa konfiguracja:
frontend my-acl-load-balancer
bind 172.19.0.1:81
acl is-passive method GET
use_backend my-passive-application-server if is-passive
default_backend my-active-application-server
backend my-passive-application-server
server my-passive-app-server-1 172.19.0.2:8080
backend my-active-application-server
server my-active-app-server-1 172.19.0.3:8080
Przy takiej konfiguracji żądania z metodą GET będą kierowane na pasywny serwer aplikacji, a wszystkie pozostałe na aktywny serwer aplikacji.
Zastosowania w warsztacie programistycznym
Poza klasycznym użyciem jako reverse-proxy zapewniające load-balancing i high-availability, HAProxy może być z powodzeniem wykorzystane w warsztacie programistycznym. Przedstawię poniżej kilka zastosowań z własnego doświadczenia.
Routing, który łatwo zmienić
Rozwijając aplikację, która wywołuje serwisy z innych aplikacji, chcemy niekiedy móc przełączać się między różnymi adresami tych zewnętrznych usług, np. możemy chcieć sprawnie przełączać się między prawdziwymi aplikacjami oraz ich zmockowanymi wersjami. Często restart naszej aplikacji jest długotrwały, a zmiana namiarów na zewnętrzne usługi wymaga edycji więcej niż jednego pliku. W takim przypadku HAProxy może nam posłużyć jako wygodna “centralka”, w której będziemy się sprawnie przełączać między różnymi wersjami zewnętrznych usług. Konfigurując HAProxy w ten sposób:
frontend my-forward-proxy
bind 172.19.0.1:81
default_backend my-external-services
backend my-external-services
server my-external-server-1 172.19.0.2:8080
backend my-mocked-services
server my-mocked-server-1 172.19.0.3:8080
i ustawiając w naszej aplikacji adres zewnętrznych usług na 172.19.0.1:81, możemy bez restartu aplikacji zmieniać jej zewnętrzne zależności przez zmianę wartości “default_backend” i szybki “reload” HAProxy.
Podglądanie TLS/SSL
Jeśli serwisy w naszym systemie przesyłają sobie dane przez szyfrowane połączenia TLS/SSL, może to stanowić przeszkodę w debugowaniu komunikacji przy pomocy analizatorów ruchu sieciowego, takich jak tcpdump, czy Wireshark. Możemy jednak pomiędzy serwisami ustawić pośrednika w postaci HAProxy, które będzie terminowało szyfrowane połączenie i umożliwiało nam podglądanie ruchu sieciowego.
Opóźnienia requestów
Podczas rozwijania aplikacji zachodzi czasem potrzeba przetestowania jak zachowa się ona w przypadku dłuższego niż oczekiwane przetwarzania w wywoływanych usługach zewnętrznych. Jeśli pośrednikiem w dostępie do zewnętrznych usług jest HAProxy, to mamy możliwość zasymulowania takiego opóźnienia. HAProxy samo w sobie nie posiada takiej funkcjonalności, ale pozwala na rozszerzanie możliwości za pomocą skryptów pisanych w języku Lua. Przykładowy skrypt dodający opóźnienie:
function delay_request(txn)
core.msleep(15000)
end
core.register_action("delay_request", { "http-req" }, delay_request);
Załadowanie skryptu do HAProxy:
lua-load /etc/haproxy/delay.lua
i użycie w konfiguracji:
frontend my-delay-frontend
bind 172.19.0.1:81
mode http
http-request lua.delay_request
default_backend my-delay-backend
backend my-delay-backend
server my-external-server-1 172.19.0.2:8080
Prosty serwer statycznej zawartości
W dość przewrotny sposób możemy wykorzystać HAProxy jako prosty serwer statycznych plików. Dla każdego backendu możemy skonfigurować pliki, które mają zostać zaserwowane w przypadku określonych statusów HTTP. Z drugiej strony, w przypadku gdy w konfiguracji backendu nie zdefiniowano żadnego serwera, HAProxy generuje status HTTP 503. Łącząc te dwa fakty możemy skonfigurować frontend, który dla określonych requestów będzie zwracał statyczny plik:
frontend my-frontend
bind 172.19.0.1:81
mode http
use_backend my-backend-static if { path_end /index.html }
default_backend my-backend-services
backend my-backend-static
mode http
errorfile 503 /etc/haproxy/index.html
backend my-backend-services
server my-external-server-1 172.19.0.2:8080
Podłączanie się do sesji
W środowisku deweloperskim zdarza się, że chcielibyśmy podłączyć się przeglądarką do istniejącej na serwerze sesji użytkownika (znając oczywiście identyfikator/token sesji). Nowoczesne desktopowe przeglądarki zazwyczaj pozwalają nam ręcznie dodać ciasteczko sesyjne do zbioru ciasteczek danej witryny. Co jednak, jeśli musimy obsłużyć również te mniej lubiane przeglądarki albo wersje mobilne? Tu z pomocą również może przyjść nam HAProxy:
frontend my-setcookie-frontend
bind 172.19.0.1:81
mode http
default_backend my-setcookie-backend
backend my-setcookie-backend
mode http
http-request redirect location http://172.19.0.1:80/\r\nSet-Cookie:\ JSESSIONID=%[urlp(session_id)] code 302
Przy takiej konfiguracji, wejście z dowolnej przeglądarki na adres http://172.19.0.1:81/?session_id=123456 spowoduje przekierowanie na http://172.19.0.1:80 z ustawionym już ciasteczkiem sesyjnym (wykorzystujemy tu fakt że ciasteczka ustawione dla domeny nie uwzględniają portów).
Podsumowanie
Jak widać, HAProxy ma wiele klasycznych i mniej klasycznych zastosowań.
Jego niewątpliwym atutem jest stosunkowo prosta konfiguracja i runtime’owa wydajność oraz “lekkość” (zaczytanie zmienionej konfiguracji odbywa się zwykle w ułamku sekundy).
Jest to narzędzie, które polecam do toolboxa każdego architekta systemów rozproszonych, a także do warsztatu deweloperskiego zwinnego programisty.
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA
newsletter
techniczny
Podobne wpisy
Czy wiesz, że w Angular 17 została wprowadzona alternatywa dla *ngIf?
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA