Woop!

Configuración de un iniciador iSCSI en Linux / CentOS


En una red de almacenamiento iSCSI existen dos partes: la parte cliente o iniciador, y la parte servidor o target. En este manual veremos como configurar un iniciador iSCSI en Linux, mas concretamente nos centraremos en la instalación del software Open-iSCSI sobre la distribución CentOS. Es indispensable contar con un target iSCSI (ya sea una solución hardware o software) para poder conectarnos a el y realizar los primeros pasos.

Si necesitas configurar un cliente iSCSI sobre Debian, el proceso será algo mas complicado ya que tendrás que parchear los scripts de inicio/parada de algunos servicios para que todo funcione correctamente. Federico Sayd lo explica en su blog paso a paso para Debian Etch, además si utilizas esta distribución es recomendable consultar el post de Davide Ferrari que propone un método alternativo haciendo uso del script sendsigs de Debian.



Instalación y configuración del iniciador Open-iSCSI en Linux


El primer paso es instalar el software Open-iSCSI que nos va a permitir configurar nuestra máquina Linux como un iniciador iSCSI, el paquete viene de serie en todas las versiones de CentOS bajo el nombre iscsi-initiator-utils, por lo tanto podremos instalarlo directamente con Yum:

yum install iscsi-initiator-utils


Inicialmente el proyecto Open-iSCSI estaba divido en dos grandes partes: los módulos del kernel para el soporte iSCSI (scsi_transport_iscsi, libiscsi y iscsi_tcp) y las herramientas de gestión. Para poder utilizar las primeras versiones era necesario parchear el kernel, y además este proyecto convivía con la implementación alternativa por parte de Cisco Systems, Linux-iSCSI. En el 2005 se produjo un paso importante en el soporte de iSCSI en Linux, los dos proyectos se juntaron y unificaron sus esfuerzos para mejorar el soporte de los iniciadores iSCSI en Linux, un año después y con la salida de la versión 2.6.16 de Linux, los módulos del kernel del proyecto Open-iSCSI se integraron en el kernel de Linux, fue un gran paso.

Una vez instalado el paquete Open-iSCSI editamos el fichero /etc/iscsi/initiatorname.iscsi para configurar el nombre y alias del iniciador iSCSI de acuerdo a la nomenclatura IQN (iSCSI Qualified Name), por ejemplo:

InitiatorName=iqn.2001-05.com.example.labs:server01
InitiatorAlias=server01


La nomenclatura IQN normalmente sigue un patrón descriptivo con información sobre la empresa, función del servidor, nombre de la cabina, volumen, etc.. para ayudar a la identificación de los targets e iniciadores iSCSI. Aunque no es recomendable, también podemos utilizar nombres IQN que no aporten esta información, para ello tenemos la aplicación iscsi-iname que se encarga de generar nombres aleatorios de direcciones IQN válidas, ejemplo:

# iscsi-iname 
iqn.1994-05.com.redhat:ae8b72c5290


Una vez elegido el nombre IQN del iniciador pasamos a editar la configuración del cliente iSCSI. El fichero /etc/iscsi/iscsid.conf contiene la configuración generérica del demonio iscsid, es decir, la que tendrán todos los target iSCSI a los que nos conectemos, aunque posteriormente podremos personalizar cada una de los nodos con iscsiadm, ejemplo básico:

node.startup = automatic
node.session.timeo.replacement_timeout = 120
node.conn[0].timeo.login_timeout = 15
node.conn[0].timeo.logout_timeout = 15
node.conn[0].timeo.noop_out_interval = 5
node.conn[0].timeo.noop_out_timeout = 5
node.session.initial_login_retry_max = 60
node.session.cmds_max = 128
node.session.queue_depth = 32
node.session.iscsi.InitialR2T = No
node.session.iscsi.ImmediateData = Yes
node.session.iscsi.FirstBurstLength = 262144
node.session.iscsi.MaxBurstLength = 16776192
node.conn[0].iscsi.MaxRecvDataSegmentLength = 131072
discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 32768


Para una configuración básica de un cliente iSCSI nos interesa conocer al menos los siguientes parámetros:



