La mayoría de los problemas de rendimiento en el almacenamiento de objetos no son problemas de hardware. Los discos son rápidos, las tarjetas de red son capaces y las CPUs están en gran medida ociosas. El cuello de botella es casi siempre la configuración predeterminada del kernel, que fue ajustada para cargas de trabajo de propósito general que no se parecen en nada a las operaciones de I/O de objetos sostenidas y de alta concurrencia. Pasar de “el hardware debería ser más rápido” a “el hardware es más rápido” depende de unos pocos parámetros sysctl, una elección de planificador de I/O y entender por qué cada uno importa.
Este artículo recorre los parámetros específicos que marcan la diferencia: buffers de red dimensionados para productos de ancho de banda por retardo reales, control de congestión TCP que deja de penalizarte por rutas con buffers pequeños, y selección del planificador de I/O adaptada a tu medio de almacenamiento. Nada de esto requiere capacidad adicional. Requiere ajuste.
Empieza con el producto de ancho de banda por retardo
La restricción fundamental sobre el rendimiento TCP es el producto de ancho de banda por retardo (BDP, por sus siglas en inglés): la cantidad de datos que deben estar en tránsito simultáneamente para llenar un enlace. La fórmula es simple: rendimiento máximo = tamaño de ventana TCP / RTT. Si tus buffers son más pequeños que el BDP, el emisor se detiene esperando acuses de recibo y nunca alcanzas la velocidad de línea, independientemente de lo que reporte la NIC.
En un enlace de 10 Gbps con un RTT de 50 ms, el BDP es de 62,5 MB. El límite máximo del buffer de socket predeterminado del kernel de Linux es de unos 212 KB. Esa diferencia explica muchos resultados decepcionantes en iperf3. La regla estándar de dimensionamiento es establecer tu buffer máximo en al menos 2 veces el BDP y dejar que el ajuste automático del kernel (net.ipv4.tcp_moderate_rcvbuf, habilitado por defecto) escale dentro de ese rango según las condiciones reales.
El detalle crítico que la mayoría de las guías omite: net.core.rmem_max y net.core.wmem_max son límites máximos a nivel de sistema que se aplican a todos los sockets, no solo a TCP. Si elevas net.ipv4.tcp_rmem a 128 MB pero dejas rmem_max en el valor predeterminado de 212 KB, el kernel lo limita silenciosamente a 212 KB. Ambos deben modificarse juntos.
Los parámetros sysctl que importan para el rendimiento del almacenamiento de objetos
Para un entorno de 10 Gbps o superior, una configuración de partida razonable para los parámetros del kernel que afectan al rendimiento del almacenamiento de objetos es la siguiente:
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 1048576 134217728
net.ipv4.tcp_wmem = 4096 1048576 134217728
net.core.rmem_default = 262144
net.core.wmem_default = 262144
Las tuplas de tres valores en tcp_rmem y tcp_wmem corresponden al mínimo, el valor predeterminado y el máximo. El kernel asigna el doble del valor solicitado mediante setsockopt(), por lo que las cifras en /proc parecerán mayores que la ventana efectiva. Tenlo en cuenta al interpretar la salida de diagnóstico.
Los nodos de almacenamiento de objetos suelen gestionar grandes cantidades de conexiones concurrentes. Dos parámetros que es fácil pasar por alto:
net.core.somaxconn = 65535ynet.ipv4.tcp_max_syn_backlog = 65535: la profundidad de cola de escucha predeterminada es demasiado pequeña para servicios con alta tasa de conexiones. Agotarla provoca descartes silenciosos de conexiones que parecen errores de aplicación.net.core.netdev_max_backlog: el valor predeterminado de 1000 descartará paquetes bajo carga sostenida en una NIC ocupada. Auméntalo al menos a 5000, más en interfaces con alta tasa de paquetes.fs.file-max = 2097152: cada operación sobre un objeto abre un descriptor de archivo. Agotarlos es un límite absoluto.net.ipv4.tcp_tw_reuse = 1: seguro para conexiones salientes y reduce la acumulación de conexiones en estado TIME_WAIT en nodos que abren muchas conexiones de corta duración hacia backends.
Persiste todo en /etc/sysctl.d/99-objectstore.conf y aplícalo con sysctl --system. Hacer cambios directamente en /etc/sysctl.conf también funciona, pero es más difícil de auditar y sobreescribir.
Los valores predeterminados del kernel son conservadores por diseño. Se establecieron para un mundo de enlaces de 100 Mbps y recuentos de conexiones modestos. El almacenamiento de objetos en 2025 opera sobre interfaces de 10 Gbps o 25 Gbps con miles de conexiones concurrentes. Cerrar esa brecha no es opcional; es lo primero que hay que hacer antes de cualquier conversación sobre capacidad.
Control de congestión TCP: el caso a favor de BBR
CUBIC, el algoritmo de control de congestión predeterminado de Linux, está basado en pérdidas: interpreta la pérdida de paquetes como una señal de congestión y reduce la velocidad. Eso funciona bien en rutas LAN cableadas con buffers profundos. Funciona mal en rutas con buffers pequeños, pérdidas aleatorias moderadas o variación significativa del RTT, todo lo cual es habitual en despliegues de almacenamiento de objetos que abarcan zonas de disponibilidad o sirven a clientes remotos.
BBR (Bottleneck Bandwidth and Round-trip propagation time) adopta un enfoque diferente. En lugar de reaccionar ante pérdidas, construye un modelo explícito de la ruta midiendo continuamente la tasa de entrega y el RTT, y luego utiliza ese modelo para establecer tanto la tasa de envío como el volumen de datos en tránsito. El resultado práctico es un mayor rendimiento en rutas donde CUBIC habría reducido la velocidad innecesariamente, y menor retardo de cola en rutas con buffers profundos.
Habilitarlo requiere dos parámetros juntos:
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
El qdisc fq (fair queuing) no es opcional aquí; BBR depende de él para el ritmo de envío. Configurar BBR sin fq socava el comportamiento del algoritmo.
Dos advertencias que vale la pena mencionar con honestidad. Primera: BBR v1 (la versión presente en la mayoría de los kernels de distribución) es conocido por competir de forma injusta con CUBIC y HTCP en rutas de red compartidas; ESnet no lo recomienda en redes donde también se usan otros algoritmos de control de congestión. Si tus nodos de almacenamiento de objetos comparten una red con otras cargas de trabajo, prueba antes de comprometerte. Segunda: investigaciones recientes publicadas en ACM SIGCOMM en 2025 muestran que el rendimiento de BBR puede deteriorarse bajo condiciones de planificación virtualizada, lo cual es relevante si ejecutas nodos de almacenamiento como máquinas virtuales en lugar de bare metal. BBRv3, anunciado en 2023, apunta a resolver estos problemas, pero su inclusión en el árbol principal sigue en curso.
Selección del planificador de I/O
La capa de bloque multi-cola de Linux ofrece tres planificadores. La elección correcta depende completamente del medio de almacenamiento:
- none: sin planificación; las solicitudes van directamente a la cola del controlador del dispositivo. Es la opción correcta para SSDs NVMe, que tienen sus propias colas internas profundas y no se benefician del reordenamiento a nivel del kernel. Añadir un planificador aquí agrega latencia sin ningún beneficio.
- mq-deadline: aplica garantías de plazo tanto para lecturas como para escrituras, evitando la inanición de escrituras. Adecuado para SSDs SATA/SAS y configuraciones de medios mixtos donde importa una latencia predecible junto con el rendimiento.
- bfq (Budget Fair Queueing): diseñado para la equidad entre procesos y baja latencia en cargas de trabajo interactivas. Generalmente es la elección incorrecta para el almacenamiento de objetos, donde el objetivo es maximizar el rendimiento agregado en lugar de la equidad por proceso.
Consulta el planificador actual por dispositivo con cat /sys/block/nvme0n1/queue/scheduler. Establécelo temporalmente con echo none > /sys/block/nvme0n1/queue/scheduler y hazlo persistente mediante una regla udev que apunte al tipo de dispositivo. Los parámetros del kernel que afectan al rendimiento del almacenamiento de objetos en la capa de I/O suelen recibir menos atención que el ajuste de red, pero en un nodo con doce unidades NVMe, dejarlos en mq-deadline o bfq limita visiblemente el rendimiento secuencial.
Lectura anticipada y escritura diferida de páginas sucias
Dos parámetros adicionales importan para lecturas y escrituras secuenciales de objetos grandes. vm.dirty_ratio y vm.dirty_background_ratio controlan cuántos datos sucios acumula el kernel antes de forzar la escritura. Los valores predeterminados (20% y 10% de la RAM, respectivamente) están diseñados para cargas de trabajo de escritorio. En un nodo de almacenamiento con 256 GB de RAM, el 20% equivale a 51 GB de datos sucios antes de que el kernel aplique contrapresión. Eso puede causar picos de latencia en escritura en lugar de un rendimiento fluido. Reducir vm.dirty_background_ratio a 5 y vm.dirty_ratio a 10 mantiene la escritura continua en lugar de en ráfagas.
Para lecturas secuenciales de objetos grandes, la lectura anticipada por dispositivo (/sys/block/sdX/queue/read_ahead_kb) debe dimensionarse según el tamaño típico de tus objetos. El valor predeterminado de 128 KB es adecuado para I/O aleatoria pequeña; para objetos de varios megabytes, 2048 KB o más reduce el número de operaciones de I/O necesarias para satisfacer una sola lectura.
Integrándolo todo
En AMS1, donde el denso peering con AMS-IX hace que el tráfico de almacenamiento de objetos atraviese con frecuencia múltiples sistemas autónomos antes de llegar a clientes en el Benelux y el Reino Unido, ejecutamos BBR con fq en los nodos de almacenamiento precisamente porque las características de la ruta varían de formas que CUBIC gestiona mal. La configuración de buffers de red descrita anteriormente es la línea base; los valores reales se validan con iperf3 contra el RTT real de cada clase de tráfico.
El ajuste del kernel de Linux para almacenamiento no es un ejercicio de configuración puntual. Las cargas de trabajo cambian, los tamaños de los objetos cambian y la distribución de clientes cambia. Los parámetros anteriores son un punto de partida con un razonamiento claro detrás de cada uno. Mide antes y después, mantén los cambios en control de versiones junto con el resto de tu configuración de infraestructura, y trata cualquier parámetro que no puedas explicar como un riesgo, no como una configuración.




