wrk y siege son dos herramientas de línea de comandos para someter un servidor web a carga y medir cuántas peticiones atiende y con qué latencia. wrk genera mucha carga con pocos recursos y es ideal para medir el máximo rendimiento de un endpoint; siege simula usuarios que recorren varias URL, lo que se acerca más al tráfico real. En este tutorial instalarás ambas en Ubuntu 24.04, lanzarás pruebas contra tu servidor, usarás scripts Lua para probar una API y aprenderás a interpretar los resultados.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor objetivo con un servidor web o una aplicación HTTP en marcha (Nginx, Apache, una API...), accesible en your_server_ip o your_domain.
  • Una segunda máquina con Ubuntu 24.04 LTS que actuará como cliente de carga, por ejemplo un VPS de CubePath en la misma ubicación que el servidor. Así mides el servidor y no tu conexión doméstica.
  • Un usuario no root con privilegios sudo en el cliente.
  • Permiso para probar el servidor. Nunca lances pruebas de carga contra sistemas de terceros: para el destino es indistinguible de un ataque de denegación de servicio.

Paso 1: Instalar wrk y siege

Ambas herramientas están en el repositorio universe de Ubuntu 24.04. Instálalas en la máquina cliente:

sudo apt update
sudo apt install wrk siege

Comprueba las versiones instaladas:

wrk --version
siege --version
wrk 4.1.0 [epoll] Copyright (C) 2012 Will Glozer
...
SIEGE 4.0.7
...

Paso 2: Preparar el cliente de carga

Cada conexión abierta consume un descriptor de fichero. El límite por defecto de una sesión es 1024, que se queda corto en cuanto pruebas con cientos de conexiones. Súbelo para la sesión actual:

ulimit -n 65535
ulimit -n
65535

El cambio solo afecta a la terminal actual, que es justo lo que necesitas para las pruebas. Consulta también cuántos núcleos tiene el cliente, porque de ahí sale el número de hilos de wrk:

nproc

Paso 3: Lanzar la primera prueba con wrk

wrk usa tres parámetros principales:

  • -t: número de hilos. Usa como máximo el número de núcleos del cliente.
  • -c: conexiones HTTP abiertas en total, repartidas entre los hilos.
  • -d: duración de la prueba (30s, 2m...).

Lanza una prueba de 30 segundos con 4 hilos y 100 conexiones, pidiendo la distribución de latencias con --latency:

wrk -t4 -c100 -d30s --latency http://your_server_ip/
Running 30s test @ http://your_server_ip/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.21ms    2.03ms  48.10ms   88.12%
    Req/Sec     6.04k   512.33     7.21k    70.25%
  Latency Distribution
     50%    3.87ms
     75%    4.95ms
     90%    6.30ms
     99%   11.42ms
  721893 requests in 30.02s, 585.21MB read
Requests/sec:  24047.10
Transfer/sec:     19.49MB

Cómo leer el resultado:

  • Requests/sec: peticiones completadas por segundo. Es la cifra de rendimiento total.
  • Latency Distribution: percentiles de latencia. El p99 (99 %) indica cuánto espera el 1 % de peticiones más lentas y suele ser más útil que la media.
  • Stdev alta respecto a la media indica tiempos de respuesta irregulares (bloqueos, recolección de basura, disco lento).
  • Si aparecen líneas Socket errors o Non-2xx or 3xx responses, parte de las peticiones fallaron y el resultado no es válido como medida de rendimiento: revisa los logs del servidor antes de seguir.

Para enviar cabeceras, por ejemplo un token de autenticación, usa -H:

wrk -t4 -c100 -d30s -H "Authorization: Bearer your_token" http://your_server_ip/api/status

Paso 4: Probar una API con scripts Lua

wrk puede cargar un script Lua con -s para cambiar el método, el cuerpo o las rutas de cada petición. Crea un script que envíe un POST con JSON:

nano post.lua
wrk.method = "POST"
wrk.body   = '{"name": "test", "quantity": 1}'
wrk.headers["Content-Type"] = "application/json"

Ejecútalo contra el endpoint de tu API:

wrk -t2 -c50 -d30s --latency -s post.lua http://your_server_ip/api/items

Para repartir la carga entre varias rutas, define la función request, que wrk llama antes de cada petición. Crea paths.lua:

nano paths.lua
local paths = { "/", "/about", "/api/status" }
local i = 0

request = function()
  i = (i % #paths) + 1
  return wrk.format("GET", paths[i])
end

done = function(summary, latency, requests)
  io.write(string.format("Peticiones: %d, errores de estado: %d\n",
    summary.requests, summary.errors.status))
  io.write(string.format("p50: %.2f ms, p99: %.2f ms\n",
    latency:percentile(50) / 1000, latency:percentile(99) / 1000))
end

La función done se ejecuta al terminar y recibe las latencias en microsegundos, por eso se dividen entre 1000. Lanza la prueba:

wrk -t4 -c100 -d30s -s paths.lua http://your_server_ip

Al final de la salida habitual verás tus propias líneas:

Peticiones: 698412, errores de estado: 0
p50: 3.95 ms, p99: 12.10 ms

Paso 5: Simular usuarios con siege

siege funciona con usuarios concurrentes (-c) que repiten peticiones durante un tiempo (-t, con sufijo S, M o H). Lanza 25 usuarios durante un minuto:

siege -c 25 -t 1M http://your_server_ip/

La primera vez que lo ejecutes, siege crea su configuración de usuario en ~/.siege/siege.conf. Al terminar muestra un resumen:

Transactions:                  41250 hits
Availability:                 100.00 %
Elapsed time:                  59.87 secs
Data transferred:              33.41 MB
Response time:                  0.04 secs
Transaction rate:             688.99 trans/sec
Throughput:                     0.56 MB/sec
Concurrency:                   24.93
Successful transactions:       41250
Failed transactions:               0
Longest transaction:            0.31
Shortest transaction:           0.00

Los campos clave son Availability (porcentaje de peticiones correctas), Transaction rate (transacciones por segundo), Response time (tiempo medio de respuesta) y Longest transaction. Si Concurrency queda muy por debajo del número de usuarios, el servidor responde tan rápido que los usuarios pasan tiempo esperando; si se acerca, el servidor empieza a ser el cuello de botella.

Por defecto siege introduce una pausa aleatoria entre peticiones de cada usuario, lo que imita a una persona navegando. Para medir el máximo rendimiento sin pausas usa el modo benchmark -b:

siege -b -c 50 -t 30S http://your_server_ip/

Probar varias URL

La ventaja de siege es que puede recorrer una lista de URL, incluidas peticiones POST. Crea el fichero:

nano urls.txt
http://your_server_ip/
http://your_server_ip/about
http://your_server_ip/api/status
http://your_server_ip/api/items POST {"name": "test", "quantity": 1}

Ejecuta siege en modo internet (-i), que elige una URL al azar en cada petición, con una pausa de hasta 1 segundo (-d 1) entre peticiones de cada usuario:

siege -c 50 -t 2M -i -d 1 -f urls.txt --content-type "application/json"

Las cabeceras se añaden con -H, igual que en wrk:

siege -c 25 -t 1M -H "Authorization: Bearer your_token" http://your_server_ip/api/status

Paso 6: Aumentar la carga por fases

Una única prueba no dice cuánta carga aguanta el servidor. Lo útil es subir las conexiones por fases y ver dónde deja de crecer el rendimiento y empieza a dispararse la latencia. Este bucle ejecuta wrk con cargas crecientes y muestra solo las líneas relevantes:

for c in 10 50 100 200 400; do
  echo "== ${c} conexiones"
  wrk -t4 -c"${c}" -d30s --latency http://your_server_ip/ \
    | grep -E 'Requests/sec|^ +99%|Socket errors|Non-2xx'
done
== 10 conexiones
     99%    1.20ms
Requests/sec:   9120.44
== 50 conexiones
     99%    3.10ms
Requests/sec:  21875.02
== 100 conexiones
     99%   11.42ms
Requests/sec:  24047.10
== 200 conexiones
     99%   38.75ms
Requests/sec:  24310.66
== 400 conexiones
     99%  121.30ms
Requests/sec:  23980.18

En este ejemplo el rendimiento se estabiliza en unas 24 000 peticiones por segundo a partir de 100 conexiones, mientras la latencia p99 sigue subiendo. Ese es el punto de saturación: más conexiones solo generan cola.

Mientras corre la prueba, observa el servidor desde otra terminal para saber qué recurso se agota:

vmstat 1

Si la columna us + sy se acerca al 100 %, el límite es la CPU; si wa es alta, el disco; si ninguna lo es, busca límites de la aplicación (workers, pool de conexiones a la base de datos). Vigila también la CPU del cliente con top: si wrk ocupa todos sus núcleos, el cuello de botella es el cliente y necesitas una máquina más grande para medir.

Para comparar antes y después de un cambio (activar caché, ajustar workers de PHP-FPM, etc.), ejecuta la misma prueba al menos tres veces en cada estado, con el mismo cliente y a la misma hora, y guarda los resultados:

mkdir -p ~/benchmarks
wrk -t4 -c100 -d60s --latency http://your_server_ip/ | tee ~/benchmarks/antes-$(date +%F-%H%M).txt

wrk o siege: cuál usar

wrksiege
Modelo de cargaConexiones persistentes al máximo ritmoUsuarios con pausas opcionales
Rendimiento del clienteMuy alto, pocos recursosModerado
Varias URLCon script LuaFichero de URL nativo
Percentiles de latenciaSí (--latency)No, solo media y máximo
Uso típicoMáximo rendimiento de un endpointSimular navegación y disponibilidad

Usa wrk para encontrar el techo de rendimiento y comparar configuraciones, y siege para comprobar cómo se comporta el sitio con un número realista de usuarios navegando.

Solución de problemas

Socket errors: connect ... o timeout ... en wrk. El servidor rechaza o no atiende conexiones. Revisa el log de errores del servidor web, el límite de conexiones (worker_connections en Nginx) y la cola del kernel (net.core.somaxconn). Los timeout se cuentan cuando una petición supera 2 segundos; puedes cambiarlo con --timeout 5s.

too many open files. No has subido ulimit -n en la terminal donde lanzas la prueba (paso 2), o el servidor web tiene su propio límite de descriptores demasiado bajo.

siege no pasa de 255 usuarios. siege limita la concurrencia con la directiva limit de ~/.siege/siege.conf. Súbela si de verdad necesitas más usuarios, aunque para cargas altas wrk es más adecuado.

Resultados muy variables entre ejecuciones. Suele deberse a caché fría en la primera ejecución, a otros procesos en el cliente o a pruebas demasiado cortas. Haz una ejecución de calentamiento que descartes y usa duraciones de 60 segundos o más.

Conclusión

Ahora sabes medir el rendimiento de un servidor web con wrk, probar APIs con scripts Lua, simular usuarios con siege y localizar el punto de saturación subiendo la carga por fases. Como siguientes pasos, puedes perfilar el servidor durante la prueba con perf para ver qué funciones consumen la CPU, ajustar la configuración de Nginx o PHP-FPM y repetir las mismas pruebas para cuantificar la mejora.