Seguidament us explicaré com es pot montar un balancejador httpS molt econòmic i molt eficient, el qual s’ha implementat tan en entorns productius com no productius, la motivació inicial del qual va ser centralitzar el balanceig dels diferents entorns d’integració continua d’una aplicació web. En entorns productius també l’he implementat després de fer varies probes de rendiment amb el “ab” i el “siege” .
L’únic requeriment que hem d’asumir és allotjar tot tràfic dels diferents entorns en subdominis d’un domini (p.e.example.com), on podem tenir int01.example.com, int02.example.com, test01.example.com, test02.example.com, pre.example.com, etc..
Anàlogament, en els entorns de producció, podem fer la mateixa discriminació diferenciant el tràfic pels paisos(es.example.com,fr.example.com), plataformes(m.example.com), tipus de contingut(static.example.com).
Els objectius que em vaig marcar van ser:
Els dimonis que utilitzarem serà Nginx, Haproxy, Pacemaker i Corosync, tot programari lliure on abarata el cost considerablement.
En el següent esquema es veu clarament l’arquitectura de tot el montatge.
El comento breument, no entraré en detall en tot, em centro en lo important d’aquest post. Els navegadors dels usuaris fan les peticions a les empreses que fan de CDN, després aquestes peticions van a parar en la nostra plataforma on filtrarem el tràfic amb un firewall. Un cop les peticions són filtrades van a parar un nginx. La finalitat del nginx es discriminar el tràfic segons el entorn i centralitzar totes les negociacions SSL del tràfic HTTPS. Seguidament, les peticions passen a un haproxy on aquest balanceja entre els diferents frontals de cadascun dels entorns.
Per tal de securitzar-ho tot, definirem 3 VLAN’s, dmz, frontend i backend. Tot el tràfic és filtrat pel firewall, el tràfic sortint, el entrant i el tràfic entre zones. En la DMZ posarem 2 maquines virtuals CentOs on tindrem els serveis de Nginx i Haproxy en actiu-passiu utlitzant pacemaker-corosync. En el frontend tenim els frontals de cadascun dels entorns i en el backend tenim les dades, mysql, cassandras, mongos, nfs, cifs, etc….
Per exemplificar-ho millor, ens centrarem només en un entorn, int01.example.com.
Primerament en la DMZ montarem una 2 màquines virtuals on hi tindrem 2 ip flotants, una per el nginx i l’altre pel haproxy. Jo ho he implemetat en una infraestructura virtualitzada vmware però també es poden montar solucions més econòmiques com XenServer, KVM, etc… Evidentment, sigui quina sigui la solució de virtualització, hem d’assegurar-nos que les màquines virtuales sempre estàn corrent en màquines físiques diferents.
Les peticions entraran en la ip flotant del nginx, el nginx té dues funcions, centralitzar la negociació SSL i discriminar el tràfic per entorns. En el tràfic HTTPS afegirem una capçalera HTTP “X-forwarded-proto: https”, on l’apache la recollirà i activarà la variable HTTPS emmascarant-li a la aplicació. El tipus de certificat que afegirem en el nginx serà un certificat signat per una entitat certificadora oficial on el Common-Name que utilitzarem serà de tipus wildcard, es a dir, per aquest exemple, “*.example.com”, això ens facilitza molt la gestió del certificats i ens dona molta facilitat de gestió. En entorns de producció ho dimensionem assignant els valors correctes de worker_processes i worker_connections tenint en compte la formula: max clients = worker_processes * worker_connections/4.
Seguidament les peticions passen al haproxy on aquest balanceja entre els diferents frontals de cadascun del entorn. El haproxy ens permet estriar l’algorisme de balanceig, jo acostumo a utilitzar Round Robin. A més, podem prefixar els rangs de IPs dels frontals que hi hauran en cadascun dels entorns, així podem desplegar més frontals en el cas que sigui necessari sense haber de reinicilitzar el dimoni.
Haproxy fara una petició a un php que hi ha en cadascun dels frontals on retorna un “OK” si no està swapejant, no té molta càrrega i té conectivitat en les bases de dades. Cal remarcar que haproxy també ens permet realitzar balancejos “sticky” tan en aplicacions JAVA com PHP, depenentment d’una capçalara HTTP, habitualment les capçaleres JSESSIONID i PHPSESSID respectivament. (en apps ASP teniem ASPSESSIONID). En entorns de producció fixarem la variable maxconn tenim en compte que cada petició consumeix 17kb.
Finalment les peticions arriben als frontals on primerament passen pel varnish i les peticions que no siguin cachejables arribaran finalment al apache, crec que aquests dos dimonis es mereixen un post especial per ells dos, que ja tinc en el backlog del blog.
En el següent diagrama clarifica tota l’arquitectura:
Seguidament posaré les configuracions més importants, Nginx, Haproxy, Pacemaker:
Parametres del Nginx
.... upstream http-example-int01 { server lb2-vip.example.com:8080; keepalive 16; } server { listen lb1-vip.example.com:80; server_name int01.example.com ~^.*-int01\.example\.com$; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_pass http://http-example-int01/; proxy_redirect off; } } server { listen lb1-vip.example.com:443 ssl; server_name int01.example.com ~^.*-int01\.example\.com$; ssl on; ssl_certificate /etc/nginx/ssl/crt/concat.pem; ssl_certificate_key /etc/nginx/ssl/key/example.key; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header Host $host; proxy_pass http://http-example-int01/; proxy_redirect off; } } ....
Parametres del Haproxy
... frontend example-int01 lb2-vip.grpprod.com:8080 default_backend example-int01 backend example-int01 option forwardfor option httpchk GET /healthcheck.php http-check expect string OK server web01 x.y.z.w:80 check inter 2000 fall 3 server web02 x.y.z.w:80 check inter 2000 fall 3 server web03 x.y.z.w:80 check inter 2000 fall 3 server web04 x.y.z.w:80 check inter 2000 fall 3 server web05 x.y.z.w:80 check inter 2000 fall 3 ...
Parametres del Apache
ServerName int01.example.com DocumentRoot "/srv/www/example/fa-front/public" <Directory "/srv/www/example/fa-front/public"> Options -Indexes FollowSymLinks AllowOverride None Allow from All Order Allow,Deny RewriteEngine On RewriteCond %{HTTP:X-Forwarded-Proto} https RewriteRule .* - [E=HTTPS:on] RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ index.php [NC,L] SetEnv APPLICATION_ENV int01 DirectoryIndex index.php LogFormat "%v %{Host}i %h %l %u %t \"%r\" %>s %b %{User-agent}i" marc.int01 CustomLog /var/log/httpd/cloud-example-front.log example
Parametres del Pacemaker
node balance01 node balance02 primitive nginx lsb:nginx \ op monitor interval="1s" \ meta target-role="Started primitive haproxy lsb:haproxy \ op monitor interval="1s" \ meta target-role="Started" primitive lb1-vip ocf:heartbeat:IPaddr2 \ params ip="x.x.x.x" iflabel="nginx-vip" cidr_netmask="32" \ op monitor interval="1s" primitive lb2-vip ocf:heartbeat:IPaddr2 \ params ip="y.y.y.y" iflabel="haproxy-vip" cidr_netmask="32" \ op monitor interval="1s" group haproxy_cluster lb2-vip haproxy \ meta target-role="Started" group nginx_cluster lb1-vip nginx \ meta target-role="Started" property $id="cib-bootstrap-options" \ dc-version="1.1.7-6.el6-148fccfd5985c5590cc601123c6c16e966b85d14" \ cluster-infrastructure="openais" \ expected-quorum-votes="2" \ stonith-enabled="false" \ last-lrm-refresh="1355137974" \ no-quorum-policy="ignore" rsc_defaults $id="rsc-options" \ resource-stickiness="100"