El resto de parámetros permiten modificar los timeouts, tamaños de buffers, etc.. para mejorar el rendimiento y personalizar el iniciador iSCSI a nuestras necesidades. Si queremos obtener mas información, todos ellos están explicados en la documentación del proyecto Open-iSCSI.

Una vez modificada la configuración y antes de empezar a utilizar iscsiadm, es necesario que el demonio iscsid esté funcionando. Además este demonio se encargará de cargar los módulos iscsi_tcp, libiscsi, ib_iser y scsi_transport_iscsi en el caso de que no estén cargados:

/etc/init.d/iscsid start




Conectando a un target iSCSI


Una vez configurado el iniciador Open-iSCSI podremos empezar a utilizar iscsiadm para conectarnos al target iSCSI. El primer paso es descubrir los volúmenes que está exportando el target o cabina, ejemplo:

# iscsiadm --mode discovery --type sendtargets --portal 10.0.0.1
10.0.0.1:3260,1 iqn.2002-10.com.infortrend:raid


En este ejemplo, utilizamos iscsiadm para conectarnos a la IP/portal 10.0.0.1 -que se trata de una cabina de discos Infortrend-, para descubrir los volumenes que está exportando utilizando el método SendTargets (los métodos iSNS y SLP también están soportados). La cabina responderá con el listado de targets disponibles, en este caso solo se está exportando uno: iqn.2002-10.com.infortrend:raid.

Una vez tenemos el nombre del target al que nos queremos conectar (iqn.2002-10.com.infortrend:raid), tratamos de hacer login directamente en la cabina, sin utilizar la autenticación CHAP básica de usuario/contraseña, ejemplo:

# iscsiadm --mode node --targetname iqn.2002-10.com.infortrend:raid  --portal 10.0.0.1 --login
Logging in to [iface: default, target: iqn.2002-10.com.infortrend:raid, portal: 10.0.0.1,3260]
iscsiadm: initiator reported error (5 - encountered iSCSI login failure)
iscsiadm: Could not execute operation on all records. Err 107.


NOTA: El iniciador iSCSI de Microsoft fuerza a que el tamaño mínimo de las contraseñas para CHAP sean de 12 caracteres, así que para evitar futuros problemas al compartir volúmenes entre diferentes sistemas, puede ser buena práctica que las contraseñas sean superiores a 12 caracteres. En cualquier caso el RFC 3720, que define el protocolo iSCSI, no hace ninguna referencia a este detalle.

En este caso el proceso de login contra el target ha fallado, ya que la cabina está configurada para permitir únicamente el acceso a los volúmenes utilizando la autenticación CHAP. Con la ayuda de iscsiadm vamos a definir un usuario (node.session.auth.username) y contraseña (node.session.auth.password) para hacer login en nuestro target:

PORTAL="10.0.0.1"
TARGETNAME="iqn.2002-10.com.infortrend:raid"
USERNAME="woop"
PASSWORD="contraseña"
iscsiadm -m node --targetname ${TARGETNAME} -p ${PORTAL} -o update -n node.session.auth.username -v ${USERNAME}
iscsiadm -m node --targetname ${TARGETNAME} -p ${PORTAL} -o update -n node.session.auth.password -v ${PASSWORD}


De este modo iscsiadm se encargará de actualizar la base de datos de los targets iSCSI (ficheros dentro de /var/lib/iscsi) para agregar esta información, no será necesario indicar el par usuario/contraseña en cada operación, la aplicación la enviará automáticamente. Una vez realizado este paso, tratamos de volver a conectarnos a la cabina con la autenticacion CHAP configurada:

# iscsiadm -m node --targetname ${TARGETNAME} -p ${PORTAL} -l
Logging in to [iface: default, target: iqn.2002-10.com.infortrend:raid, portal: 10.0.0.1,3260]
Login to [iface: default, target: iqn.2002-10.com.infortrend:raid, portal: 10.0.0.1,3260]: successful


NOTA: Open-iSCSI soporta los métodos de autenticación OneWay-CHAP y Mutual-CHAP, en este caso estamos utilizando la configuración mas básica OneWay-CHAP, es decir el iniciador iSCSI envía al target el par usuario/contraseña y es este último quien se encarga de la validación de los datos. En la autenticación Mutual-CHAP, el iniciador envía el par usuario/contraseña y además el target envía otro par con usuario/contraseña para verificar que realmente nos estamos conectando al target que queremos, en Open-iSCSI estos datos se corresponden con los parámetros node.session.auth.username_in y node.session.auth.password_in.

