Mise un place de l’ iSCSI sur une plate-forme Linux

Le protocole iSCSI est un protocole mis au point en 1998 (date du 1er prototype). Ce protocole sert à fournir une interface standardisée pour transporter du SCSI sur de l’IP (iSCSI = Internet Small Computer System Interface).

1° – Introduction

Appuyé par Cisco & IBM, ils soumettent à l’IETF une proposition de normalisation du protocole en se basant sur les recherches effectuées chez IBM en 2000 mais il faudra attendre 2004 pour que l’IETF valide la norme. Il s’agit donc d’un protocole d’encapsulation : il sert à transporter un protocole de plus haut niveau, le protocole SCSI. Il permet donc d’assurer le transfert de blocs de données à haute vitesse sur un réseaux Ethernet IP. Son plus gros concurrent est le Channel Fiber (beaucoup plus onéreux). L’ iSCSI est utilisé principalement dans le cadre d’une baie de stockage SAN ou d’un serveur NAS voir un DAS.

2° – Un peu de vocabulaire et le contexte !

Alors bien que ça ne nous servira pas des masses ici, qu’est ce qu’un SAN, NAS et DAS :

  • SAN (Storage Area Network) : Réseau dédié au stockage . Il permet la mutualisation des systèmes de stockage sur un réseau dédié ne permettant que le transport des informations par blocs (SCSI)
  • NAS (Network Attached Storage) : Périphérique de stockage relié à un réseau dans la fonction principale est le stockage centralisé de gros volumes de données. Il partage le même réseau que les serveurs contrairement au SAN qui est sur un réseau dédié.
  • DAS (Direct Attached Storage) : Système de disque en attachement direct, par opposition au NAS qui est en attachement réseau. En effet, seuls les ordinateurs auquel il est raccordé y accède, le plus souvent grâce au protocole iSCSI

Ensuite voici la terminologie concernant notre affaire, c’est à dire l’iSCSI :

  • Target : Serveur cible qui contient les disques réseau. Autrement dit, celui qui va gérer l’espace de stockage. Il est aussi appelé « Cible ».
  • Initiator : Client qui va accueillir le disque réseau qui sera fourni par la « target »

