Un système de fichiers haute disponibilité avec GlusterFS !

GNU/Linux Magazine n° 209 | novembre 2017 | Julien Morot
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
GlusterFS est un système de fichiers réseau client/serveur permettant d’agréger différents nœuds de stockage afin de fournir un environnement NAS hautement disponible.

1. Présentation

1.1 Pour quoi faire ?

Admettons que j’ai une application web lambda, je vais pouvoir déployer plusieurs instances Apache ou Nginx qui se trouveront derrière un équilibreur de charge, lui-même hautement disponible. Sur chaque instance de serveur web, il me sera facile de déployer l’application. Toutefois chaque instance aura besoin d’accéder à des fichiers communs, générés ou non par l’application. Bien souvent, je vais rencontrer dans ce cas un serveur NFS qui va donc lui-même constituer un point de faiblesse dans l’architecture.

Gluster permet de mettre en cluster plusieurs nœuds de stockage (a minima deux), ce qui permet de répondre à deux problématiques majeures dès qu’une application a besoin de pouvoir monter en charge : la parallélisation et la réplication du stockage. Pour fournir ces fonctionnalités sur un volume, une « brick » en langage Gluster, le système s’appuie sur des systèmes de fichiers traditionnels, XFS ou EXT4 au-dessus d’un périphérique en mode bloc (partition, LVM, RAID, etc.). Gluster travaille donc principalement au niveau fichier.

Contrairement à un certain nombre d’autres systèmes de fichiers de ce type, Gluster offre l’immense avantage de ne pas nécessiter de serveur de métadonnées pour fonctionner. De fait, cette absence ne constitue pas un point de faiblesse ou un élément supplémentaire à maintenir dans l’infrastructure de stockage. De plus, chaque fois que l’on ajoute un nœud au cluster, le système devient plus performant et l’augmentation de la performance est linéaire avec l’extension de l’infrastructure. Dernier point pour mettre en évidence cette simplicité de conception, il n’existe pas de notion de maître ou d’esclave avec GlusterFS.

1.2 Les volumes GlusterFS

Un volume est une agrégation de plusieurs bricks réparties sur différents nœuds de stockage. Le choix du type de volume se fait en fonction des attentes de performances, de sécurité ou de la combinaison des deux. Voyons les types de volumes standards, sachant qu’il existe des modes géorépliqués, strippés ou basés sur l’erasure coding pour des workflows spécifiques.

1.2.1 Volume distribué

Ce mode est le mode par défaut de GlusterFS. Les fichiers sont répartis sur l’ensemble des bricks du volume sans redondance aucune. Par conséquent, lors de la perte d’un nœud, les données de celui-ci sont perdues et il faudra se baser sur des mécanismes complémentaires pour assurer la reprise après incident. La volumétrie utile est celle de l’ensemble des nœuds du cluster. Ce mode permet une croissance aisée de la volumétrie en ajoutant simplement des nœuds au volume. Il faut donc au minimum deux nœuds, et la distribution peut se faire sur autant de nœuds du cluster (voir figure 1).

Fig. 1 : Volume distribué.

1.2.2 Volume répliqué

Ce mode permet de répondre au problème de la sécurité de la donnée posé par le mode distribué. Dans ce mode opératoire, le système maintient n copies de chaque fichier au sein des « bricks » spécifiées. Il faut donc autant de nœuds au cluster que de réplicas désirés. De la même façon que sur du RAID1, la volumétrie utile est la moitié de la volumétrie allouée (voir figure 2).

Fig. 2 : Volume répliqué.

1.2.3 Volume distribué répliqué

Vous l’aurez compris, ce mode est une combinaison des deux modes précédents. Cela permet de traiter des workflows nécessitant disponibilité et capacité à monter en charge. Le nombre de bricks nécessaires est un multiple du niveau de réplication attendu. De plus, la réplication entre les bricks est définie par leur ordre de déclaration à la création du volume. Pour quatre bricks avec deux réplicas, les deux premières bricks répliquent ensemble et de même pour les deux suivantes (voir figure 3). Si nous souhaitions quatre réplicas, il nous faudrait donc huit bricks et les quatre premières répliqueraient entre elles.

Fig. 3 : Volume distribué répliqué.

2 Un peu de pratique

2.1 L’environnement

GlusterFS est assez agnostique par rapport à l’environnement et à la distribution. Pour ma part, les démonstrations suivantes seront toutes réalisées sous Ubuntu 16.04. Un point indispensable étant que les nœuds soient capables de discuter par leur nom, qu’il soit résolu par DNS ou par le fichier hosts, mais pas par adresse IP. Pour la suite, je partirai sur deux VMs stor0 et stor1 afin de monter un cluster à deux nœuds.

Chaque serveur dispose d’un second disque virtuel de 10 Gio pour la démonstration. Le périphérique doit par contre impérativement disposer d’un système de fichiers supportant les attributs étendus, ext4 ou XFS sachant qu’XFS est de loin le système de fichiers recommandé. La convention de nommage veut, mais n’impose pas, que les données soient placées dans /data/glusterfs/volume/brick. La création des volumes peut se faire simplement comme ceci :

$ apt-get -y install lvm2 acl attr xfsprogs

$(echo o; echo n; echo p; echo 1; echo ; echo; echo t; echo 8E; echo w) | fdisk /dev/sdb

$ pvcreate  /dev/sdb1

$ vgcreate VG-Brick0 /dev/sdb1

$ lvcreate  -l 100%VG -n LV-Brick0 VG-Brick0

$ mkfs.xfs -i size=512 -L Brick0 /dev/VG-Brick0/LV-Brick0

$ mkdir -p /data/glusterfs/vol0/

$ echo "/dev/VG-Brick0/LV-Brick0 /data/glusterfs/vol0/ xfs defaults 1 2" >> /etc/fstab

$ mount /data/glusterfs/vol0/

$ mkdir /data/glusterfs/vol0/brick0

Le LVM n’est pas obligatoire, mais s’y tenir permet d’avoir les bons réflexes pour de la production. Pour le reste, l’installation des packages est très simple, cette simple commande suffit :

$ apt-get -y install glusterfs-server

Par souci de simplification, aucun pare-feu n’est activé sur les différentes machines. Point de vigilance, il ne faudra pas cloner les machines avec le disque de données supplémentaire.

2.2 Le trusted pool

Avant d’être en mesure de gérer des volumes de stockage, les membres d’un cluster GlusterFS doivent se reconnaître entre eux et faire partie d’un même trusted pool. Tant que cette opération n’est pas réalisée, il n’est pas possible pour un hôte de joindre le réseau de stockage. Pour cela, c’est très simple, il suffit depuis un nœud de sonder avec la commande gluster peer probe et d’ajouter les autres nœuds :

root@stor1:~# gluster peer probe stor0

peer probe: success.

Pour vérifier :

root@stor1:~# gluster peer status

Number of Peers: 1

Hostname: stor0

Uuid: a920b020-9e5a-46f6-b073-1cc8ec00ba0e

State: Peer in Cluster (Connected)

2.3 Un volume répliqué

On va poursuivre notre itinéraire au sein de GlusterFS en créant un volume répliqué à deux nœuds. J’ai donné en introduction un exemple basé sur des attentes de haute disponibilité du stockage, il me semble pertinent de poursuivre sur cet exemple qui parlera sans doute davantage. Notre cluster ayant deux nœuds, avec conservation de deux copies, cela nous fait donc un système en miroir. Sur chaque serveur, on indique le dossier dans lequel se trouvent les données. Par sécurité, il est préconisé de créer le volume dans un sous-répertoire du point de montage afin qu’en cas d’échec de montage du volume, cela n’ait pas d’incidence sur la réplication gluster. Du fait du risque d’avoir un dossier vide sur un membre du cluster lors du démarrage des services, le comportement ne serait pas forcément prévisible.

root@stor1:~# gluster volume create repl-vol replica 2 transport tcp stor0:/data/glusterfs/vol0/brick0/ stor1:/data/glusterfs/vol0/brick0/

volume create: repl-vol: success: please start the volume to access data

root@stor1:~# gluster volume start repl-vol

volume start: repl-vol: success