En este caso la conexión al target iSCSI ha sido correcta, Linux detectará la conexión de un nuevo disco SCSI al sistema y podremos empezar a utilizarlo como si un disco normal se tratara. En nuestro ejemplo, el dispositivo asignado al nuevo disco es /dev/sdb:

# dmesg
  Vendor: IFT       Model: A16E-G2130-4      Rev: 361G
  Type:   Direct-Access                      ANSI SCSI revision: 04
SCSI device sdb: 629145600 512-byte hdwr sectors (322123 MB)
sdb: Write Protect is off
sdb: Mode Sense: 8f 00 00 08
SCSI device sdb: drive cache: write back
SCSI device sdb: 629145600 512-byte hdwr sectors (322123 MB)
sdb: Write Protect is off
sdb: Mode Sense: 8f 00 00 08
SCSI device sdb: drive cache: write back
 sdb: unknown partition table
sd 2:0:0:0: Attached scsi disk sdb
sd 2:0:0:0: Attached scsi generic sg2 type 0

# fdisk -l /dev/sdb

Disk /dev/sdb: 322.1 GB, 322122547200 bytes
255 heads, 63 sectors/track, 39162 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdb doesn't contain a valid partition table


Utilizando iscsiadm hacemos varias pruebas de conexión/desconexión al target iSCSI para comprobar con la ayuda de dmesg y/o fdisk que el volumen aparece y desaparece sin problemas, ejemplo:

# iscsiadm -m node --logoutall all
Logout session [sid: 9, target: iqn.2002-10.com.infortrend:raid, portal: 10.0.0.1,3260]

# iscsiadm -m node --loginall all
Login session [iface: default, target: iqn.2002-10.com.infortrend:raid, portal: 10.0.0.1,3260]




Montar automáticamente las particiones iSCSI en el arranque de Linux


Linux verá el nuevo dispositivo como un disco SCSI "normal y corriente" abstrayendo por completo el medio de transporte iSCSI utilizado para conectarnos a el: podremos utilizar las aplicaciones típicas (fdisk, parted, mkfs, etc.. ) para crear y dar formato a las particiones. Si el volumen va a ser montado y utilizado por mas de un iniciador a la vez será necesario utilizar un sistema de ficheros para almacenamiento compartido del tipo GFS, OCFS2, VMFS, etc.. por el contrario, si la partición va a ser montada únicamente por un cliente, para empezar será el escenario mas típico, podremos utilizar un sistema de ficheros para disco: ext3, JFS, XFS, etc..

En nuestro ejemplo vamos a suponer el caso mas sencillo de todos: el volumen será montado únicamente por un nodo y utilizaremos ext3 como sistema de ficheros. Utilizamos fdisk para crear una única partición y llamamos a partprobe para notificiar al kernel Linux los últimos cambios relalizados en la tabla de particiones y evitar así un reinicio de la máquina. Con la ayuda de mkfs.ext3 y tune2fs formateamos y configuramos la partición para adaptarla a nuestras necesidades:

PARTICION=/dev/sdb1
fdisk /dev/sdb
partprobe -s
mkfs.ext3 -b 4096 -j -m 0 -O dir_index $PARTICION
tune2fs -c 0 -i 0 $PARTICION
tune2fs -L /datos $PARTICION
tune2fs -e continue $PARTICION
mkdir /datos
mount $PARTICION /datos


Uno de los pasos mas importantes es configurar correctamente el dispositivo iSCSI en el fichero /etc/fstab para que la partición se monte automáticamente sin problemas en el próximo arranque de Linux, para ello es necesario realizar dos pasos:



# tune2fs -L /datos /dev/sdb1

# blkid /dev/sdb1
/dev/sdb1: LABEL="/datos" UUID="894bb0a5-c885-4e39-8470-d4048d4478e7" SEC_TYPE="ext2" TYPE="ext3" 

