Una novedad del próximo núcleo Linux 3.10, bcache

per Victor Carceler darrera modificació 2020-03-25T15:32:09+01:00

La versión 3.10 de Linux incluye por primera vez bcache, una capa para los dispositivos de bloques que permite utilizar discos SSD como cache de uno (o varios) discos tradicionales, de manera transparente y con diferentes políticas (write-through y write-back). Así se puede combinar un disco SSD con un disco tradicional para intentar conseguir las ventajas de ambos (gran capacidad y gran velocidad de acceso).

Obviamente la idea de utilizar un dispositivo rápido como cache de uno lento y de mayor capacidad no es nueva. Respecto a los sistemas de ficheros y dispositivos de almacenamiento podemos encontrar implementaciones parecidas: en el sistema de archivos ZFS, en Fusion Drive de Apple o Flashcache de Facebook. En el caso de bcache, que es una evolución de Flashcache, se trabaja a nivel de bloque con total independencia del sistema de archivos utilizado. Otro proyecto relacionado es el target dm-cache que en Linux 3.10 está marcado como experimental. En este último caso se pretende utilizar un dispositivo de bloques local (una partición o disco) como cache para acelerar el acceso a medios de almacenamiento SAN (como recursos iSCSI).

En el momento de escribir este artículo el kernel Linux 3.10 todavía no se ha lanzado, pero gracias a la transparencia propia del software libre se puede seguir su desarrollo al minuto. Así que cuando leí la noticia en Phoronix de que Torvalds acababa de hacer el merge de bcache en la rama principal del kernel me entraron ganas de probarlo. Como no he sido lo suficientemente rápido en hacerlo, he dado tiempo a que en www.kernel.org preparen un archivo con el código fuente de la RC1 (release candidate 1) de la versión 3.10. De haber sido más rápido tendría que haber descargado el código, lo que aún se puede hacer, desde el servidor git.

Descargando e instalando el nuevo núcleo:

El proceso de construcción del núcleo está tan bien automatizado que, una vez instaladas las dependencias necesarias:

usuario@Sputnik:~$ sudo apt-get install git-core libncurses5 libncurses5-dev libelf-dev asciidoc binutils-dev linux-source libncurses5 libncurses5-dev fakeroot build-essential crash kexec-tools makedumpfile kernel-wedge kernel-package

y por supuesto descargado y desarchivado el código fuente:

vcarceler@Sputnik:~$ wget https://www.kernel.org/pub/linux/kernel/v3.x/testing/linux-3.10-rc1.tar.xz
--2013-05-14 11:51:31--  https://www.kernel.org/pub/linux/kernel/v3.x/testing/linux-3.10-rc1.tar.xz
Resolviendo www.kernel.org (www.kernel.org)... 149.20.4.69, 198.145.20.140
Conectando con www.kernel.org (www.kernel.org)[149.20.4.69]:443... conectado.
Petición HTTP enviada, esperando respuesta... 200 OK
Longitud: 73128916 (70M) [application/x-xz]
Grabando a: “linux-3.10-rc1.tar.xz”

100%[====================================================================================================>] 73.128.916   579K/s   en 2m 6s   

2013-05-14 11:53:39 (565 KB/s) - “linux-3.10-rc1.tar.xz” guardado [73128916/73128916]

vcarceler@Sputnik:~$ tar -xJf linux-3.10-rc1.tar.xz

Bastará con especificar qué componentes de Linux se van a compilar. Esto se hace mediante una configuración para construir el núcleo.

En nuestro caso vamos a partir de la configuración utilizada por el núcleo que estamos utilizando antes de actualizar. Así que entraremos en el directorio del código fuente y ejecutaremos:

vcarceler@Sputnik:~/linux-3.10-rc1$ cp -vi /boot/config-`uname -r` .config
«/boot/config-3.2.0-23-generic-pae» -> «.config»
vcarceler@Sputnik:~/linux-3.10-rc1$