On peut donc vérifier que tout est en ordre avec la commande ci-dessous. Le volume doit être marqué comme online sur l’ensemble des nœuds :

root@stor1:~# gluster volume status

Status of volume: repl-vol

Gluster process                             TCP Port  RDMA Port  Online  Pid

------------------------------------------------------------------------------

Brick stor0:/data/glusterfs/vol0/brick0     49152     0          Y       2536

Brick stor1:/data/glusterfs/vol0/brick0     49152     0          Y       2338

NFS Server on localhost                     2049      0          Y       2359

Self-heal Daemon on localhost               N/A       N/A        Y       2364

NFS Server on stor0                         2049      0          Y       2557

Self-heal Daemon on stor0                   N/A       N/A        Y       2562

Task Status of Volume repl-vol

------------------------------------------------------------------------------

There are no active volume tasks

2.4 Connexion d’un client

Il existe trois mécanismes d’accès principaux côté client :

- le client natif accédé au travers de FUSE, le système permettant de créer des pilotes de filesystem au niveau userland. Il suffit pour cela d’installer les packages nécessaires.

- via NFS, Gluster implémentant nativement le support NFS. Si vous avez été vigilant lors de l’installation du package glusterfs-server, vous avez sûrement remarqué certaines dépendances. Le serveur NFS n’est pas activé par défaut cependant.

- en CIFS, avec un serveur Samba.

Dans les deux derniers cas, il est souhaitable d’associer les serveurs à un système de type CTDB pour fournir de la haute disponibilité. NFS et Samba ne savent en effet pas tirer parti de l’ensemble des fonctionnalités contrairement au client natif. Connectons donc un premier client :

$ apt-get -y install glusterfs-client

$ mkdir /data

$ mount -t glusterfs stor1:/repl-vol /data

Créons un fichier aléatoire avec par exemple la commande ci-dessous.

$ dd if=/dev/urandom of=/data/toto bs=1024 count=10240

Pour confirmer que la réplication est fonctionnelle, il suffit de vérifier avec une simple commande ls que le fichier est présent sur les bricks de chacun des deux serveurs GlusterFS :

$ ls -l /data/glusterfs/vol0/brick0/toto

-rw-r--r-- 2 root root 10485760 sept.  9 19:58 /data/glusterfs/vol0/brick0/toto

Un point qui a dû vous surprendre est la commande de montage. On a en effet explicitement spécifié l’un des serveurs alors que l’on est censé avoir déployé un stockage hautement disponible. En pratique, le client natif glusterfs ne fait que récupérer lors de la commande de mount les informations de configuration du cluster. Il communiquera directement avec l’ensemble des serveurs définis dans les volfile (dans le répertoire /var/lib/glusterd/vols/repl-vol sur les nœuds de stockage). Un bon moyen de vérifier est d’arrêter le nœud vers lequel on a réalisé le montage (un halt -p sur stor1 dans ce cas) : le client doit continuer à fonctionner. Côté client, la perte de connexion doit être visible dans le fichier /var/log/glusterfs/data.log.

[2017-09-09 18:01:18.933835] W [socket.c:588:__socket_rwv] 0-glusterfs: readv on 192.168.69.61:24007 failed (Aucune donnée disponible)