Voici un petit schéma bien plus parlant pour poser le contexte : Iscsi - Schéma de principe Mise en garde importante :

  • Étant donné que le protocole iSCSI est un protocole réseau en mode block et qu’il est vu comme local par le système, il n’est pas possible de partager un même volume entre plusieurs machines (sauf avec un système de fichier distribué tèl que GFS.
  • Ici, même principe que le SAN : il faudra au minimum autant de volume que d’initiateur

Ce que j’appelle « VOLUME » peut être :

  • Un fichier en mode block créé avec DD par exemple (cf exemple plus bas)
  • Un volume LVM
  • Un périphérique disque tel que /dev/sdb ou /dev/sdb1 par exemple

3° – Installation sur le « NAS » ou « SAN » enfin celui qui a les disques :

Dans le monde de l’Open Source, le protocole iSCSI est fournit par l’utilitaire iSCSI Enterprise Target appelé iscsitarget ou daemon « ietd ». Voici la procédure d’installation sur Debian et sur CentOS.

A – Sur debian

 

 # apt-get install iscsitarget iscsitarget-modules-`uname -r`


Il faut ensuite charger le module :

# modprobe iscsi_trgt
iSCSI Enterprise Target Software - version 0.4.15
iotype_init(92) register fileio
iotype_init(92) register blockio
iotype_init(92) register nullio


Puis démarrer le service iSCSI :

# /etc/init.d/iscsitarget start


Pour vérifier si le service est bien démarré :

# /etc/init.d/iscsitarget status
iSCSI enterprise target is running at pid 9855

 

B – Sur CentOS


La, va falloir compiler le module à la mano !! mais ne prenons pas peur :p Tout d’abord, on installe l’environnement nécessaire à la compilation :

# yum install kernel-devel openssl-devel gcc rpm-build


Ensuite, nous allons créer le répertoire qui va nous servir à la compilation et se positionner dedans :

# mkdir /usr/src/iscsitarget# cd /usr/src/iscsitarget


Nous pouvons maintenant récupérer les sources et les dé-tarrer :

# wget [http://dfn.dl.sourceforge.net/sourceforge/iscsitarget/iscsitarget-0.4.15.tar.gz->http://dfn.dl.sourceforge.net/sourceforge/iscsitarget/iscsitarget-0.4.15.tar.gz]
# tar xvf iscsitarget-0.4.15.tar.gz


Enfin il ne reste plus qu’a se placer dans le répertoire contenant les sources et de lancer la compilation :

# cd iscsitarget-0.4.15
# make
# make install


Nous pouvons maintenant charger le driver :

# modprobe iscsi_trgt
iSCSI Enterprise Target Software - version 0.4.15
iotype_init(92) register file
ioiotype_init(92) register blockio
iotype_init(92) register nullio

 

4° – Configuration de la « target » ou Cible !! :p


On considèrera ici que nos périphériques iSCSI sont des images sur un disque. Ces images ont été créé par exemple comme suit :

# dd if=/dev/zero of=disk.img bs=1k count=1000


Les fichiers de configuration du démon IET (iSCSI Entreprise Target) se trouvent ici :

  • /etc/ietd.conf : fichier contenant la description des différents volumes.
  • /etc/initiators.allow : fichier contenant les hôtes autorisés à se connecter
  • /etc/default/iscsitarget : sur debian uniquement, pour activer le démarrage du service

Tout d’abord le fichier /etc/ietd.conf. Ce fichier contient donc la description de chaque ressource iSCSI. Voici sa structure :

Target iqn.2010-04.fr.freenux:storage.serveur1.root
#IncomingUser Utilisateur monmotdepasse
Lun 0 Path=/home/vmfs/serveur1/disk.img,Type=fileio
Alias serveur1.root
HeaderDigest CRC32C,None
DataDigest CRC32C,None
MaxConnections 1
InitialR2T No
ImmediateData Yes
MaxRecvDataSegmentLength 8192
MaxXmitDataSegmentLength 8192
MaxBurstLength 262144
FirstBurstLength 65536
MaxOutstandingR2T 8
HeaderDigest CRC32C,None
DataDigest CRC32C,None
Wthreads 8

Détaillons un peu plus précisément cette déclaration : Target iqn.2010-04.fr.freenux:storage.serveur1.root Le 1er paramètre, le nom du périphérique ou plutôt son adresse normalisé sous le nom. Il en existe 2 formats :

  • Le format IQN (iSCSI Qualified Name). Il est constitué selon le modèle suivant : iqn . Date ( format AAAA-MM) . Domaine . Chaîne unique identifiant le noeud.Par exemple : iqn.2010-04.fr.freenux:storage.serveur1.root
  • Le format eui (Enterprise Unique Identifier). Il est constitué de la façon suivante : eui . 16 chiffres hexadécimauxPar exemple : eui.a7502b8efa5903c0

IncomingUser Utilisateur monmotdepasse : Permet de spécifier un login / mot de passe pour accèder à la ressource. (voir 4° Petites notes) Lun 0 Path=/home/vmfs/serveur1/disk.img,Type=fileio : Chemin vers le volume, le périphérique de stockage, ou un périphérique de type LVM.

  • Le Type fileio permet de faire le mappage entre le Lun et un périphériques de bloc de type sdX ou hdX ou encore un périphérique de bloc virtuel comme LVM ou même un fichier régulier.
  • Le Type blockio lui va effectuer des I/O directs sans passer par le « page-cache » pour tout les opération de lecture / écriture. Cela permet des traitements plus efficace durant le transfert des secteurs dans un environnement virtualisé.

Alias serveur1.root : Optionnel : définition d’un alias afin d’avoir un nom plus « sexy » Les autres options sont les suivantes : MaxConnections 1 : Spécifie le nombre maximum de connexion simultané InitialR2T No : Positionner à YES, l’initiateur doit attendre la target pour être solicité avant d’envoyer les données. En le positionnant à NO, on autorise l’initiateur à envoyer une rafale de bit non sollicité avec une commande avant ou après (en fonction de l’option ImmediateData) afin d’envoyer les données. Afin d’amélioré les performances, le positionner à NO. ImmediateData Yes : Permet à l’initiateur d’envoyer de données non solicité à une commande. Afin d’améliorer les performances, le possionner à Yes MaxRecvDataSegmentLength 8192 Définit la taille maximum du segment de donnée qui peut être reçut. Le valeur devrait être fixé avec un multiples de PAGE_SIZE. La configuration d’une valeur trop grandes peut entraîner des problèmes d’allocation mémoire. La valeur par defaut est de 8 192 MaxXmitDataSegmentLength 8192 Définit la taille maximum du segment de donnée qui peut être envoyé. Même principe que ci-dessus MaxBurstLength 262144 Définit la valeur maximal qu’un message sollicité ou non peut envoyer en un seul burst. FirstBurstLength 65536 Définit la quantité de données non sollicitées que l’initiateur peut transmettre à la première rafale soit avec la commande ou juste après, en fonction des paramètres de InitialR2T et ImmediateData MaxOutstandingR2T 8 Contrôle le nombre maximum de transferts de données que la cible peut demander à la fois HeaderDigest CRC32C,None Active le checksum du header afin de valider son intégrité. DataDigest CRC32C,None Active le checksum des données afin de valider son intégrité wthreads 8 La cible iSCSI utilise plusieurs threads pour exécuter les fonctions d’entrée et sortie sur le périphérique de bloc. En fonction du matériel et de la charge induite, le nombre de ces fils peuvent être soigneusement réglée. 8 semble suffisant dans la majorité des cas Passons ensuite le fichier /etc/initiators.allow. Ce fichier contient les hôtes autorisés, un peu à la manière du fichiere host.allow. Voici un extrait :

iqn.2001-04.com.example:storage.disk1.sys1.xyz ALL
iqn.2001-04.com.example:storage.disk1.sys2.xyz 192.168.12.2, 192.168.3.0/24, 192.167.1.16/28
iqn.2001-04.com.example:storage.disk1.sys4.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64
ALL ALL

L’exemple parle de lui même. On y met l’adresse IQN du périphérique iSCSI, puis l’adress IP v4 ou v6 de l’hôte autorisé ou un plage réseau entière, séparé entre eux par une virgule. Il est possible également de mettre ALL ALL comme ça tout le monde y’ a le droit !!! Pas très sécure en tout cas ! Bref :) Une fois ces fichiers correctement configurés, il suffit de redémarrer le service iSCSI : IET (iSCSI Entreprise Target) :