Una vez copiada la configuración será necesario procesarla con 'make oldconfig', al hacerlo se nos preguntará por los valores de los nuevos parámetros (que existen en Linux 3.10 y no están especificados en nuestra configuración que corresponde a una versión anterior). Cuando nos pregunte escogeremos el valor por defecto manteniendo pulsada la tecla enter.

vcarceler@Sputnik:~/linux-3.10-rc1$ make oldconfig

A partir de la versión 2.6.32 de Linux es posible seleccionar automáticamente aquellos módulos que son necesarios para nuestro hadware, de manera que ahorraremos tiempo al compilar partes de Linux que no necesitamos. Igual que en el apartado anterior, si nos pregunta, podemos escoger el valor por defecto.

vcarceler@Sputnik:~/linux-3.10-rc1$ make localmodconfig

Finalmente podemos lanzar la herramienta que nos despliga el menú de opciones para seleccionar aquellas que convenga.

vcarceler@Sputnik:~/linux-3.10-rc1$ make menuconfig

En nuestro caso, activaremos:

  1. Device Drivers > Multiple devices driver support (RAID and LVM) -> Block device as cache
  2. Device Drivers > Multiple devices driver support (RAID and LVM) -> Cache target (EXPERIMENTAL), Snapshot target y Thin provisioning target.

Configuración de make menuconfig para incluir soporte de bcache y, ya que estamos, de dm-cache

Realmente para utilizar bcache únicamente necesitamos el primer punto, pero como tampoco había probado dm-cache y los otros targets aprovecho para incluirlos.

Una vez que la configuración ha sido guardada viene el trabajo duro para la máquina, compilar!

vcarceler@Sputnik:~/linux-3.10-rc1$ time make -j5

El parámetro -j de make permite especificar el número de ficheros del núcleo que se compilarán en paralelo (cuando sea posible). Normalmente se suele indicar el número de núcleos que tenga nuestra máquina + 1 para garantizar que no habrá ningún núcleo parado.

Una vez compilado podemos instalar los módulos y el núcleo ejecutando:

sudo make modules_install
sudo make install

Después podremos reiniciar la máquina y arrancar con Linux 3.10 - rc1!

Sobre bcache