[2017-09-09 18:01:37.954070] W [socket.c:588:__socket_rwv] 0-repl-vol-client-1: readv on 192.168.69.61:49152 failed (Connexion terminée par expiration du délai d'attente)

Un point que vous aurez noté également, c’est que la bascule n’est pas immédiate. En pratique, le délai est de 42 secondes. Pour ramener ce délai à une valeur plus raisonnable de 5 secondes, modifions notre nœud comme suit :

root@stor1:~# gluster volume set repl-vol network.ping-timeout 5

volume set: success

Ce changement est tracé dans le log /var/log/glusterfs/glustershd.log avec une ligne par nœud comme celle-ci :

[2017-09-09 18:33:19.591108] I [rpc-clnt.c:1823:rpc_clnt_reconfig] 0-repl-vol-client-0: changing ping timeout to 5 (from 42)

2.5 Un brin de sécurité

Jusqu’ici, on a pu monter le volume simplement en contactant l’un des serveurs du pool GlusterFS, mais aucune sécurité supplémentaire n’a été imposée. Il est possible de restreindre l’accès à notre volume en définissant une ACL similaire à ce qui existe en NFS via le fichier /etc/exports.

root@stor2:~# gluster volume set repl-vol auth.allow 192.168.69.104

volume set: success

Il est également possible de définir une wildcard, par exemple 192.168.69.* afin d’autoriser tout un réseau. Dans cet exemple, nous avons autorisé explicitement une adresse IP à se connecter au volume.

Nous aurions également pu autoriser un nom d’hôte ou plusieurs adresses IP ou noms séparés par des virgules. Le fait de définir l’attribut auth.allow a comme effet immédiat d’interdire toutes les autres machines qui n’ont pas été explicitement autorisées. Pour revenir au comportement par défaut, il faut autoriser le caractère wildcard (*) tout simplement. À l’inverse, l’attribut auth.reject n’interdit aucune machine par défaut (auth.reject avec comme valeur NONE). Il sert comme vous l’avez deviné à interdire explicitement une machine. Pour résumer, le contrôle d’accès a une logique similaire avec ce qui existe côté TCP Wrappers.

2.6 Une corbeille sur le volume

GlusterFS sait gérer une corbeille au niveau volume pour conserver les fichiers supprimés. Le dossier est créé automatiquement par gluster et ne peut être supprimé. Fait intéressant, gluster sait si on le lui dit, tirer parti de cette corbeille pour ses opérations internes. Activons donc une corbeille pour les fichiers de moins de 10 Mio :

$ gluster volume set repl-vol features.trash on

$ gluster volume set repl-vol features.trash-dir "Corbeille"

$ gluster volume set repl-vol features.trash-max-filesize 10485760

$ gluster volume set repl-vol features.trash-internal-op on

2.7 Node HS ? Pas de panique !

Un incident majeur sur un équipement sensible d’un système d’information, c’est bien entendu quelque chose auquel on se doit d’être préparé. Dans un système hautement disponible, tout élément qui n’est pas considéré comme un point unique de défaillance (SPOF) doit pouvoir être indisponible sans impacter fortement le bon fonctionnement du système. Nous nous retrouvons dans un état de fonctionnement dégradé. Si le système défaillant ne peut être dépanné, un processus de reconstruction doit être mis en œuvre.

Nous allons considérer que le nœud stor0 est irrémédiablement défaillant, la VM est même supprimée. Cela se vérifie par la commande suivante :

root@stor1:~# gluster volume heal repl-vol info

Brick stor0:/data/glusterfs/vol0/brick0

Status: Noeud final de transport n'est pas connecté

Brick stor1:/data/glusterfs/vol0/brick0

Number of entries: 0

Voyons étape par étape comment le nouveau serveur nommé stor2 va prendre de relais de celui-ci. Pour cela, la première étape que je ne vais pas détailler consiste à provisionner un nouveau serveur avec le disque de données et les dépendances comme indiqué précédemment.

Premièrement, on ajoute le nouveau nœud et on va confirmer qu’on a bien un nouveau nœud présent, et un ancien toujours connu du cluster, mais manquant :

root@stor1:~# gluster peer probe stor2

peer probe: success.

root@stor1:~# gluster peer status

Number of Peers: 2

Hostname: stor0

Uuid: a920b020-9e5a-46f6-b073-1cc8ec00ba0e

State: Peer in Cluster (Disconnected)

Hostname: stor2

Uuid: f2a03465-11bb-4c2a-a882-22933cfa2d08

State: Peer in Cluster (Connected)

Remplaçons maintenant la brick du stor0 par celle de notre nouveau serveur stor2 et vérifions son état de santé :

root@stor1:~# gluster volume replace-brick repl-vol stor0:/data/glusterfs/vol0/brick0 stor2:/data/glusterfs/vol0/brick0 commit force

volume replace-brick: success: replace-brick commit force operation successful

On réconcilie le volume :

root@stor1:~# gluster volume heal repl-vol full

Launching heal operation to perform full self heal on volume repl-vol has been successful

Use heal info commands to check status

root@stor1:~# gluster volume heal repl-vol info

Brick stor2:/data/glusterfs/vol0/brick0

Number of entries: 0

Brick stor1:/data/glusterfs/vol0/brick0

Number of entries: 0

Et depuis le nouveau node, lançons une synchronisation :

root@stor2:/data/glusterfs/vol0/brick0# gluster volume sync stor1 repl-vol

Sync volume may make data inaccessible while the sync is in progress. Do you want to continue? (y/n) y

Il nous reste une dernière étape : répliquer le volume id dans les attributs étendus du système de fichiers et le propager au second serveur. Pour le récupérer, il faut lancer la commande suivante :

root@stor1:~# getfattr  -n trusted.glusterfs.volume-id /data/glusterfs/vol0/brick0/

getfattr: Suppression des « / » en tête des chemins absolus

# file: data/glusterfs/vol0/brick0/

trusted.glusterfs.volume-id=0seEhN1zXZTFOXmRGV92ibvw==

Sur le nouveau serveur, on applique l’ID du volume sur la brick :

root@stor2:/data/glusterfs/vol0/brick0# setfattr -n trusted.glusterfs.volume-id -v '0seEhN1zXZTFOXmRGV92ibvw==' /data/glusterfs/vol0/brick0/

service glusterfs-server restart

La configuration de notre volume est bien mise à jour comme on peut le voir ci-dessous. Dans le cadre d’un volume distribué, il faudrait lancer un rééquilibrage (rebalance) du volume :

root@stor1:~# gluster volume info repl-vol

Volume Name: repl-vol

Type: Replicate

Volume ID: 1c493043-9c2d-4be6-afcd-8512577342c9

Status: Started

Number of Bricks: 1 x 2 = 2

Transport-type: tcp

Bricks:

Brick1: stor2:/data/glusterfs/vol0/brick0

Brick2: stor1:/data/glusterfs/vol0/brick0

Options Reconfigured:

performance.readdir-ahead: on

cluster.self-heal-daemon: enable

network.ping-timeout: 5

Enfin, il ne reste plus qu’à retirer l’ancien nœud des peers autorisés dans le trusted pool :

root@stor1:~# gluster peer detach stor0

peer detach: success

root@stor1:~# gluster peer status

Number of Peers: 1

Hostname: stor2

Uuid: f2a03465-11bb-4c2a-a882-22933cfa2d08

State: Peer in Cluster (Connected)

Il ne doit plus apparaître dans la liste des nœuds :

root@stor1:~# gluster pool list

UUID                                    Hostname        State

0cb0f3b6-10e5-41c4-ad7e-cb9ca794db9e    stor2           Connected

d5ff9617-6989-48ae-be3a-3e1286060ea1    localhost       Connected

Vous savez désormais comment remplacer un nœud défaillant, sachant que ce processus s’applique également en cas de migration de la brick de stor0 vers un nouveau serveur.

2.8 Étendre le volume

Quand l’espace disque commence à manquer, une première solution peut être d’étendre l’espace libre sur les bricks, d’où l’intérêt d’être parti au départ sur du LVM. Une autre solution est d’étendre le cluster avec de nouveaux nœuds afin d’améliorer la disponibilité du système dans son ensemble. Cette extension du cluster se fait en outre sans interruption de service.

Pour étendre un cluster répliqué, il faut ajouter un nombre de bricks avec un nombre multiple du nombre de réplicas. Nous avons monté un volume à deux réplicas, il nous faut donc ajouter deux bricks supplémentaires. La commande gluster volume info repl-vol nous permet de le confirmer (1x2). Nous allons ajouter donc deux serveurs stor3 et stor4, avec le volume disque préparé et le package glusterfs-server installé.

La première étape consiste à autoriser les deux nœuds avec la commande gluster peer probe vue précédemment. On peut donc ensuite ajouter des bricks au volume en spécifiant les bricks de nos deux nouveaux serveurs :

root@stor2:~# gluster volume add-brick repl-vol stor3:/data/glusterfs/vol0/brick0 stor4:/data/glusterfs/vol0/brick0

volume add-brick: success

Vérifions notre volume, nous devons retrouver nos deux bricks supplémentaires.

root@stor2:~# gluster volume info repl-vol

Volume Name: repl-vol

Type: Distributed-Replicate

Volume ID: 78484dd7-35d9-4c53-9799-1195f7689bbf

Status: Started

Number of Bricks: 2 x 2 = 4

Transport-type: tcp

Bricks:

Brick1: stor2:/data/glusterfs/vol0/brick0

Brick2: stor1:/data/glusterfs/vol0/brick0

Brick3: stor3:/data/glusterfs/vol0/brick0

Brick4: stor4:/data/glusterfs/vol0/brick0

Options Reconfigured:

performance.readdir-ahead: on

Notre volume à deux réplicas comportant quatre nœuds se comporte donc désormais comme un volume distribué répliqué par la magie de l’extension du volume. Seul problème, il n’y a aucune donnée sur les serveurs stor3 et stor4, ce qui n’a pas eu pour effet de libérer de l’espace disque sur les deux premiers serveurs. Il est donc nécessaire de répartir la volumétrie sur l’ensemble des bricks qui composent le volume :

root@stor2:~# gluster volume rebalance repl-vol start

volume rebalance: repl-vol: success: Rebalance on repl-vol has been started successfully. Use rebalance status command to check status of the rebalance process.

ID: dffbed2e-3a0c-4d7d-9f43-9d978a546b04

Pour vérifier, il suffit de lancer la même commande avec le paramètre status :

root@stor2:~# gluster volume rebalance repl-vol status

                                    Node Rebalanced-files          size       scanned      failures       skipped               status   run time in secs

                               ---------      -----------   -----------   -----------   -----------   -----------         ------------     --------------

                               localhost                5        0Bytes            10             0             0            completed               2.00

                                   stor1                0        0Bytes             0             0             0            completed               1.00

                                   stor3                0        0Bytes             2             0             0            completed               1.00

                                   stor4                0        0Bytes             0             0             0            completed               1.00

volume rebalance: repl-vol: success

2.9 Les quotas

GlusterFS dispose d’un mécanisme permettant de définir des quotas au niveau dossier. Ils ne sont pas activés par défaut. Pour changer ce comportement :

root@stor2:~# gluster volume quota repl-vol enable

volume quota : success

Nous allons appliquer une limite à 1Gio sur le sous-dossier subdir de notre volume. Ce dossier devra avoir été impérativement créé depuis le client glusterfs ajouté précédemment. Pour créer ce quota :

root@stor2:~# gluster volume quota repl-vol limit-usage /subdir 1GB

volume quota : success

Si nous avions souhaité créer un quota au niveau du volume, il suffit d’indiquer / dans le chemin. Créons un fichier approchant le quota depuis notre client GlusterFS :

root@desktop:/data/subdir# dd if=/dev/zero of=/data/subdir/toto bs=1024 count=1024000

1024000+0 enregistrements lus

1024000+0 enregistrements écrits

1048576000 bytes (1,0 GB, 1000 MiB) copied, 223,044 s, 4,7 MB/s

Et voyons l’état du quota :

root@stor1:~# gluster volume quota repl-vol list

                  Path                   Hard-limit  Soft-limit      Used  Available  Soft-limit exceeded? Hard-limit exceeded?

-------------------------------------------------------------------------------------------------------------------------------

/subdir                                    1.0GB     80%(819.2MB) 1000.0MB  24.0MB             Yes                   No

Reprenons notre commande précédente, en créant un fichier au nom différent, la création est bien interrompue sur le dépassement de quota hard :

root@desktop:/data# dd if=/dev/zero of=/data/subdir/tata bs=1024 count=1024000

dd: erreur d'écriture de '/data/subdir/tata': Débordement du quota d'espace disque

dd: fermeture du fichier de sortie '/data/subdir/tata': Débordement du quota d'espace disque

Conclusion

Il est difficile d’être exhaustif sur un sujet aussi vaste. J’espère toutefois avoir aiguisé votre appétit sur GlusterFS et vous avoir donné l’envie de tester ce qui n’a pas été détaillé ici.