# /etc/init.d/iscsitarget restart

Pour vérifier si les volumes apparaisent bien :

# cat /proc/net/iet/volume
tid:5 name:iqn.2010-04.fr.freenux:storage.srv2.swap
lun:0 state:0 iotype:fileio iomode:wt path:/home/vmfs/srv2/swap.img
tid:4 name:iqn.2010-04.fr.freenux:storage.srv2.home
lun:0 state:0 iotype:fileio iomode:wt path:/home/vmfs/srv2/home.img
tid:3 name:iqn.2010-04.fr.freenux:storage.srv2.root
lun:0 state:0 iotype:fileio iomode:wt path:/home/vmfs/srv2/disk.img
tid:2 name:iqn.2010-04.fr.freenux:storage.srv1.swap
lun:0 state:0 iotype:fileio iomode:wt path:/home/vmfs/srv2/swap.img
tid:1 name:iqn.2010-04.fr.freenux:storage.srv1.root
lun:0 state:0 iotype:fileio iomode:wt path:/home/vmfs/srv2/disk.img

Nous pouvons voir ici 5 volumes déclarés.

5° – Installation de l’initiateur (client)

Sous Debian, l’installation de l’initiateur est très simple :

# apt-get install open-iscsi iscsitarget-modules-`uname -r`

