Archivo de la etiqueta: iptables

Consejos para pruebas de carga de servidores y proxies web

[spanish]Una de las tecnologías con las que me pego en el trabajo son los proxies web. En mi anterior trabajo, eran los servidores y aplicaciones web. En ambos casos antes de meter nada en producción (o antes de hacer una actualización de lo que ya hay) es una buena idea hacer pruebas de carga para conocer los límites de la tecnología y si hay que añadir servidores. Para hacer estas pruebas hay soluciones comerciales como el Avalanche de Spirent, pero con un poco de trabajo y software libre se puede montar un sistema igual de bueno.

En primer lugar y antes de comenzar cualquier test, hace falta una herramienta para tomar medidas. No vale simplemente meterle carga al Apache y pegarle un vistazo a los logs, free, ps y el server-status, es mucha información en bruto como para tratarla a mano. Una herramienta que a mi me ha resultado extremadamente útil, tanto para analizar el comportamiento de un sistema durante una prueba de carga como para la monitorización del día a día en operaciones es el Cacti.

Cacti es una aplicación LAMP que ejecuta una serie de sondas (programables) en los diferentes sistemas a monitorizar, y hace gráficas con los resultados, gráficas de distintos periodos de tiempo: las últimas horas, los últimos días, meses, etc. de forma que tienes una visión histórica de la evolución del servicio. La información que proporciona esta visión histórica es crítica, al hacer una prueba de carga normalmente no quieres sólo saber cuán bien funciona el sistema con la última versión del software, si no cómo funciona en comparación con la versión anterior. O si estás monitorizando un sistema en producción, quieres analizar la diferencia entre el pico de tráfico web que hay un lunes por la mañana frente a la calma de un domingo por la tarde. Ejecutar un test y tomar cuatro medidas puntuales no vale de nada si no se pone en contexto y perspectiva con los resultados de tests anteriores o los valores que haya en producción. Y como Cacti te permite desarrollar tus propias sondas en el lenguaje de programación que quieras, no hay nada que pueda ser medido (uso de CPU o memoria, latencia, carga de I/O, conexiones concurrentes, etc.) de lo que no se pueda hacer un gráfico. De hecho lo más normal es que vayas añadiendo más y más gráfico conforme vayas haciendo más pruebas y detectando más posibles puntos de fallo y variables a monitorizar. Detectar regresiones de rendimiento, memory leaks, etc. es muy fácil (de hecho, muy visual) con Cacti.

Una vez que ya tenemos nuestra herramienta para tomar mediciones, podemos empezar con las pruebas. Si el sistema a probar es un servidor web, sólo hace falta un programa que simule varios clientes; si es un proxy, tanto eso como un servidor web al que atacar. La parte del servidor es fácil, se puede usar Apache o (incluso mejor) lighttpdnginx. En este caso lo que queremos es estresar el proxy, no el servidor web que tiene que haber detrás, así que queremos un servidor web tan rápido como sea posible. Se debería pensar incluso en poner la «nube» (el juego de páginas html que van a pedir los clientes) en un disco ram y en desactivar el access.log del servidor, para minimizar los accesos a disco y así aumentar el rendimiento. ¡Hay que asegurarse de que el servidor web no se convierte en un cuello de botella, de lo contrario no estaríamos estresando al proxy!

Respecto al software para simular clientes, hay varias opciones: desarrollar uno desde cero, hacerlo a base de scripts y wget o curl, o usar alguno de los programas que ya hay a tal efecto como curl-loader, Apache’s JMeter o cualquier otro. Busca en Google.

Independientemente de qué software uses para simular los clientes, hay que tener una cosa en cuenta: estos programas generalmente van a generar una carga muy alta pero bajo condiciones ideales. Es decir: todo el sistema (cliente-proxy-servidor) va a estar en una red local, probablemente con velocidades de Giga, sin pérdida de paquetes, retransmisiones, paquetes fuera de secuencia, etc. Eso no es lo que te vas a encontrar «ahí fuera» cuando pases el sistema a producción. Según lo que quieras probar, si el límite máximo teórico de carga o cuán bien el nuevo desarrollo va a funcionar con tráfico real, deberás adaptar el tráfico a ese caso.

Consejos útiles a la hora de cacharrear con el tráfico:

  • Si tu simulador de clientes te lo permite, ¡no lo ejecutes a tope de velocidad! trickle puede ser útil en estos casos. Como mínimo haz dos pruebas: una sin límite de velocidad y otra p.ej. a 3mbps por cliente para simular usuarios con una línea ADSL. Ten en cuenta que esto es sólo un límite de velocidad, todavía conseguiremos meterle bastante carga al servidor porque a menos velocidad, probablemente acabe teniendo más conexiones concurrentes (¿bloqueantes?). He visto programas aguantar como unos campeones cargas altísimas en condiciones ideales y petar en cuestión de segundos en producción con tráfico real… y lento.
  • Pierde paquetes. Reordena paquetes. Mete un router/bridge linux entre el cliente y el equipo a probar y juega con el sistema tc de Linux. Pégale un vistazo a estoestoestoesto.
  • Simula varias IPs cliente distintas. Elige toda una clase B o C, asegúrate de configurar bien las rutas en todos los sistemas para que sean coherentes, y ejecuta algo como esto en el cliente (o el router/bridge del punto anterior). Es un poco sucio pero funciona:
    while :
    do
      I=$((1+RANDOM%254))
      J=$((1+RANDOM%254))
      /sbin/iptables -t nat -F
      /sbin/iptables -t nat -A POSTROUTING -d 1.1.1.1 -o eth1 -j SNAT --to-source 10.10.$I.$J
      sleep 0.1s
    done

Con todo esto, deberías ser capaz de montar una infraestructura de pruebas bastante decente.[/spanish]

[english]At work, one of the technologies I work with are web proxies. On my previous work, web servers and web applications. On both cases before going into production (or before rolling an update) it’s wise doing some stress-testing, trying to assess the limits of the technology or the need for more servers. There are commercial solutions available, like e.g. Spirent‘s Avalanche, but with some work you can roll out your own stress-testing infrastructure using free software.

On the first place, before beginning with the test, you need a measuring tool. Just throwing some load to Apache and then taking a look at the logs, free, ps, and server-status by hand is plain crazy, too much raw information to deal with. One tool I’ve found extremely useful, both for analyzing stress-tests results and for everyday operations monitoring is Cacti.

Cacti is a LAMP-based software which runs a series of (programmable) probes on your systems and makes graphs out of them, graphs of different time periods: the last hours, last days, months, etc. so that you have an historic view of the evolution of the service. Having this historic information is critical, when doing these kind of tests you not only want to know how well the latest release works but how does it compare to the previous one. Or in operations, you want to know the difference between the web traffic spike on a Monday morning compared to the calm of the Sunday afternoon. Just running a test and taking a measure in that moment is not enough, you need to put it in perspective with your previous tests and what you have in your production environment. And you can develop your own probes in whatever programming language you want, so there’s nothing that can be measured (CPU or memory usage, latency, I/O load, concurrent connections, etc.) that you can’t get on the graphs. You’ll add more and more probes and graphs as you go testing and discovering new variables that can affect system performance. Detecting regressions, memory leaks, etc. is very easy (well… visual) when using Cacti.

After having some measuring tool in place, we can start stress testing the systems. If you’re dealing with a web server, you only need a client-simulation program; if a proxy, both that and a server. The server part is easy, just use Apache or (even better) lighttpd or nginx. You want to stress test your proxy application, not the web server behind it, so you want a web server as fast as possible. Even think of putting your cloud (the set of web pages you’re going to test with) in a ramdisk and disabling the server’s access.log. Make sure that the web server doesn’t become a bottleneck or you won’t be really stressing the proxy!

As for the client-simulation software, you have several options: develop your own from the ground up, develop a series of scripts with wget or curl, or use some of the existent solutions available out there, like curl-loader, Apache’s JMeter. or may others. Just Google them.

No matter which software you use for the test, one consideration: These programs usually generate a very high load but under ideal conditions. I mean: you’re running them on a local network, likely with Gbps speeds, without packet loss or rearrangements … That’s not what you’ll find «out there». Depending on what you want to test, either the theoretical top performance or how a new development would cope with real traffic, you need to adapt the traffic of your test to that.

Things you’ll find useful for tweaking your traffic:

  • If your client-simulation software allows you, don’t run it at full network speed! trickle may come in handy here. At least run two tests: one without speed limit, and another with e.g. a 3mbps limit simulating a DSL line. Bear in mind that this is just a speed limit, the server will still have a high load because at a lesser speed it’ll likely have to cope with more concurrent (blocking?) connections. I’ve seen some software do great on a high speed laboratory test and choke in seconds with slow, real-world traffic.
  • Lose packets. Rearrange them. Insert a router/bridge linux server between your client and your test object and use Linux’s tc system. Take a look here, here, here and here.
  • Simulate multiple origin IPs. Take a whole B or C class network, make sure the routes in all your systems are coherent, and run something like this to randomize the clients’ IP addresses (a bit hacky, but it works):
    while :
    do
      I=$((1+RANDOM%254))
      J=$((1+RANDOM%254))
      /sbin/iptables -t nat -F
      /sbin/iptables -t nat -A POSTROUTING -d 1.1.1.1 -o eth1 -j SNAT --to-source 10.10.$I.$J
      sleep 0.1s
    done

With all this in place, you should have a pretty decent testing framework going.[/english]

¿Por qué interfaz de red sale el tráfico UDP?

[spanish]

Sobre el post anterior…

Me ha pasado ya más de una vez que cuando un servidor tiene más de un interfaz de red (sean físicos o virtuales) y tiene algún servicio sobre UDP, aunque reciba tráfico UDP por todos los interfaces, cuando contesta siempre lo hace por el interfaz que da a su gateway, con la IP de ese interfaz.

Es decir, simplificando imaginaos que tenemos algo más o menos así:

esquemaredudp.png

si recibimos un paquete por el interfaz de la izq., se responde por ese mismo interfaz con IPa. Pero si lo recibimos por el de la drcha., también se responde por la izq. con IPa. ¿Y qué pasa cuando el cliente recibe la respuesta correcta pero desde una IP que no tiene nada que ver con la del servidor al que le ha preguntado? ¿Con un direccionamiento completamente distinto? ¿Y si, además, entre cliente y servidor hay algún firewall haciendo NAT?

Esto por supuesto desbarata el servicio. En el caso que comentaba ayer era una VPN L2TP, y ni siquiera se conseguía establecer la conexión. En otras ocasiones me ha pasado esto con un servidor de DNS, y según el sistema operativo del cliente aceptaba sin más la respuesta aunque viniera desde una IP incorrecta, no lo hacía y presentaba una alerta de seguridad.

Supongo que esto en parte es culpa de cada implementación en concreto, digo yo que en programación se podrá ver a qué IP iba dirigido el paquete que se acaba de recibir y la respuesta enviarla por el interfaz y con la IP que toca, pero por lo visto o esto no es así o no siempre se tiene en cuenta.

Ayer con ayuda de un compañero que controla de iptables y tal lo solucionamos así:

  • con iptables, detectar el tráfico que queremos «redirigir» por otro interfaz y marcarlo
  • en función de esa marca, definir una tabla de rutas con «ip rule/route» y encaminarlo a un GW en el otro extremo
  • de nuevo con iptables y aprovechando la marca anterior, hacer NAT de la dirección de origen

Un ejemplo para redirigir tráfico UDP en función del puerto $PORT con la IP $IPb a través de la puerta de enlace $GWb sería:

[/spanish]
[english]

It has happened to me a cuple of times when dealing with UDP-based services that, when a server has more than one network interface (either physical or virtual), all the UDP traffic goes out through the interface on the default gateway’s network segment and with that interface’s IP address, even when the original request came through the other interface and was directed to the other IP address.

Graphically, say you have something similar to this:

esquemaredudp.png

If the server receives a request on IPa, the response goes out through that same interface and with origin IPa. But if the request arrives on IPb through the interface on the right, the response is also sent through the left interface with IPa. And what happens when the client receives a response from an incorrect IP address? Maybe even from a completely different network segment? And if there’s a fw in between doing NAT?

Of course this breaks the service. This week I’ve had this very problem setting up a L2TP VPN, and it was impossible to establish the tunnel. On some other ocasions I’ve had a similar problem with a DNS server, and the outcome depended on the client’s operating system: some OSes accepted the DNS response even when it came from a different address than that of the server originally queried; others would reject it and even raise a security alert.

I guess that this behaviour can be programmatically controlled. I mean, when you receive a packet you can check the IP address it was sent to, and craft the response so that it gets sent with that same address from the right interface. But it seems that this is seldom done.

Yesterday I got around this issue with the help of iptables and a coworker more knowledgeable than me on routing issues:

  • with iptables, you can detect the traffic to «redirect» and mark it
  • depending on this mark and using «ip rule/route», have a special routing table that sends this traffic to the proper GW/through the right interface.
  • with iptables again and using the previous mark, do a SNAT on the origin IP address

An example for redirecting all UDP traffic from a certain $PORT using IP address $IPb through gateway $GWb would be:

[/english]


echo 255 local > /etc/iproute2/rt_tables
echo 254 main >> /etc/iproute2/rt_tables
echo 253 default >> /etc/iproute2/rt_tables
echo 0 unspec >> /etc/iproute2/rt_tables
echo 200 udp >> /etc/iproute2/rt_tables
ip rule add fwmark 1 table udp
ip route add default via $GWb dev eth0 table l2tp
iptables -t mangle -A OUTPUT -p udp -m udp --sport $PORT -j MARK --set-mark 0x1
iptables -t nat -A POSTROUTING -m mark --mark 0x1 -j SNAT --to-source $IPb