Benchmarking de E/S de Disco con fio

fio (Flexible I/O Tester) es la herramienta estándar de la industria para benchmarking de almacenamiento que proporciona control preciso sobre características de carga de trabajo, lo que la hace esencial para validación y optimización de infraestructura de almacenamiento. Con soporte para cargas de trabajo secuenciales y aleatorias, múltiples configuraciones de trabajos y métricas de rendimiento detalladas, fio permite análisis exhaustivo de rendimiento de almacenamiento. Esta guía cubre instalación de fio, benchmarks comunes e interpretación de resultados.

Tabla de Contenidos

  1. Instalación de fio y Conceptos Básicos
  2. Pruebas de Carga de Trabajo Secuencial
  3. Benchmarking de E/S Aleatorio
  4. Análisis de IOPS y Latencia
  5. Archivos de Trabajos y Configuración
  6. Comparación de Dispositivos de Almacenamiento
  7. Simulación Avanzada de Cargas de Trabajo
  8. Análisis y Reporte de Rendimiento
  9. Conclusión

Instalación de fio y Conceptos Básicos

Instalación de fio

# Instalación en Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y fio

# Instalación en CentOS/RHEL
sudo yum install -y fio

# Compilar desde fuente más reciente
git clone https://github.com/axboe/fio.git
cd fio
./configure
make
sudo make install

# Verificar instalación
fio --version

Preparación de Dispositivo de Almacenamiento

# Identificar dispositivo objetivo
lsblk
sudo fdisk -l

# Verificar uso actual del dispositivo
df -h
sudo lsof | grep /dev/sda

# Para probar (solo dispositivo desmontado)
sudo fio --filename=/dev/sdb --rw=read --bs=4k --iodepth=32 \
  --runtime=10 --group_reporting --name=random_read

# Para probar partición
sudo fio --filename=/mnt/test/fio_test_file --rw=read --bs=4k \
  --iodepth=32 --runtime=10 --size=10G --name=file_read

Pruebas de Carga de Trabajo Secuencial

Rendimiento de Lectura Secuencial

# Lectura secuencial de línea base
fio --filename=/mnt/test/fio_test_file --rw=read --bs=1m \
  --iodepth=32 --runtime=30 --name=seq_read

# Parámetros explicados:
# --filename: Ubicación del archivo de prueba
# --rw=read: Lectura secuencial
# --bs: Tamaño de bloque (1m = 1 MB)
# --iodepth: Profundidad de cola (32 típico)
# --runtime: Duración de la prueba (segundos)

# Secuencial de bloque grande (streaming)
fio --filename=/mnt/test/fio_test_file --rw=read --bs=4m \
  --iodepth=16 --runtime=60 --size=100G --name=streaming_read

# Resultados típicos (SSD):
# read: IOPS=1234.56, BW=1240.56 MiB/s
# Rendimiento: ~1.2 GB/s

Rendimiento de Escritura Secuencial

# Prueba de escritura secuencial
fio --filename=/mnt/test/fio_test_file --rw=write --bs=1m \
  --iodepth=32 --runtime=30 --name=seq_write --pre=touch \
  --nrfiles=1 --filesize=100G

# Llenar almacenamiento con escritura secuencial (prueba de vaciado de caché)
fio --filename=/mnt/test/fio_test_file --rw=write --bs=1m \
  --iodepth=64 --runtime=120 --size=500G --sync=1 --name=seq_write_sync

# Monitorear rendimiento de escritura
watch -n 1 'iostat -x -d sdb 1'

Benchmarking de E/S Aleatorio

IOPS de Lectura Aleatoria

# Medir IOPS de lectura aleatoria
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --name=random_read_iops

# Resultados esperados:
# SSD: 10,000-100,000+ IOPS
# HDD: 100-200 IOPS

# Aumentar profundidad de cola para mayor potencial de IOPS
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=128 --runtime=60 --size=10G --name=high_queue_depth

# Probar con tamaño de archivo múltiple
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=64 --runtime=30 --size=100G --name=large_working_set

