haproxy, czyli mieszania pakietami HTTP ciąg dalszy

Ostatnio pisałem o haproxy jako balanserze dla ruchu HTTP, dziś chciałbym uzupełnić poprzedni opis o coś co nazywane jest popularnie content switchingiem... czyli kierowanie ruchu HTTP na podstawie żądanego contentu/typu danych. Dla prostszego wyobrażenia sytuacji przygotowałem taki obrazek:

mamy jeden serwis w jednej domenie http://yoursite.pl/ i posiadamy tylko jeden adres IP, ale chcemy podawać content w zależności od żądanego pliku z różnych serwerów, apache sprawdzi się przy (.php .html), lighttpd lekki, szybki, zwinny, etc. doskonale poradzi sobie z obrazkami (.gif .jpg), a nginx'a chcemy użyć bo też jest modny, do podawania (.js .css), bez balancera w postaci haproxy który pracuje w 7 warstwie i potrafi analizować i wykonywać przeróżne akcje niczym ninja z filmów z dzieciństwa na nagłówkach HTTP byłoby to trochę bardziej skomplikowane. Zakładam, że mamy działające haproxy i pozostałe serwery httpd, jeśli tak to można troszkę pobawić się konfiguracją.

Zacznę od wyjaśnienia kilku pojęć:

  • frontend - zgodnie z dokumentacją, frontendem jest nasza usługa która przyjmuje połączenia od użytkowników i je proxy'uje dalej do backendów.
  • backend - to definicja serwerów które odbierają ruch od balancer'a i zwracają wyniki, w naszym przypadku backendami są serwery Apache, Lighttpd, Nginx.
  • acl - Access List, dzięki acl'kom możemy zmatchować dany request np. na podstawie src, dst, path_reg, path_end

Nasza konfiguracja haproxy będzie wyglądać następująco:

[email protected]:~# cat /etc/haproxy.cfg

global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
maxconn 4096
user haproxy
group haproxy

defaults
log global
mode http
option httplog
option dontlognull
retries 3
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
option httpclose

frontend frontend_xen
bind 192.168.1.220:80
option forwardfor

acl acl_apache path_end .php .html
acl acl_lighttpd path_end .gif .jpg
acl acl_nginx path_end .css .js

use_backend backend_xen2 if acl_apache
use_backend backend_xen3 if acl_lighttpd
use_backend backend_xen4 if acl_nginx
default_backend backend_xen2

backend backend_xen2
mode http
balance roundrobin
server xen2 192.168.1.241:80 check

backend backend_xen3
mode http
balance roundrobin
server xen3 192.168.1.242:80 check

backend backend_xen4
mode http
balance roundrobin
server xen4 192.168.1.125:80 check

Po starcie daemona przeprowadzamy mały test:

$ lwp-request -Sde http://xen7/test.php | grep Server
Server: Apache/2.2.8 (Ubuntu)

$ lwp-request -Sde http://xen7/test.css | grep Server
Server: nginx/0.5.33

$ lwp-request -Sde http://xen7/test.jpg | grep Server
Server: lighttpd/1.4.19

Kluczową rolę odegrały definicje frontendu oraz backendów, ale bez ACL nie moglibyśmy powiedzieć balancer'owi jaki ruch gdzie kierować. Definiujemy więc 3 ACLe (acl_apache, acl_lighttpd, acl_nginx) gdzie warunkiem jest rozszerzenie:

acl acl_apache path_end .php .html
acl acl_lighttpd path_end .gif .jpg
acl acl_nginx path_end .css .js

składnia:
acl nazwa_acla sposób_matchowania warunek

Kolejny bardzo ważny element konfiguracji to wskazanie balancerowi gdzie ma kierować ruch po dopasowaniu do konkretnej acl'ki:

use_backend backend_xen2 if acl_apache
use_backend backend_xen3 if acl_lighttpd
use_backend backend_xen4 if acl_nginx
default_backend backend_xen2

Jeśli request pasuje do ACL acl_apache to kieruj do backend_xen2, jeśli pasuje do acl_lighttpd to kieruj do backend_xen3, itd... jeśli nie dopasuje do żadnej acl kieruje do default'owego backendu backend_xen2.

Bardzo proste do wdrożenia i daje nam ogromne możliwości zarządzania streamem HTTP, operowanie na rozszerzeniach to ułamek możliwości tego load balancer'a, będę starał się co jakiś czas, opisywać inne/pozostałe ciekawe przykłady wykorzystania haproxy.