Les fichiers de configuration se trouvent dans le répertoire /etc/iscsi. Tout d’abord, donnons un petit nom à l’initiateur :

emacs /etc/iscsi/initiatorname.iscsi
InitiatorName=iqn.2010-04.fr.frenux.srv1:01:ae1ab85a3718

Le nom se compose comme suit, selon la norme IQN définit plus haut : iqn . Date (format AAAA-MM) . Le nom du serveur à l’envers : un id unique Ensuite il ne reste plus qu’a lancer la découverte des périphérique iSCSI en donnant l’adresse de la TARGET via le mode « SendTargets », seul mode implémenté sous linux aujourd’hui. Les autres modes sont le mode SLP (utilisé par CUPS par ex) et le mode iSNS (comme le SNS pour le fiber channel) :

# iscsiadm -m discovery -t st -p 192.168.1.1192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.home192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.swap192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.root192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv1.home192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv1.root

La découverte se passe ainsi :

  • L’initiator effectue des requêtes de découverte (SendTargets) vers les targets qu’il connait grâce au fichier de configuration.
  • Chaque target iSCSI retourne les noms des cibles disponibles à l’initiateur.
  • L’initiateur essaye de se connecter et reçoit les ID des cibles.
  • L’initiateur iSCSI demande les infos pour chaque périphérique.
  • Il crée enfin une table des périphériques disponibles.

Enfin il ne reste plus qu’a rattacher le périphérique à l’initiateur :

# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv1.root -p 192.168.1.1 -l

Pour vérifier s’il est bien vu par l’OS, dans les logs du noyau vous devez voir ceci :

dmesg | less
[41.246371] scsi 2:0:0:0: Direct-Access IET VIRTUAL-DISK 0 PQ: 0 ANSI: 4
[41.246371] sd 2:0:0:0: [sdb] 2097152 512-byte hardware sectors (1074 MB)
[41.250383] sd 2:0:0:0: [sdb] Write Protect is off
[41.250383] sd 2:0:0:0: [sdb] Mode Sense: 77 00 00 08
[41.250383] sd 2:0:0:0: [sdb] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[41.250383] sd 2:0:0:0: [sdb] 2097152 512-byte hardware sectors (1074 MB)
[41.250383] sd 2:0:0:0: [sdb] Write Protect is off[41.250383] sd 2:0:0:0: [sdb] Mode Sense: 77 00 00 08
[41.250383] sd 2:0:0:0: [sdb] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[41.250383] sdb: unknown partition table
[41.257320] sd 2:0:0:0: [sdb] Attached SCSI disk

Obtenir des infos sur le périphérique iSCSI :

# udevinfo -q symlink -n /dev/sdb
block/8:16 disk/by-id/scsi-14945540000000000000000000600000011a0650301008000 disk/by-path/ip-192.168.1.20:3260-iscsi-iqn.2010-04.fr.freenux:storage.srv1.swap-lun-0 disk/by-uuid/089a79df-2cd1-470f-b470-8d5ebb2a64b2

Lister les périphériques vu par le système :

# cat /proc/scsi/scsi
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: Maxtor 6L080M0 Rev: BANC
Type: Direct-Access ANSI SCSI revision: 05

Host: scsi2 Channel: 00 Id: 00 Lun: 00
Vendor: IET Model: VIRTUAL-DISK Rev: 0
Type: Direct-Access ANSI SCSI revision: 04