IOPS de Escritura Aleatoria

# Benchmark de escritura aleatoria
fio --filename=/mnt/test/fio_test_file --rw=randwrite --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --name=random_write_iops

# Escrituras sincronizadas (tipo diario, importante para bases de datos)
fio --filename=/mnt/test/fio_test_file --rw=randwrite --bs=4k \
  --iodepth=16 --fsync=1 --runtime=60 --size=10G --name=sync_writes

# Monitorear disco durante prueba
iostat -x 1

# Probar con E/S directa (omite caché)
fio --filename=/mnt/test/fio_test_file --rw=randwrite --bs=4k \
  --iodepth=32 --direct=1 --runtime=60 --size=10G --name=direct_writes

Análisis de IOPS y Latencia

Percentiles de Latencia

# Medir distribución de latencia
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --lat_log=latency_results \
  --name=latency_test

# Analizar percentiles de latencia
# La salida muestra: min, max, mean, p50, p95, p99, p99.9

# Resultados de latencia de ejemplo:
# lat (usec) : min=5, max=45000, avg=150, stdev=500
# percentiles (usec) :
#  50.00th=[100]
#  90.00th=[200]
#  99.00th=[500]
#  99.90th=[1500]
#  99.99th=[10000]

# Interpretar resultados:
# p99 = 99% de solicitudes se completan dentro de latencia
# p99.9 = 99.9% de solicitudes (latencia de cola)

Análisis de Distribución de Latencia

# Generar histograma de latencia detallado
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=120 --size=10G --log_histogram \
  --name=detailed_latency

# Probar consistencia de latencia a lo largo del tiempo
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=300 --size=10G --lat_log=hourly_latency \
  --name=consistency_test

# Analizar valores atípicos
# El p99.9 alto indica picos ocasionales de latencia
# Sugiere limitación térmica o eventos de recolección de basura

Archivos de Trabajos y Configuración

Crear Archivos de Trabajo de fio

# Crear archivo de trabajo reutilizable
cat > ~/storage_benchmark.fio <<'EOF'
[global]
ioengine=libaio
direct=1
group_reporting=1
time_based=1

[random_read]
rw=randread
bs=4k
iodepth=32
runtime=60
size=10G
numjobs=1

[random_write]
rw=randwrite
bs=4k
iodepth=32
runtime=60
size=10G
numjobs=1

[sequential_read]
rw=read
bs=1m
iodepth=16
runtime=60
size=100G
numjobs=1

[sequential_write]
rw=write
bs=1m
iodepth=16
runtime=60
size=100G
numjobs=1
EOF

# Ejecutar archivo de trabajo
fio ~/storage_benchmark.fio

Configuración de Múltiples Trabajos

# Crear suite de prueba completa
cat > ~/complete_benchmark.fio <<'EOF'
[global]
ioengine=libaio
direct=1
runtime=60
time_based=1
group_reporting=1
filename=/mnt/test/fio_test_file

[seq_read_4k]
rw=read
bs=4k
iodepth=32
stonewall

[seq_read_1m]
rw=read
bs=1m
iodepth=16
stonewall

[rnd_read_4k]
rw=randread
bs=4k
iodepth=32
stonewall

[rnd_write_4k]
rw=randwrite
bs=4k
iodepth=32
stonewall

[mixed_read_write]
rw=randrw
rwmixread=70
bs=4k
iodepth=32
stonewall
EOF

# Ejecutar suite completa
fio ~/complete_benchmark.fio

Comparación de Dispositivos de Almacenamiento

Pruebas de SSD vs HDD

# Prueba de rendimiento de SSD
echo "=== SSD Performance ==="
fio --filename=/mnt/ssd/fio_test --rw=randread --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --name=ssd_randread

# Prueba de rendimiento de HDD
echo "=== HDD Performance ==="
fio --filename=/mnt/hdd/fio_test --rw=randread --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --name=hdd_randread

# Comparación secuencial
echo "=== SSD Sequential ==="
fio --filename=/mnt/ssd/fio_test --rw=read --bs=1m \
  --iodepth=32 --runtime=60 --size=100G --name=ssd_seq

echo "=== HDD Sequential ==="
fio --filename=/mnt/hdd/fio_test --rw=read --bs=1m \
  --iodepth=32 --runtime=60 --size=100G --name=hdd_seq

Análisis de Envejecimiento y Desgaste de Unidad

# Monitorear resistencia de SSD durante pruebas
# Crear script para monitoreo de desgaste
cat > monitor_ssd_wear.sh <<'EOF'
#!/bin/bash
DEVICE=$1

echo "=== Pre-Test SSD Status ==="
sudo smartctl -a $DEVICE | grep -E "Wear_Leveling|Program_Fail|Erase_Fail"

# Ejecutar carga de trabajo sostenida
fio --filename=/mnt/test/fio_test --rw=randwrite --bs=4k \
  --iodepth=32 --runtime=7200 --size=100G --name=endurance_test

echo "=== Post-Test SSD Status ==="
sudo smartctl -a $DEVICE | grep -E "Wear_Leveling|Program_Fail|Erase_Fail"
EOF

chmod +x monitor_ssd_wear.sh
./monitor_ssd_wear.sh /dev/sdb

Simulación Avanzada de Cargas de Trabajo

Simulación de Carga de Trabajo de Base de Datos

# Carga de trabajo tipo OLTP (E/S de bloque pequeño mixta)
cat > oltp_workload.fio <<'EOF'
[global]
ioengine=libaio
direct=1
group_reporting=1
runtime=300
time_based=1

[db_transactions]
rw=randrw
rwmixread=80
bs=16k
iodepth=32
numjobs=4
filename=/mnt/test/database_file
size=50G
EOF

fio oltp_workload.fio

Carga de Trabajo de Streaming/Archivo

# Carga de trabajo de streaming de video o copia de seguridad
cat > streaming_workload.fio <<'EOF'
[global]
ioengine=libaio
direct=1
group_reporting=1

[streaming_read]
rw=read
bs=4m
iodepth=8
runtime=300
numjobs=2
filename=/mnt/test/streaming_file
size=500G
EOF

fio streaming_workload.fio

Análisis y Reporte de Rendimiento

Salida JSON y Análisis

# Generar salida JSON para análisis
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=60 --size=10G --output-format=json \
  > results.json --name=test

# Analizar métricas específicas
cat results.json | jq '.jobs[0].read.iops'
cat results.json | jq '.jobs[0].read.bw'
cat results.json | jq '.jobs[0].read.lat_ns.percentile."99.000000"'

Crear Comparaciones de Línea Base

# Establecer línea base
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=120 --size=10G --output-format=json \
  > baseline.json --name=baseline

# Ejecutar después de modificaciones
fio --filename=/mnt/test/fio_test_file --rw=randread --bs=4k \
  --iodepth=32 --runtime=120 --size=10G --output-format=json \
  > after_tuning.json --name=tuned

# Comparar resultados
echo "Baseline IOPS: $(cat baseline.json | jq '.jobs[0].read.iops')"
echo "After tuning IOPS: $(cat after_tuning.json | jq '.jobs[0].read.iops')"

Conclusión

fio proporciona flexibilidad incomparable para validación de rendimiento de almacenamiento, permitiendo simulación precisa de cargas de trabajo del mundo real y detección de cuellos de botella de almacenamiento. Al entender efectos de tamaño de bloque, optimización de profundidad de cola e implicaciones de percentiles de latencia, los equipos de infraestructura toman decisiones informadas sobre compras de almacenamiento y optimización. El benchmarking regular establece líneas base que detectan degradación de rendimiento por edad, limitación térmica o problemas de firmware. Ya sea optimizando rendimiento de base de datos, validando nueva infraestructura de almacenamiento o solucionando cuellos de botella de E/S, fio sigue siendo la herramienta esencial para excelencia en ingeniería de almacenamiento.