A jakie Wy widzicie zastosowanie dla tego mechanizmu?

  • Pytanie jest jeszcze czy jest sens łączenia tego w jakiś sposób z varnishem? Lub czy istnieją inne mechanizmy odciążania strony na jednej maszynie? Oprócz np. generowania po stronie aplikacji statycznej wersji strony z danymi które zmieniają się co kilka godzin (strona główna portalu z newsami np.) - chociaż to głównie odciążanie baz danych, które również wpływa na wydajność strony.
    Interesują mnie głównie rozwiązania z wykorzystaniem apache/ngnix oraz perl/php.

  • Łączenie haproxy z varnishem ma sens, jeśli chcesz dostawić kolejny serwer i rozkładać ruch na oba. Sam varnish pracując w trybie reverse proxy już Ci powinien znacznie odciążyć serwer HTTP, chociażby dlatego, że będziesz miał krótsze połączenia do serwera (varnish -> apache/nginx/lighttpd), a mniej sesji to mniej zużycia pamięci, dodatkowo varnish daje Ci to, że żądania do elementów statycznych mogą się zatrzymywać na nim, powinien je podawać z pamięci, do tego tak elementarne rzeczy jak poprawne nagłówki cacheujące, m. in. Expires, Cache-Control. Do odciążenia baz danych polecam Ci rozwiązanie memcache, lub memcachedb jeśli zależy Ci na tym by nie tracić danych po restarcie serwera/aplikacji. Możesz też spróbować zamontować kod z serwisem na partycji typu tmpfs (ramdisk), ale musisz pamiętać, że odmontowanie/restart spowoduje utratę tych danych, tak więc koniecznie, regularna synchronizacja. Statyczna wersja strony głównej serwisu to podstawa, do tego cache... cache... i jeszcze raz cache... dodatkowo możesz skrócić czas ładowania strony, poprzez wprowadzenie gzipowania, kolejnym elementem może być serwowanie elementów statycznych (obrazki, css, js) z innej domeny niż główna serwisu (chodzi o nie przesyłanie dużej ilości danych zawartych w nagłówkach np. w Cookies'ach).

    Zapoznaj się z narzędziami yslow (yahoo) http://developer.yahoo.com/yslow/ oraz page speed (google) http://code.google.com/intl/pl/speed/page-speed/ pozwolą Ci one zobaczyć na czym najwięcej przeglądarka spędza czasu, mając tą informację, możesz wtedy zastanawiać się co poprawić.

    Takich niby prostych rzeczy, ale podnoszących wydajność jest bardzo dużo, na pewno będziemy chcieli o tym pisać i pokazywać w jaki sposób można podnosić wydajność serwerów bardzo niskim kosztem. Wkrótce pojawi się trochę artykułów, właśnie w tej tematyce, zachęcam do częstego odwiedzania ;-)

  • Tomasz Kraus

    Jak najefektywniej chronić się przed DDoSami właśnie na takim np frontendzie haproxy? Czy lepiej np stosować taką ochronę gdzieś w backendach?

  • Ochrona przed DDoS'ami nie jest łatwa i powinna być realizowana możliwie na jak najniższym poziomie, czyli jeśli masz firewall przed balancerem, to już na firewallu, jeśli masz router przed fw, to na routerze, ew. jeśli masz taką możliwość to powinieneś blokować ruch już po stronie swojego dostawcy, tak by jak najmniej pakietów docierało do Ciebie. Jeśli masz małą infrastrukturę, to na jak najniższej warstwie systemu, czyli nie na aplikacji haproxy a np. na iptables. Nie ma potrzeby obciążać balancera zbędnym ruchem. Pamiętaj, że mówimy jedynie o niwelowaniu skutków DDoS'a, bo nawet jeśli wytniesz ruch na lokalnym firewallu to i tak ten ruch do niego dotrze i będzie w stanie skutecznie wysycić Ci łącze. Jeśli jest to DDoS na serwer WWW np. konkretną stronę (strona jest ciężka, długo się generuje, konsumując dużo zasobów systemu) to wtedy można pomyśleć o blokowaniu tego na haproxy (acl + block if), a dopiero jeśli nie jest to możliwe to na frontendzie np. z użyciem mod_evasie (http://www.varlog.pl/2010/03/odciazanie-apache-przez-mod_evasive/). Każdy DDoS jest inny i nie ma niestety jednej skutecznej recepty... musisz wybierać to co będzie dla Ciebie najlepsze.

  • Paweł Torbus

    Haproxy posiada dyrektywę "reqtarpit" która bardzo sprytnie radzi sobie z blokowaniem ataku http DDoS, otwiera połączenie na określony timeout które prowadzi donikąd, po czym wysyła error 500 :) przydatne bo potrafi w praktyce zablokować i co ciekawe spowolnić atak http DDoS.

  • Ok, nie przewiduje żadnej farmy więc nie chciałbym iść w stronę haproxy.
    Mam jeden serwer gdzie chciałbym postawić varnish/squid (właśnie mógłbyś porównać oba serwery? podobno squid lepszy - tak ŁK mówił, a zawsze było takie przekonanie że squid to kobyła). pod tym systemem cache mieć:
    1. nginx - do .js, .css, .xml, .html
    2. apache (php, perl) - do którego dolozyc mod_evasive i większość krytycznego kodu przepisać z memcached.

    Może jakiś artykuł o tym jak zrobić takie właśnie zestawienie z uwzględnieniem parametrów squida które są istotne do wydajnej pracy jako reverse proxy.

    Testowałem też haproxy,nginx,varnish->apache i napotkałem jeden problem (chyba kwestia konf. varnisha). Dostawałem z haproxy x-forwarded-for i w VCLu też ustawiłem przekazywanie tego dalej do apache (w apache mam moduł który traktuje x-frowarded-for jako clientIP).

    Jednak Apache dostawał kilka dobrych IP i zdarzały się takie 127.0.0.1.
    Przegrzebałem logi varnisha i on z haproxy dostawał jeden nagłówek x-forwarded-for z dobrym IP a następnie do backendu słał dwa nagłówki, ten drugi miał 127.0.0.1 więc się nadpisywał. Ale tak jak mówię to nie zawsze, ciężko było mi znaleźć zależność, jakieś pomysły czemu się tak działo?

  • Paweł Torbus

    Backend rozumiem ze chodzi o apache ? i piszesz o module który x-forwarded-for przepisywal na klient ip a czy czasem to nie jest mod_rpaf? bo jeśli tak w to konfiguracji mod_rpaf można podać tzw proxy host i on wtedy ignoruje wpisy z tablicy x-forwarded-for które z założenia zna :) i wpisuje tylko ten właściwy