# tune2fs -l /dev/sdb1 | head
Filesystem volume name:   /datos
Filesystem UUID:          894bb0a5-c885-4e39-8470-d4048d4478e7


Siguiendo con nuestro ejemplo, a la partición /dev/sdb1 le hemos asignado la etiqueta /datos y con la ayuda de blkid vemos el UUID aleatorio auto-asignado.

Si el iniciador iSCSI solo se va a conectar a un target con una única partición podemos utilizar los LABELs sin problemas, en cambio, si nos conectamos a un target que está exportando varios volumenes, puede ocurrir que algunos tengan el mismo LABEL. En la medida de lo posible, se recomienda el uso del parámetro UUID para evitar montar una partición equivocada con el mismo LABEL. En nuestro caso el fichero /etc/fstab tendría este aspecto:

UUID=894bb0a5-c885-4e39-8470-d4048d4478e7       /datos     ext3    _netdev,rw  0 0  


Finalmente, es muy importante comprobar que todos los servicios necesarios están habilitados en el arranque así como que su orden es estrictamente correcto, en una instalación por defecto de CentOS esto funciona sin problemas.

# chkconfig netfs on
# ls -1 /etc/rc3.d/S*
/etc/rc3.d/S07iscsid
/etc/rc3.d/S10network
/etc/rc3.d/S13iscsi
/etc/rc3.d/S25netfs
/etc/rc3.d/S96vz


  1. El primer servicio es iscsid que cargará únicamente los módulos del kernel para el soporte iSCSI y arrancará el demonio iscsid.
  2. El servicio network se encarga de levantar y configurar los dispositivos de red (IPs, alias, DNS, rutas, etc..).
  3. Posteriormente se inicia el servicio iscsi, encargado de hacer login automáticamente en aquellos targets iSCSI configurados.
  4. Una vez inicializada la red y tras conectar a los targets, netfs se encargará de montar los dispositivos de red marcados con _netdev.
  5. Finalmente podremos arrancar el resto de servicios que hagan uso de ese punto de montaje, en este caso arrancamos por ejemplo Virtuozzo.

Además, para evitar problemas de integridad de datos con los volúmenes montados por iSCSI estos tienen que ser desmontados en orden correcto: parar las aplicaciones que acceden a la partición, desconectar el disco iSCSI y finalmente podemos deshabilitar la red. En nuestro caso, en una instalación por defecto de Open-iSCSI sobre CentOS el proceso de conexión y desconexión a los targets iSCSI durante el arranque y parada de Linux funciona sin problemas.

NOTA: En algunos casos, y con determinadas cabinas (ocurre por ejemplo con las Infortrend), para evitar problemas de login contra los targets durante en arranque de Linux será necesario parchear el script de inicio netfs añadiendo un retraso de unos segundos antes de montar el dispositivo, un segundo suele ser mas que suficiente:

# diff /tmp/netfs  /etc/init.d/netfs 
63,67d62
<
<             # 1 second sleep to prevent login timeouts with some Infortrend iSCSI targets -- santi-(at)-woop.es
<             sleep 1
<             mount -a -O _netdev
<




Tareas básicas a la hora de trabajar con Open-iSCSI


Borrar un target iSCSI

Si queremos borrar un target iSCSI de la base de datos de Open-iSCSI (directorio /var/lib/iscsi) podemos hacerlo directamente con iscsiadm. El primer comando borra todaos los volúmenes que está exportando el target, y el segundo ejemplo borra un determinado target iSCSI (el que tiene como nombre iqn.2002-10.com.infortrend:raid):

# iscsiadm -m discovery -p 10.0.0.1:3260 -o delete
# iscsiadm -m node -p 10.0.0.1:3260 --targetname iqn.2002-10.com.infortrend:raid -o delete 



Listar conexiones iSCSI establecidas o mostrar los targets iSCSI a los que estamos conectados

Con iscsiadm podemos listar las sesiones iSCSI establecidas e información adicional de cada conexión (datos del iniciador iSCSI, parámetros iSCSI negociados en la comunicación, dispositivos SCSI conectados, etc..). Con el parámetro -P hacemos que iscsiadm muestre mas o menos información de la conexión:

