Desde que un proceso hace un write() hasta que el dato se almacena en el medio físico este atraviesa varias capas (sistema de ficheros, gestor de volúmenes, controladora RAID + BBU, caches de disco, etc.) donde se realizan una serie de optimizaciones para mejorar el acceso a disco, en entornos virtuales estas capas pueden llegar a duplicarse:
Hoy en día es común encontrarnos escenarios con 3-4 capas de I/O scheduling y a priori podríamos pensar que sumarlas va a mejorar el rendimiento. En la práctica y aunque parezca increíble, en conexiones a sistemas RAID -ya sea por iSCSI, Fiber Channel o vía DAS- deshabilitar estas optimizaciones pueden darnos mejores resultados.
En Linux la optimización más importante se realiza en el planificador de E/S de los dispositivos de bloques (en inglés, I/O scheduler), una capa inteligente entre el sistema operativo y el hardware donde se aplican diferentes algoritmos (CFQ, deadline, anticipatory, noop, etc) para ordenar operaciones de E/S y hacer así un uso más eficiente de los discos: agrupar escrituras, reducir seeks, priorizar ciertas operaciones de I/O, etc. Este comportamiento tiene mayor impacto en sistemas con I/O aleatorias, por ejemplo en grandes bases de datos MySQL.
En estas dos gráficas vemos el %iowait y la carga de una máquina Linux con CentOS conectada vía iSCSI a una cabina NetApp, tras deshabilitar el I/O scheduler CFQ (planificador por defecto en RHEL) la carga baja de 10 a 1 y el iowait del %20 a %4:
En Linux podemos cambiar este comportamiento en caliente y por cada dispositivo vía SysFS (entre corchetes aparece el planificador de E/S activo):
# cat /sys/block/sda/queue/scheduler noop anticipatory deadline [cfq] # echo noop > /sys/block/sda/queue/scheduler # cat /sys/block/sda/queue/scheduler [noop] anticipatory deadline cfq
Para hacer estos cambios persistentes a reinicios podemos utilizar udev y aplicar un planificador de forma selectiva, en este ejemplo establecemos “noop” solo en conexiones del tipo iSCSI o FC (podríamos afinar la regla y aplicarla únicamente a cabinas de un determinado fabricante, por ejemplo con vendor=”NETAPP” y model=”LUN”):
ACTION=="add", SUBSYSTEM=="scsi", ENV{ID_FS_USAGE}!="filesystem", \
ENV{ID_PATH}=="*-iscsi-*|-fc-*", \
RUN+="/bin/sh -c 'echo noop > /sys$DEVPATH/queue/scheduler'"
Un I/O scheduler da su máximo rendimiento cuando tiene una cola por cada disco y si los discos están bajo un RAID el kernel los verá como un único dispositivo y utilizará una única cola (aunque tengamos 10 discos).
En un sistema RAID el kernel entrega ordenado el I/O a la controladora a través de una cola única y esta a su vez lo distribuye entre los discos, este mecanismo funciona bien hasta alcanzar el límite de una cola/disco (~250 ops para discos de 15K aprox.) y a partir de aquí empieza a degradarse. Al deshabilitar el planificador el tráfico I/O llegaría desordenado a la controladora RAID y esta podría hacer el reparto de forma más eficiente (para una explicación más detallada recomiendo la lectura del post “Optimizing Linux for random I/O on hardware RAID“), gráficamente:
Como alternativa menos cómoda a modificar el I/O scheduler en vez crear un RAID con todos los discos podemos crear grupos y posteriormente unirlos por software, así el kernel podrá trabajar con más de una cola; Ejemplo: si tenemos un RAID con 10 discos podemos crear 5 grupos de 2 discos en RAID-1 y unirlos en stripe vía LVM (a efectos un RAID 1+0), de este modo Linux vería 5 dispositivos y trabajaría más eficientemente al utilizar 5 colas.
Finalmente, todo esto depende del tipo de cabina, tráfico I/O, discos, etc. pero en general si tienes un buen sistema RAID + BBU deshabilitar la reordenación de I/O en el kernel y delegar este trabajo al hardware especializado va a dar mejores resultados. Pero como siempre… lo mejor es realizar benchmarks con diferentes schedulers y comparar resultados





