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:
- node.startup: indica si se iniciaran las sesiones iSCSI de forma automática al arrancar el demoninio (automatic) o será necesario conectar de forma manual a cada uno de ellos (manual), la recomendación es dejarlo en automatic para hacer login automáticamente en los targets configurados.
- initial_login_retry_max: define el número de reintentos máximos para conectar a un target, el valor por defecto es 4 pero se recomienda aumentarlo al menos hasta 60 para evitar problemas de login con algunas cabinas.
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:
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:
- Añadir el parámetro _netdev a las opciones de montaje del dispositivo, de este modo durante el arranque de la máquina el script rc.sysinit no lo montará. Posteriormente el script netfs se encargará de montar todos aquellos puntos de montajes marcados con esta opción, una vez inicializada la red y tras conectar a los targets iSCSI. Si no utilizamos el parámetro _netdev será imposible montar la partición, ya que Linux tratará de montar el punto de montaje antes de tener habilitado el soporte de red.
- Nunca utilizar nombres de dispositivos (/dev/sdXX) para dispositivos iSCSI. Linux asigna y ordena los nombres de dispositivos según se van descubriendo/conectando nuevos discos, nada nos puede asegurar que un target tendrá siempre asignado, por ejemplo, el dispositivo /dev/sdb, tendremos que utilizar la alternativa UUID o LABEL. Prácticamente todos los sistemas de ficheros permiten asignar una etiqueta (LABEL), así como un identificador único o UUID (Universally Unique Identifier). Con el comando tune2fs -L podemos modificar la etiqueta de una partición ext2/ext3, y con los comandos blkid y tune2fs podemos obtener el UUID y etiqueta, ejemplo:
# 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
- El primer servicio es iscsid que cargará únicamente los módulos del kernel para el soporte iSCSI y arrancará el demonio iscsid.
- El servicio network se encarga de levantar y configurar los dispositivos de red (IPs, alias, DNS, rutas, etc..).
- Posteriormente se inicia el servicio iscsi, encargado de hacer login automáticamente en aquellos targets iSCSI configurados.
- Una vez inicializada la red y tras conectar a los targets, netfs se encargará de montar los dispositivos de red marcados con _netdev.
- 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 */
- 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