# iscsiadm -m session -P 0
tcp: [1] 10.0.0.1:3260,1 iqn.2002-10.com.infortrend:raid


Obtener el nombre del módulo del kernel Linux que controla el dispositivo/bus SCSI

Esta información se puede obtener de /sys en el parámetro proc_name, dato muy útil para saber si nos estamos conectando a un bus SCSI por iSCSI, Fiber Channel, por una controladora HBA RAID, etc..

# ls -1 /sys/class/scsi_host/    
host0
host2

# cat /sys/class/scsi_host/host*/proc_name 
megaraid_sas
iscsi_tcp	



Obtener información de la tabla iBFT (iSCSI Boot Firmware Table)

La tabla iBFT contiene parámetros utilizados para arrancar desde la NIC vía iSCSI, podemos consultar esa información con iscsiadm (en la web de Microsoft existe un documento con las especificaciones de iBFT):

# modprobe iscsi_ibft
# iscsiadm -m fw




SCSI return codes

Información sobre los códigos de error del sistema SCSI extraida de drivers/scsi/scsi.h, también esta disponible en las especificaciones del standard T10/SAM-4 (SCSI Architecture Model - 4):

#define DID_OK                  0x00    /* NO error                                */
#define DID_NO_CONNECT          0x01    /* Couldn't connect before timeout period  */
#define DID_BUS_BUSY            0x02    /* BUS stayed busy through time out period */
#define DID_TIME_OUT            0x03    /* TIMED OUT for other reason              */
#define DID_BAD_TARGET          0x04    /* BAD target.                             */
#define DID_ABORT               0x05    /* Told to abort for some other reason     */
#define DID_PARITY              0x06    /* Parity error                            */
#define DID_ERROR               0x07    /* Internal error                          */
#define DID_RESET               0x08    /* Reset by somebody.                      */
#define DID_BAD_INTR            0x09    /* Got an interrupt we weren't expecting.  */
#define DID_PASSTHROUGH         0x0a    /* Force command past mid-layer            */
#define DID_SOFT_ERROR          0x0b    /* The low level driver just wish a retry  */
#define DID_IMM_RETRY           0x0c    /* Retry without decrementing retry count  */
#define DID_REQUEUE             0x0d    /* Requeue command (no immediate retry)    */
#define DID_TRANSPORT_DISRUPTED 0x0e    /* Transport error disrupted execution     */
#define DID_TRANSPORT_FAILFAST  0x0f    /* Transport class fastfailed the io       */
#define DRIVER_OK               0x00    /* Driver status                           */




Sobre los parámetros timeo.noop_out_interval y timeo.noop_out_timeout de Open-iSCSI


- El iniciador iSCSI hace un ping iSCSI al target cada timeo.noop_out_interval segundos para comprobar que el target está "vivo".

- Si el iniciador no recibe respuesta en timeo.noop_out_timeout segundos, Open-iSCSI cierra la conexión y tratará de reconectar, en este caso se genera el error:

ping timeout of 5 secs expired, last rx 41758431, last ping 41757394, now 41762394
 connection1:0: iscsi: detected conn error (1011)


Extraído de la documentación de Open-iSCSI:

To quickly detect problems in the network, the iSCSI layer will send iSCSI pings (iSCSI NOP-Out requests) to the target. If a NOP-Out times out the iSCSI layer will respond by failing running commands and asking the SCSI layer to requeue them if possible (SCSI disk commands get 5 retries if not using multipath). If dm-multipath is being used the SCSI layer will fail the command to the multipath layer instead of retrying. The multipath layer will then retry the command on another path.

To control how often a NOP-Out is sent the following value can be set:

node.conn[0].timeo.noop_out_interval = X

Where X is in seconds and the default is 10 seconds. To control the timeout for the NOP-Out the noop_out_timeout value can be used:

node.conn[0].timeo.noop_out_timeout = X

Again X is in seconds and the default is 15 seconds.

Normally for these values you can use:

node.conn[0].timeo.noop_out_interval = 5
node.conn[0].timeo.noop_out_timeout = 10

If there are a lot of IO error messages, then the above values may be too aggresive and you may need to increase the values for your network conditions and workload, or you may need to check your network for possible problems.



Referencias sobre iSCSI



Powered by Woop!