Host: scsi4 Channel: 00 Id: 00 Lun: 00
Vendor: IET Model: VIRTUAL-DISK Rev: 0
Type: Direct-Access ANSI SCSI revision: 04

Pour démonter le disque :

# iscsiadm –m node –T iqn.2010-04.fr.freenux:storage.srv1.root –p 192.168.1.1 -u

 

6° – Petites notes

Pour tout déconnecter d’un seul coup :

# iscsiadm -m node -p 192.168.1.1 -u

Pour tout connecter d’un seul coup :

# iscsiadm -m node -p 192.168.1.1 -l

A priori, à chaque reboot, on aimerait bien que les volumes soient de nouveau la ! Pour faire cela, il faut rendre le montage des volumes persistant :

# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv1.root -p 192.168.1.1 -o update -n 'node.conn[0].startup' -v automatic

Il est important de refaire un discover à chaque ajout de volume sur la target :

# iscsiadm --mode discovery --type sendtargets --portal 192.168.1.1

Pour voir les sessions lancées sur la TARGET :

# cat /proc/net/iet/session

Pour avoir des infos sur le périphérique vu par l’initiateur :

# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv2.home -p 192.168.1.1
node.name = iqn.2010-04.fr.freenux:storage.srv2.home node.tpgt = 1
node.startup = automaticiface.hwaddress = default
iface.iscsi_ifacename = default
iface.net_ifacename = default
iface.transport_name = tcp
iface.initiatorname =
node.discovery_address = 192.168.1.1
node.discovery_port = 3260
node.discovery_type = send_targets
node.session.initial_cmdsn = 0
node.session.initial_login_retry_max = 8
node.session.cmds_max = 128
node.session.queue_depth = 32
node.session.auth.authmethod = None
node.session.auth.username =
node.session.auth.password =
node.session.auth.username_in =
node.session.auth.password_in =
node.session.timeo.replacement_timeout = 120
node.session.err_timeo.abort_timeout = 15
node.session.err_timeo.lu_reset_timeout = 20
node.session.err_timeo.host_reset_timeout = 60
node.session.iscsi.FastAbort = Yes
node.session.iscsi.InitialR2T = No
node.session.iscsi.ImmediateData = Yes
node.session.iscsi.FirstBurstLength = 262144
node.session.iscsi.MaxBurstLength = 16776192
node.session.iscsi.DefaultTime2Retain = 0
node.session.iscsi.DefaultTime2Wait = 2
node.session.iscsi.MaxConnections = 1
node.session.iscsi.MaxOutstandingR2T = 1
node.session.iscsi.ERL = 0
node.conn[0].address = 192.168.1.1
node.conn[0].port = 3260
node.conn[0].startup = manual
node.conn[0].tcp.window_size = 524288
node.conn[0].tcp.type_of_service = 0
node.conn[0].timeo.logout_timeout = 15
node.conn[0].timeo.login_timeout = 15
node.conn[0].timeo.auth_timeout = 45
node.conn[0].timeo.noop_out_interval = 5
node.conn[0].timeo.noop_out_timeout = 5
node.conn[0].iscsi.MaxRecvDataSegmentLength = 131072
node.conn[0].iscsi.HeaderDigest = None
node.conn[0].iscsi.DataDigest = None
node.conn[0].iscsi.IFMarker = No
node.conn[0].iscsi.OFMarker = No

Connexion d’un volume avec authentification : Si vous avez renseigner dans le fichier de conf de la target un IncomingUser avec login / mot de passe, vous devez vous identifier auprès de la Target :

# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv1.root -p 192.168.1.1 -o update -n node.session.auth.authmethod -v CHAP
# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv1.root -p 192.168.1.1 -o update -n node.session.auth.username -v utilisateur
# iscsiadm -m node -T iqn.2010-04.fr.freenux:storage.srv1.root -p 192.168.1.1 -o update -n node.session.auth.password -v monmotdepasse

Informations à partir de l’initiateur sur un volume :

# iscsiadm -m session -r 2 --stats
Stats for session [sid: 2, target: iqn.2010-04.fr.freenux:storage.srv1.home, portal : 192.168.1.1,3260]
iSCSI SNMP:
txdata_octets: 1995827400
rxdata_octets: 267972740
noptx_pdus: 0
scsicmd_pdus: 249551
tmfcmd_pdus: 0
login_pdus: 0
text_pdus: 0
dataout_pdus: 87558
logout_pdus: 0
snack_pdus: 0
noprx_pdus: 0
scsirsp_pdus: 249551
tmfrsp_pdus: 0
textrsp_pdus: 0
datain_pdus: 32906
logoutrsp_pdus: 0
r2t_pdus: 35420
async_pdus: 0
rjt_pdus: 0
digest_err: 0
timeout_err: 0
iSCSI Extended:
tx_sendpage_failures: 0
rx_discontiguous_hdr: 0
eh_abort_cnt: 0

Pour tout les périphériques :

# iscsiadm -m session --stats

Pour avoir des infos sur les sessions établies :

# iscsiadm -m session
tcp: [1] 192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv1.swap
tcp: [2] 192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv1.root
tcp: [3] 192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.swap
tcp: [4] 192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.root
tcp: [5] 192.168.1.1:3260,1 iqn.2010-04.fr.freenux:storage.srv2.home

 

7° – Udev pour fixer le nom des volumes

Le souci c’est qu’en fonction de l’ordre ou les périphériques sont détecté, le nom peut varier d’un reboot à l’autre. Afin de fixer cela, on va demander à Udev de renommer le périphérique de sorte qu’a chaque reboot, il garde son petit nom. Pour faire ceci, créez le fichier suivant :

# emacs /etc/udev/rules.d/95-iscsi.rules

Et ajoutez dedans :

KERNEL=='sd*', BUS=='scsi', PROGRAM='/lib/udev/iscsidev.sh %b', SYMLINK+='iscsi/%c{1}'

Ensuite nous allons ajouter le script iscsidev.sh :

# emacs /lib/udev/iscsidev.sh

Voici le script en question :

#!/bin/sh

BUS=${1}
HOST=${BUS%%:*}
file='/sys/class/scsi_host/host${HOST}/device/session*/iscsi_session*/targetname'
target_name=`echo $(cat ${file}) | sed -r -e 's/(^.*-[^-]*)$/\1/'`
if [ -z '${target_name}' ]; then
exit 1
fi
echo '${target_name}'

On n’oublie pas de rendre le script exécutable :

# chmod 711 /lib/udev/iscsidev.sh

Et de redémarrer udev et l’initiateur :

# /etc/init.d/udev restart && /etc/init.d/open-iscsi restart

On peut maintenant lister les divers périphériques iSCSI dans le répertoire /dev/iscsi :

# ls /dev/iscsi/
iqn.2010-04.fr.freenux:storage.srv1.root iqn.2010-04.fr.freenux:storage.srv1.home iqn.2010-04.fr.freenux:storage.srv2.root iqn.2010-04.fr.freenux:storage.srv1.swap iqn.2010-04.fr.freenux:storage.srv2.home iqn.2010-04.fr.freenux:storage.srv2.swap

 

8° – Conclusion

Voilou un petit tour d’horizon rapide de l’implémentation du protocole iSCSI sur une plate-forme Linux. Tout les aspects n’ont pas été abordés dans cette article. En effet le sujet est bien plus vaste et l’implémentation d’un tel protocole se fait dans un contexte bien particulier en général (infrastructure réseau avec redondance et tolérance de panne).


Article lu 3302 fois

2 comments

  1. hackman61 dit :

    Merci beaucoup!! Je teste depuis 3 jours, et c’est top!!

  2. Anonyme dit :

    Très instructif merci pour le travail je vais tester de ce pas

Laisser un commentaire