Configuracion de HAproxy para socket.io y apache/nginx

1 dic 2012 by shadow_of__soul, No Comments »

Ultimamente me encontre en la necesidad de crear una infraestructura para una aplicacion en la que estoy trabajando, que involucra varias tecnologias, entre ellas, socket.io y un web server (en mi entorno de desarrollo era apache, en el de produccion, era nginx), mas que nada por que debia mantener un esquema preciso de accesos y ademas, podes asegurar escalabilidad (ya se que existe el cloud, amazon, etc.. pero yo sigo usando dedicados comunes y corrientes :P ). Voy a pasarles una config muy util, que me tomo unos dias construir (mas que nada por mi 0 experiencia en HAproxy y que sinceramente, no soy un sysadmin, aunque me las arreglo ;) .

Que es HAproxy?

HAproxy esquema

HAproxy es un load balancer , un servidor que se pone primero antes de todo los demas, y recibe las peticiones de lo usuarios y las redirige a los servidores que van a procesarlas (que pueden ser cualquier cosa, desde un web server, a una aplicacion custom (como una con nodejs), un servidor de bases de datos etc..). Normalmente se lo pone a recibir peticiones en la interfaz de red (osea, internet) al puerto 80, donde nuestros navegadores envian las peticiones por ejemplo, al requerir una pagina web. Una vez recibida la peticion, redirige la misma a los servidores que nosotros especifiquemos en la configuracion, que usualmente en una red local/intranet.

Problema y solucion

En mi caso particular, requeria de poder soportar la escalabilidad de conexiones y respuesta a pedidos de websockets (uno de los mejores protocolos de conexion persistente hoy dia en la web y soportado por socket.io para nodejs), plus, poder servir contenido estatico desde la misma url. Nginx tiene funciones de proxy, con lo que podria realizar las 2 funciones, la de servir contenido estatico y la de load balancer, pero desgraciadamente al dia de hoy, no soporta websocket. HAproxy, se plantea como una solucion ideal para esto, es mas, en la wiki de socket.io pueden encontrar una configuracion de ejemplo, con la cual, *en teoria* podrias estar redireccionando la conexion de websockets a la app escuchando a un puerto especifico y listo. El problema recae, en que debemos filtrar por dominio, y ademas, poder servir contenido estatico desde la misma url, con la configuracion de ejemplo y con diferentes pruebas, me di cuenta que versiones estables de HAproxy esto no es posible, por que hay opciones necesarias como http-server-close que son necesarias para que la app de socket.io pueda enviarle al cliente el cliente de socket.io y cierre la sesion al terminar y puedan los nuevos pedidos del cliente (que pueden ser contenido estatico, que debe ser servidor por nginx) sea ruteado correctamente.

Despues de varias consultas a la lista de mail de HAproxy, me apuntaron al siguiente articulo donde explica de manera detallada como HAproxy maneja las conexiones de websockets. En versiones estables del mismo (1.4) faltan opciones como option tunnel fundamental para que todo esto funcione de manera armoniosa (cualquier version de HAproxy 1.5-dev10 es compatible con esta configuracion, en mi caso, use la 1.5-dev15). Aqui pueden ver como me quedo la configuracion final:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
#log loghost local0 info
maxconn 4096
#chroot /usr/share/haproxy
user haproxy
group haproxy
daemon
#debug
#quiet

defaults
log global
mode http
option httplog
option http-server-close
option dontlognull
option redispatch
option contstats
retries 3
backlog 10000
timeout client 25s
timeout connect 5s
timeout server 25s
timeout tunnel 3600s
timeout http-keep-alive 1s
timeout http-request 15s
timeout queue 30s
timeout tarpit 60s
option forwardfor

frontend all 0.0.0.0:80
timeout client 5000

#por default, siempre se llama al backend del webserver

default_backend www_backend

#chequeamos que la url tenga socket.io en algun lado para redirigir a la app en ese caso

acl is_soio url_dir -i socket.io

#chequeamos que el request sea para un dominio determinado

acl is_mydom hdr_dom(host) -i mydomain

#si el request es para "mydomain" y es para socket.io, lo redireccionamos al backend correspondiente

use_backend mydom_backend_ws if is_soio is_mydom

#webserver backend

backend www_backend
balance roundrobin
server server1 localhost:6060 weight 1 maxconn 1024 check

#socket.io backend

backend mydom_backend_ws

balance roundrobin

server server1 localhost:5558 weight 1 maxconn 1024 cookie server1 check

En el link original, van a encontrar que el ACL se hace chequeando las headers por indicios de un pedido de conexion a un websocket, como en mi caso no solo soporto websockets. sino todas las conexiones que pueda hacer socket.io en el caso de que el cliente no pueda usar websockets (como xhr-polling, flashsockets, etc..) preferi checkear por “socket.io” en la url del request y redireccionar todo eso a la app si es que cumple el dominio. De esa manera se pueden tener varias aplicaciones con socket.io corriendo en la misma maquina o nodo, usando solo 1 load balancer y al mismo tiempo, servir contenido estatico desde la misma url. Probe esta config con todos los protocolos, y funciono, aunque en mi caso, flashsocket no anduvo, pero *creo* que es un problema de mi version de flash en linux.

Sin mas, espero que les sea util, les ahorre mucho tiempo en crear su propia infraestructura y cualquier duda, pueden acercarse al foro a plantear sus consultas :)

Etiquetas: , , , , ,

Sigueme !

Follow Me! Follow Me! Follow Me!