La herramienta que me ha llevado a compilar una RC de Linux tiene una naturaleza interesante, algunos de sus rasgos son:

  1. Se distingue entre cacheset, cache device y storage device. El primero, es el agregado formado por uno o varios discos rápidos que actúan como cache y uno o varios discos grandes que guardan los datos. Por supuesto el cache device sería el disco rápido que actúa como cache y el storage device es el disco tradicional que guarda la información.
  2. Se supone que los cache device son discos SSD y los storage device son discos tradicionales, de manera que todo se orienta hacia conseguir un mayor rendimiento dadas las características de cada dispositivo. A saber:
  • Los discos SSD son mucho mejores que los tradicionales en las operaciones de lectura/escritura aleatorias
  • Los discos tradicionales no son malos en las operaciones de lectura/escritura secuenciales. Se discrimina (configurable) el tráfico secuencial para que se salte la cache y vaya directamente al disco de respaldo
  • Las escrituras aleatorias realizadas en el SSD se convierten en escrituras secuenciales cuando los datos se mueven al disco tradicional
  • Un mismo cache device puede actuar como cache para varios storage device. En tal caso puede generarse congestión en el cache device y para conseguir un mejor rendimiento convendría saltárselo accediendo directamente a los discos de respaldo. Bcache monitoriza los cache device para detectar la congestión y activar un bypass cuando sea necesario.
  • Los dispositivos de respaldo se pueden añadir y quitar de un cacheset dinámicamente, mientras están montados y en uso.
  • Es posible seleccionar la política de cache a utilizar: writethrough, writeback y writearound.
  • Estabilidad e integridad, es una solución que ya se está utilizando en producción. Promete recovers from unclean shutdown dado que las escrituras no se completan hasta que la cache no está en un estado consistente con el dispositivo de respaldo. Y se perfila como una mejor solución (y más barata) que las unidades de almacenamiento alimentadas con baterías.
  •  

    El modo de operación, gracias a las bcache-tools que conviene descargar, se resume en:

    1. Hay que marcar los dispositivos que se van a utilizar como discos cache o de respaldo. Lo que se puede hacer dispositivo a dispositivo o varios a la vez. Cuando se formatean discos de respaldo y de cache quedan conectados de forma automática.
      make-bcache -B /dev/sdb
      make-bcache -C /dev/sdc
      make-bcache -B /dev/sda /dev/sdb -C /dev/sdc
    2. Los dispositivos bcache deben registrarse
      echo /dev/sdb > /sys/fs/bcache/register
      echo /dev/sdc > /sys/fs/bcache/register
      Una vez registrados se podrá acceder al correpondiente dispositivo /dev/bcacheN para formatearlo y montarlo con normalidad.
    3. Después de haber registrado los dispositivos de chache y respaldo, es necesario conectar el dispositivo de respaldo con un cache set. Esto se hace utilizando el UUID del cache set en /sys/fs/bcache
      echo <UUID> > /sys/block/bcache0/bcache/attach
      Esta operación sólo se debe realizar una vez, en los próximos arranques únicamente será necesario registrar los dispositivos.

     

    Para ilustrar valores reales podemos leer el excelente artículo SSD caching using Linux and bcache, en el que el autor pone a prueba bcache utilizando los siguientes discos:

    DispositivoCapacidadRandom ops (read/write)Sequential (read/write)
    Intel 330 SSD 60GB 42000/52000 op/s 500/450 MB/s
    WD Green Disk (5400 RPM) 2TB 100 op/s 110 MB/s

    Y se puede ilustrar con el esquema que ha preparado el mismo autor

    Original: http://pommi.nethuis.nl/wp-content/uploads/2012/12/hdd-ssd-scheme.png

     

    Sobre el entorno de pruebas

    Ha llegado el momento de aclarar que no tengo ningún disco SSD a mano para hacer de cache, y mucho menos cuento con los suficientes para que los alumnos repliquen la experiencia como práctica. Это неважно.

    No importa porque hasta ahora todo lo he hecho en una máquina virtual con VirtualBox, y:

    • VirtualBox permite identificar un disco duro como SSD (para que el SO huésped lo vea como SSD, aunque probablemente a bcache con que sea un dispositivo de bloque le valga)
    • VirtualBox permite limitar el ancho de banda disponible para las imágenes de disco

    Así que a la máquina virtual le agrego dos discos nuevos, uno de 2GB que va a actuar como SSD y no tendrá limitación de velocidad y otro de 8GB que actuará como un disco tradicional y tendrá su velocidad limitada a 1MB/s.

    vcarceler@sombragris:~$ VBoxManage bandwidthctl "Ubuntu-RAID-LVM-Linux-3.10" --name "limit" --add disk --limit 1
    vcarceler@sombragris:~$ VBoxManage storageattach "Ubuntu-RAID-LVM-Linux-3.10" --storagectl "Controlador SCSI" --port 1 --bandwidthgroup limit
    vcarceler@sombragris:~$

    Obsérvese que la sentencia utilizada difiere en sintáxis de lo expresado en la documentación oficial de VirtualBox. Esta es la sintaxis válida para la versión de VirtualBox utilizado (que es la 4.1.12 distribuida con Ubuntu 12.04 LTS).

    También hay que tener en cuenta un par de factores que pueden alterar el resultado esperado:

    1. I/O Cache del anfitrión. Nuestros discos virtuales son ficheros en el SO anfitrión. Así que el sistema operativo anfitrión puede utilizar su cache para tratar de acelerar el acceso a estos datos. VirtualBox permite desde su interfaz gráfica desactivar esta opción para el controlador de los discos, de manera que el acceso a los ficheros no haga uso de la cache de la máquina física.
    2. La cache del propio SO virtualizado. Puesto que Linux utilizará la memoria disponible para acelerar la entrada/salida a disco, nos podemos encontrar con una velocidad de acceso aparente superior a la del dispositivo. Este efecto se puede mitigar cuando el volumen de datos excede en mucho a la capacidad de la cache o bien actuando sobre /proc/sys/vm/min_free_kbytes para indicar a Linux que mantenga libre cierta cantidad de memoria y, por lo tanto, poner un límite a la memoria principal utilizable como cache.

     

    Con esto ya podemos arrancar nuestra máquina virtual y encontrar en /dev/sdb un disco SSD de 2GB y en /dev/sdc un disco tradicional de 8GB que está limitado a una velocidad de acceso de 1MB/s.

    Probando bcache en nuestro entorno virtualizado

    Un entorno de pruebas virtualizado no es adecuado para medir el desempeño real de bcache pero nos puede servir para familizarizarnos con la herramienta. En este caso la primera tarea será instalar bcache-tools clonando su repositorio git y compilando. En mi caso antes de poder compilar ha sido necesario instalar el paquete uuid-dev.

    vcarceler@Sputnik:~$ git clone http://evilpiepirate.org/git/bcache-tools.git 
    Cloning into 'bcache-tools'...
    vcarceler@Sputnik:~$ cd bcache-tools/
    vcarceler@Sputnik:~/bcache-tools$
    vcarceler@Sputnik:~/bcache-tools$ sudo apt-get install uuid-dev
    vcarceler@Sputnik:~/bcache-tools$ make
    cc -O2 -Wall -g make-bcache.c bcache.o -luuid -o make-bcache
    cc -O2 -Wall -g probe-bcache.c -luuid -o probe-bcache
    cc -O2 -Wall -g bcache-super-show.c bcache.o -luuid -o bcache-super-show
    vcarceler@Sputnik:~/bcache-tools$ sudo make install
    install -m0755 make-bcache bcache-super-show /usr/sbin/
    install -m0755 probe-bcache /sbin/
    install -m0644 61-bcache.rules /lib/udev/rules.d/
    install -m0755 bcache-register /lib/udev/
    install -m0755 initramfs/hook /etc/initramfs-tools/hooks/bcache
    install -m0644 -- *.8 /usr/share/man/man8
    vcarceler@Sputnik:~/bcache-tools$

    Y probamos bcache:

    1. Formateamos los dos disopositivos
      root@Sputnik:/mnt# make-bcache -C /dev/sdb -B /dev/sdc
      UUID: e78aa303-335e-4ba7-a96f-0a2c07cd62af
      Set UUID: e067170c-95a6-4c4a-a122-44f6b8fa9842
      version: 0
      nbuckets: 4096
      block_size: 8
      bucket_size: 1024
      nr_in_set: 1
      nr_this_dev: 0
      first_bucket: 1
      UUID: 839f2625-f49a-46da-aed0-2611aec00ef7
      Set UUID: e067170c-95a6-4c4a-a122-44f6b8fa9842
      version: 1
      block_size: 8
      data_offset: 16
      root@Sputnik:/mnt#
    2. Registramos los dispositivos
      echo /dev/sd* >/sys/fs/bcache/register_quiet
    3. Creamos un sistema de archivos y lo montamos
      root@Sputnik:~# mkfs.ext4 /dev/bcache0
      mke2fs 1.42 (29-Nov-2011)
      Discarding device blocks: hecho                           
      Etiqueta del sistema de ficheros=
      OS type: Linux
      Tamaño del bloque=4096 (bitácora=2)
      Tamaño del fragmento=4096 (bitácora=2)
      Stride=0 blocks, Stripe width=0 blocks
      524288 inodes, 2097150 blocks
      104857 blocks (5.00%) reserved for the super user
      Primer bloque de datos=0
      Número máximo de bloques del sistema de ficheros=2147483648
      64 bloque de grupos
      32768 bloques por grupo, 32768 fragmentos por grupo
      8192 nodos-i por grupo
      Respaldo del superbloque guardado en los bloques: 
      	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
      
      Allocating group tables: hecho                           
      Escribiendo las tablas de nodos-i: hecho                           
      Creating journal (32768 blocks): hecho
      Escribiendo superbloques y la información contable del sistema de ficheros:  0/6hecho
      
      root@Sputnik:~# mkdir /mnt/bcache0
      root@Sputnik:~# mount /dev/bcache0 /mnt/bcache0/
      root@Sputnik:~# df -h
      S.ficheros     Tamaño Usados  Disp Uso% Montado en
      /dev/sda1        7,5G   5,5G  2,0G  74% /
      udev             992M   4,0K  992M   1% /dev
      tmpfs            401M   280K  401M   1% /run
      none             5,0M      0  5,0M   0% /run/lock
      none            1002M      0 1002M   0% /run/shm
      /dev/bcache0     7,8G    18M  7,4G   1% /mnt/bcache0
      root@Sputnik:~#
    4. Comprobamos que nuestro dispositivo bcache0 está funcionando
      root@Sputnik:/sys/fs/bcache/e067170c-95a6-4c4a-a122-44f6b8fa9842# ll
      total 0
      drwxr-xr-x 7 root root    0 may 16 19:35 ./
      drwxr-xr-x 3 root root    0 may 16 19:32 ../
      -r--r--r-- 1 root root 4096 may 16 19:35 average_key_size
      lrwxrwxrwx 1 root root    0 may 16 19:35 bdev0 -> ../../../devices/pci0000:00/0000:00:0d.0/ata3/host2/target2:0:0/2:0:0:0/block/sdc/bcache/
      -r--r--r-- 1 root root 4096 may 16 19:35 block_size
      -r--r--r-- 1 root root 4096 may 16 19:35 btree_cache_size
      -r--r--r-- 1 root root 4096 may 16 19:35 bucket_size
      lrwxrwxrwx 1 root root    0 may 16 19:35 cache0 -> ../../../devices/pci0000:00/0000:00:0d.0/ata2/host1/target1:0:0/1:0:0:0/block/sdb/bcache/
      -r--r--r-- 1 root root 4096 may 16 19:35 cache_available_percent
      --w------- 1 root root 4096 may 16 19:35 clear_stats
      -r--r--r-- 1 root root 4096 may 16 19:35 congested
      -rw-r--r-- 1 root root 4096 may 16 19:35 congested_read_threshold_us
      -rw-r--r-- 1 root root 4096 may 16 19:35 congested_write_threshold_us
      -r--r--r-- 1 root root 4096 may 16 19:35 dirty_data
      --w------- 1 root root 4096 may 16 19:35 flash_vol_create
      drwxr-xr-x 2 root root    0 may 16 19:35 internal/
      -rw-r--r-- 1 root root 4096 may 16 19:35 io_error_halflife
      -rw-r--r-- 1 root root 4096 may 16 19:35 io_error_limit
      -rw-r--r-- 1 root root 4096 may 16 19:35 journal_delay_ms
      -r--r--r-- 1 root root 4096 may 16 19:35 root_usage_percent
      drwxr-xr-x 2 root root    0 may 16 19:35 stats_day/
      drwxr-xr-x 2 root root    0 may 16 19:35 stats_five_minute/
      drwxr-xr-x 2 root root    0 may 16 19:35 stats_hour/
      drwxr-xr-x 2 root root    0 may 16 19:35 stats_total/
      --w------- 1 root root 4096 may 16 19:35 stop
      -rw-r--r-- 1 root root 4096 may 16 19:35 synchronous
      -r--r--r-- 1 root root 4096 may 16 19:35 tree_depth
      --w------- 1 root root 4096 may 16 19:35 unregister
      root@Sputnik:/sys/fs/bcache/e067170c-95a6-4c4a-a122-44f6b8fa9842#

     

    Y hasta aquí la primera prueba de bcache, ahora podemos valorar su comportamiento en diferentes situaciones. Y sobre todo quien sea el afortunado poseedor de un disco SSD puede hacer algunas pruebas de velocidad que tendrán mayor interés que lo que se pueda medir en un entorno virtualizado.

     

    Más información: