Le SAN maison pas cher

Dans un précédent article, je parlais de ma solution de stockage pour ma grappe d'hyperviseurs, pour pouvoir faire des migrations à chaud, tout ça. Si vous vous souvenez bien, pour la mise à disposition du stockage par plusieurs machines en même temps, j'avais évoqué plusieurs possibilités, dont DRBD multi-master et iSCSI sur un bon gros SAN bien gras. J'étais parti sur DRBD pour plusieurs raisons, aujourd'hui je vais vous parler de monter votre propre SAN avec multipath sans avoir à manger des pâtes pendant la prochaine décennie.

Rappel

On part du besoin de pouvoir exécuter des machines virtuelles sur plusieurs hyperviseurs, et de pouvoir interchanger ces hyperviseurs comme bon nous semble, à chaud comme à froid, et de pouvoir parer rapidement à une défaillance de l'un d'eux.

En conséquence, d'un point de vue purement stockage, on a deux choses à gérer :

  • Mise à disposition d'un point de stockage bloc unique à toutes les machines
  • Partitionnement de ce stockage avec gestion de verrous

Pour le partitionnement, ma solution est de passer par un VG, ou volume group LVM, partagé à l'aide de sanlock. Cela consiste a disposer d'un petit LV (logical volume) appelé lvmlock dédié au sein de ce VG, dont le rôle est simplement de consigner qui utilise quoi afin que les hyperviseurs se se mettre à plusieurs pour modifier une partition et donc très probablement la corrompre. En effet les systèmes de fichiers traditionnels ne supportent pas les accès concurrentiels.

Au moment de l'activation d'un LV sur un hyperviseur, LVM va dire "Attends voir, je vais demander à mon démon (lvmlockd) si j'ai le droit d'utiliser ce LV". lvmlockd peut supporter plusieurs gestionnaires de verrous, dont sanlock, ce qui est notre cas. Il va donc lui demander "Hello, nous sommes l'hyperviseur numéro X, est-ce qu'on peut monter ce LV s'il te plaît ? J'en ai un besoin exclusif si possible", et donc le petit sanlock prend sa redingote, sa bougie et son lorgnon de clerc, et va consulter le registre des verrous maintenu dans le LV lvmlock. Ensuite, deux possibilités :

  • Le petit sanlock trouve un verrou actif sur le LV demandé, et il répond alors, l'air tout penaud : "Je suis vraiment navré, mais ce LV est déjà utilisé par une autre machine, je vous invite donc à réessayer plus tard.". Alors lvmlockd se voit contraint, non sans une certaine pointe de dépit, de devoir annuler l'activation du LV. Ce sont des choses qui arrivent.

  • sanlock ne trouve pas de verrou actif pour ce LV, et il répond tout guilleret : "C'est avec quand plaisir que je vous confirme que vous pouvez activer localement ce LV dès l'instant. Je vais vous inscrire au registre afin de signaler à mes confrères que cette ressources vous est exclusivement allouée jusqu'à nouvel ordre de votre part". Ce qu'il s'empresse de faire avec le très agréable sentiment de travail accompli, comme seuls les petits processus peuvent le faire.

Lorsque lvmlockd revient voir sanlock à la désactivation du LV, c'est avec le même entrain que sanlock marque le verrou comme relâché dans son grimoire.

Pour les migrations, au lieu d'un verrou exclusif, il est possible de solliciter un verrou partagé, qui a vocation bien sûr de ne pas durer.

La scène décrite a besoin d'un décor, c'est à dire ici d'un point de stockage bloc. Comme les verrous sont gérés au niveau du partitionnement, ce stockage ne s'encombre pas de contrôler qui peut accéder à quoi.

DRBD ou iSCSI ?

Les deux ont des avantages indéniables, ceux de DRBD étant de ne pas nécessiter un équipement tiers (donc moins cher) et de permettre la réplication des données sur les différents serveurs. Cependant un gros soucis de DRBD... c'est qu'il n'est pas fait pour faire du multi-master infini. La réplication devient vite très instable et j'ai eu plusieurs séances d'arrachage des derniers cheveux qu'il me reste à devoir réparer un split-brain, ce cas où la synchronisation est par terre et les serveurs se mettent à écrire sur le stockage indépendamment, et la réconciliation est très très compliquée. Personnellement je n'ai jamais eu la chance de pouvoir les réconcilier, j'ai du à chaque fois reconstruire la réplication en invalidant l'un de mes serveurs et en lui faisant recopier son copain.

J'en ai eu assez de gérer ça (et les accros de DRBD me diront qu'ils me l'avaient bien dit et que c'est bien fait pour ma tronche). J'ai donc songé à la possibilité de changer de camp et de disposer d'un SAN, et face au coup exorbitant de ces bestioles, m'en faire un moi-même avec les moyens du bord. J'avais en effet un NUC dans un coin et une baie Thunderbolt remplie de disques, le tout représentait mon ancien NAS, avant que j'achète un HP ProLient MicroServer d'occasion pour le remplacer.

Optimisation réseau

Mon principal frein était la vitesse du réseau. Pour doter mes VMs de disques acceptable, pensais-je, il me faut au moins une connexion 10 Gbps, sinon je me verrais contraint de limiter le nombre de VMs. Ça me rendait un peu triste, mais je gardais le projet dans un coin de ma tête.

Aggrégation de liens, une fausse bonne idée

Pendant une pause de midi, j'en ai parlé à un collègue de bureau qui a une longue expérience à la fois en stockage industriel et en auto-hébergement. Je lui ai fait part de mes craintes concernant le réseau, à quoi il m'a répondu ces paroles imbibées de sagesse :

Bah t'as qu'à faire de l'aggrégation de liens 1 Gbps, et même deux liens ça fera bien l'affaire.

J'ai trouvé l'idée très bonne et j'ai tenté le truc. Mon switch de coeur permet justement de faire de l'aggrégation. Mon NUC n'ayant qu'une seule carte réseau, j'ai résolu le problème en achetant deux simples adaptateurs USB-3 vers RJ45. Malheureusement j'ai appris que l'aggrégation de liens ne permet pas de multiplier le débit pour le genre de flux iSCSI. En effet, le principe de l'aggrégation est de répartir les paquets sur les différents liens selon plusieurs stratégies possibles (round-robin, équilibrage de charge, hashage...), mais une connexion TCP donnée sera toujours sur le même lien, et c'est précisément le cas d'iSCSI où il n'est question que d'une seule connexion TCP.

Cela dit, l'expérience n'était pas dénuée d'intérêt, et en cherchant j'ai trouvé une idée encore meilleure. Pour augmenter les performances d'un SAN, la meilleure stratégie n'est pas l'aggrégation de liens, mais la multiplication de réseaux de sockage, c'est ce qu'on appelle le multipath.

Le multipath

Faire du SAN multipath consiste à utiliser plusieurs chemins réseaux pour atteindre un point de stockage. Pour cela, le SAN doit disposer de plusieurs interfaces réseaux, chacune connectée à son propre réseau dédié. Dans l'idéal on disposerait de switchs séparés sur chacun desquels chaque hyperviseur aurait un lien. Cela permet à la fois la multiplication effective de la vitesse de tranfert et la résilience à une panne matérielle.

En pratique je n'ai pas de switch dédié. On se passera donc pour l'instant de la résilience matérielle. J'ai donc dédié deux VLANs dédiés auxquels j'ai assigné des ports de mon switch (en mode access) ce qui correspond à deux petits switchs isolés logiquement.

Du côté du NUC, j'ai trois interface, celle d'administration eth0 intégrée au NUC et mes deux adaptateurs USB eth1 et eth2 qui seront utilisés pour les deux réseaux de stockage. J'assigne l'adresse 10.20.5.254/24 à eth1 et 10.20.6.254/24 à eth2. Maintenant il me faut exposer le stockage sur ces deux interfaces. Le noyau Linux dispode d'un module de partage de targets SCSI contrôllable avec un outil de l'espace utilisateur appelé targetcli. Le wiki Alpine Linux dispose d'un très bon guide pour sa mise en place. Dans mon cas, j'ai créé mon backstore sur un montage RAID5 (explications dans un de mes précédents articles) avec la commande suivante :

/backstores/blockio> create block0 /dev/md0

J'ai également créé non pas un portal mais deux, avec les commandes suivantes :

/iscsi/iqn.20.../tpg1/portals> create 10.20.5.254 3260
/iscsi/iqn.20.../tpg1/portals> create 10.20.6.254 3260

Ce qui me donne au final un truc comme ça :

/> ls
o- / ......................................................................................................................... [...]
  o- backstores .............................................................................................................. [...]
  | o- block .................................................................................................. [Storage Objects: 1]
  | | o- block0 ........................................................................... [/dev/md0 (8.2TiB) write-thru activated]
  | |   o- alua ................................................................................................... [ALUA Groups: 1]
  | |     o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized]
  | o- fileio ................................................................................................. [Storage Objects: 0]
  | o- pscsi .................................................................................................. [Storage Objects: 0]
  | o- ramdisk ................................................................................................ [Storage Objects: 0]
  o- iscsi ............................................................................................................ [Targets: 1]
  | o- iqn.2003-01.org.linux-iscsi.nuc.x8664:sn.a8ff70fddc94 ............................................................. [TPGs: 1]
  |   o- tpg1 .................................................................................................. [gen-acls, no-auth]
  |     o- acls .......................................................................................................... [ACLs: 0]
  |     o- luns .......................................................................................................... [LUNs: 1]
  |     | o- lun0 ..................................................................... [block/block0 (/dev/md0) (default_tg_pt_gp)]
  |     o- portals .................................................................................................... [Portals: 2]
  |       o- 10.20.5.254:3260 ................................................................................................. [OK]
  |       o- 10.20.6.254:3260 ................................................................................................. [OK]
  o- loopback ......................................................................................................... [Targets: 0]
  o- vhost ............................................................................................................ [Targets: 0]
  o- xen-pvscsi ....................................................................................................... [Targets: 0]

Du côté des hyperviseurs, j'ai déjà assez d'interfaces réseau, pas besoin d'adaptateurs.

eth1 eth2
serveur-1 10.20.5.1/24 10.20.6.1/24
serveur-2 10.20.5.2/24 10.20.6.2/24

Connectons nous maintenant à chacune des cibles exposées :

~# iscsiadm  -m discovery -t st -p 10.20.5.254
10.20.5.254:3260,1 iqn.2003-01.org.linux-iscsi.nuc.x8664:sn.a8ff70fddc94
10.20.6.254:3260,1 iqn.2003-01.org.linux-iscsi.nuc.x8664:sn.a8ff70fddc94
~# iscsiadm  -m node  --targetname "iqn.2003-01.org.linux-iscsi.nuc.x8664:sn.a8ff70fddc94" --portal "10.20.5.254:3260" --login
~# iscsiadm  -m node  --targetname "iqn.2003-01.org.linux-iscsi.nuc.x8664:sn.a8ff70fddc94" --portal "10.20.6.254:3260" --login

Un petit lsblk confirme que deux volumes ont été ajoutés.

Maintenant il s'agit de dire à l'OS que ces deux disques sont en fait deux points d'accès au même stockage. Pour cela il faut installer le démon multipath. J'ai suivi cette doc qui explique coment l'utiliser sous debian, mais ça doit être valable partout.

Je me retrouve avec ce résultat :

~# multipath -l
mpatha (360014053221815080c24cdc938f1f56a) dm-25 LIO-ORG,block0
size=8.2T features='0' hwhandler='1 alua' wp=rw
`-+- policy='round-robin 0' prio=0 status=active
  |- 6:0:0:0 sde 8:64 active undef running
  `- 7:0:0:0 sdf 8:80 active undef running

~# lsblk
sde                                  8:64   0   8,2T  0 disk  
└─mpatha                           253:25   0   8,2T  0 mpath 
sdf                                  8:80   0   8,2T  0 disk  
└─mpatha                           253:25   0   8,2T  0 mpath 

Mon volume est alors présenté sur /dev/mpatha, libre à moi maintenant d'en faire un support pour LVM. Et la procédure reprend son cours.

Conclusion

Il n'y a pas besoin d'un réseau de fou pour faire du SAN avec des VMs aux IOs raisonnables. De plus, après quelques tests à base de dd, je me suis rendu compte que les performances étaient meilleures sur mon SAN maison que sur ma partition de disque locale en cluster DRBD. En fait, ce n'est ici clairement pas le réseau qui limite, mais la vitesse de mes disques durs mécaniques.


J'ai mis en place un compte sur le Fediverse pour Hashtagueule, n'hésitez pas à suivre et à commenter là-bas ainsi que sur notre salon Matrix.