SUSE Linux HA : Cluster NFS avec DBRD et Pacemaker
Est-ce une bonne idée ?
Le problème initial était simple : J'ai besoin d'une solution pour la persistence des données de mon cluster Kubernetes.
La sélection d'une solution de stockage doit bien entendu faire l'objet d'une longue réflexion et je pourrais écrire longuement sur le sujet. D'ailleurs, lorsque ce sera le cas, j'ajouterai un lien >>>> ici <<<< .
Sans rentrer dans les détails techniques, le contexte dans lequel évoluait la DSI imposait des contraintes rendant l'intégration à une infrastructure de stockage type vSAN impossible, et compliquait fortement le déploiement d'une solution de stockage distribuée.
Le besoin restant très modeste :
- I/O disque faible
- Volumétrie limitée
- Nécessite cependant un mode d'écriture en ReadWriteMany (RWX),
Le choix s'est donc porté sur l'alternative la plus simple : NFS, qui était déjà utilisé lors des phases de test.
NFS est une technologie qui date de 1984, on fait difficilement plus mature. Elle n'a cependant que peu évoluée fonctionnellement, il s'agit principalement d'un protocole de partages de fichiers, avec les performances limitées que cela entraîne.
NFS a cependant l'avantage de supporter RWX.
Le choix s'est porté à un moment entre SMB/CIFS et NFS, mais le protocole de Microsoft bien plus lourd et contraignant n'a pas été retenu.
Mais que se passe-t-il en cas de crash ?
NFS ne propose pas nativement de fonctionnalité de haute-disponibilité. Il ne s'agit que d'un protocole d'échange qui opère au niveau du filesystem, le HA s'opère donc au niveau de l'hôte hébergeant le partage.
Il est aujourd'hui possible d'utiliser un stockage de type NAS avec plusieurs contrôleurs, une gestion de volume interne à la solution de stockage avec généralement des fonctionnalités de snapshots.
L'alternative est de mettre en place un serveur NFS sous Linux et de le redonder avec une technologie de cluster, on a alors deux solutions, tout devant fonctionner sur VMWare :
- Un cluster basculant une LUN entre plusieurs serveurs
- Un volume est rattaché aux noeuds du clusters au travers de RDM.
- Un volume group LVM est créé sur la LUN
- Seul le noeud actif active le VG.
- En cas de basculement, on désactive / réactive la ressource sur un noeud valide. Ici ressource = VIP + LUN
- Un cluster répliquant ses données entre plusieurs serveurs.
- Chaque cluster a son VMDK secondaire
- Un volume group LVM est créé sur la LUN
- On déploie DRBD pour synchroniser les données sur les deux VG.
- En cas de perte du noeud actif, un autre noeud reprend, ce qui se traduit par un basculement de l'IP et un passage de la ressource DRBD en primary.
Il existe des solutions de cluster permettant de faire de l'actif/actif, mais elles se basent généralement sur des solutions de stockage distribuée... On tourne en rond.
Mais est-ce une bonne idée ?
NFS est la solution de dernier recours.
Si aucune autre option ne se présente, alors autant la redonder.
En revanche, est-ce qu'ajouter une solution aussi complexe que DRBD + Pacemaker est judicieuse ? Le HA d'un hyperviseur ne suffirait-il pas ?
Si l'on considère qu'il s'agit du stockage utilisé par l'ensemble des workloads Kubernetes, une interruption de production pour maintenance n'est pas envisageable, on doit donc être en mesure d'interrompre un noeud sans impacter toutes les applications connectées.
Alors oui, dans ce contexte précis, c'est une idée valable.
Déploiement
SUSE Linux Enterprise High Availability Extension
Pacemaker est le service de HA proposé sur SUSE Linux. Ces paquets sont soumis à souscription et font partie du pattern ha_sles.
Veillez donc à enregistrer le serveur et activer l'extension.
- Lancer la commande yast registration
- Sélectionner Register again
- La ligne est à cocher est SUSE Linux Enterprise High Availability Extension 15...
- Une fois sur l'écran suivant, entrez le code que vous avez récupérer au préalable sur scc.suse.com
Cette opération ajoutera les dépôts requis sur le serveur.
Dans le cas où vous utilisez SUSE Manager pour gérer vos dépôts, il faudra alors simplement affecter les bon canaux au serveur, en les ayant au préalable synchroniser.
Préparer le serveur QNetd
- Sur le serveur QNet, installer le package requis
zypper install corosync-qnetd
Installation des services HA
- Commencer par mettre en place le cluster Pacemaker et les outils nécessaires.
zypper install -t pattern ha_sles
zypper install corosync-qdevice
- Activer le softdog watchdog pour la SBD
echo softdog > /etc/modules-load.d/watchdog.conf
systemctl restart systemd-modules-load
- Initialiser les services clusters
crm cluster init --name nfscluster
- Dans le wizard de configuration qui suit :
- Utiliser l’adresse IP de l’hôte pour le ring0
- Utiliser le port par défaut 5405
- Activer la SBD, mais taper None pour la sélection du périphérique, on définira ici un diskless SBD.
- Définir l’adresse IP virtuelle de Hawk
- Configurer le QDevice/QNetd, l’adresse IP du serveur est celle du serveur qnetd
- Pour le reste, laisser les paramètres par défaut
- Algorithm : ffsplit
- TIE_BREAKER : lowest
- TLS : on
- Heuristic : Vide
- Dans le prompt SSH, saisir le mot de passe ou alternativement faite l’enregistrement des clés au préalable.
- Joindre le second noeud au cluster avec la commande
crm cluster joinSaisir le mot de passe root et laisser la configuration se poursuivre - Une fois le cluster initialisé, sur un des deux noeuds, lancer la commande
corosync-quorumtoolLe résultat devrait indiquer que le nombre de vote est de 3, et le quorum requis de 2.
Préparation du cluster NFS
Commencer par installer les services nfs
zypper install nfs-kernel-server
Créer les volumes LVM
crm cluster run "pvcreate /dev/sdb"
crm cluster run "vgcreate nfs /dev/sdb"
crm cluster run "lvcreate -n test -L 30G nfs"
crm cluster run "lvcreate -n qual -L 30G nfs"
crm cluster run "lvcreate -n prod -L 30G nfs"
crm cluster run "lvcreate -n prodinfra -L 30G nfs"
crm cluster run "lvcreate -n state -L 8G nfs"
crm cluster run "vgchange -ay nfs"
Créer un fichier /etc/drbd.d/nfs.res
resource nfs {
volume 0 {
device /dev/drbd0;
disk /dev/nfs/state;
meta-disk internal;
}
volume 1 {
device /dev/drbd1;
disk /dev/nfs/test;
meta-disk internal;
}
volume 2 {
device /dev/drbd2;
disk /dev/nfs/qual;
meta-disk internal;
}
volume 3 {
device /dev/drbd3;
disk /dev/nfs/prod;
meta-disk internal;
}
volume 4 {
device /dev/drbd4;
disk /dev/nfs/prodinfra;
meta-disk internal;
}
net {
protocol C;
fencing resource-and-stonith;
}
handlers {
fence-peer "/usr/lib/drbd/crm-fence-peer.9.sh";
after-resync-target "/usr/lib/drbd/crm-unfence-peer.9.sh";
}
connection-mesh {
hosts nfs1 nfs2;
}
on vmnfsp101 {
address <nfs1_ip>:7790;
node-id 0;
}
on vmnfsp102 {
address <nfs2_ip>:7790;
node-id 1;
}
}
Copier la configuration sur l’autre noeud avec la commande csync2 -xv
Initialiser drbdadm
crm cluster run "drbdadm create-md nfs"
crm cluster run "drbdadm up nfs"
S’il s’agit de nouveau device, ignorer la synchronisation initiale avec la commande suivante
drbdadm new-current--uid --clear-bitmap nfs/0
drbdadm new-current--uid --clear-bitmap nfs/1
drbdadm new-current--uid --clear-bitmap nfs/2
drbdadm new-current--uid --clear-bitmap nfs/3
drbdadm new-current--uid --clear-bitmap nfs/4
Forcer le premier noeud comme primaire en lançant cette commande
drbdadm primary --force nfs
La commande drbdadm status nfs permet de vérifier l’état de synchronisation
Créer le filesystem sur les devices DRBD
mkfs.ext4 /dev/drbd0
mkfs.xfs /dev/drbd1
mkfs.xfs /dev/drbd2
mkfs.xfs /dev/drbd3
mkfs.xfs /dev/drbd4
Configurer les ressources / primitives Pacemaker pour DRBD
Ouvrir un shell avec la commande crm configure et taper les commandes suivantes
primitive drbd-nfs ocf:linbit:drbd \\
params drbd_resource="nfs" \\
op monitor interval=15 role=Promoted \\
op monitor interval=30 role=Unpromoted
clone cl-drbd-nfs drbd-nfs \\
meta promotable="true" promoted-max="1" promoted-node-max="1" \\
clone-max="2" clone-node-max="1" notify="true" interleave=true
commit
Configurer les ressources du filesystem
Créer la configuration suivante
primitive fs-nfs-state Filesystem \\
params device=/dev/drbd0 directory=/var/lib/nfs fstype=ext4
primitive fs-nfs-test Filesystem \\
params device=/dev/drbd1 directory=/srv/nfs/test fstype=xfs
primitive fs-nfs-qual Filesystem \\
params device=/dev/drbd2 directory=/srv/nfs/qual fstype=xfs
primitive fs-nfs-prod Filesystem \\
params device=/dev/drbd3 directory=/srv/nfs/prod fstype=xfs
primitive fs-nfs-prodinfra Filesystem \\
params device=/dev/drbd4 directory=/srv/nfs/prodinfra fstype=xfs
group g-nfs fs-nfs-state fs-nfs-test fs-nfs-qual fs-nfs-prod fs-nfs-prodinfra
colocation col-nfs-ondrbd inf: g-nfs cl-drbd-nfs:Promoted
order o-drbd-before-nfs Mandatory: cl-drbd-nfs:promote g-nfs:start
commit
Créer la ressource du NFS Kernel Server
Créer la configuration suivante
primitive nfsserver nfsserver \\
params nfs_server_scope=SUSE nfs_shared_infodir="/var/lib/nfs"
modgroup g-nfs add nfsserver
commit
Export des partages NFS
Première méthode - Export avec Pacemaker
Il existe plusieurs méthodes pour exporter les partages NFS, celle proposée par Pacemaker est de définir une primitive de la façon suivante :
primitive exportfs-nfs exportfs \\
params directory="/srv/nfs/share" \\ ## Le répertoire à partager
options="rw,mountpoint" clientspec="192.168.1.0/24" fsid=101 \\ #Les paramètres NFS
op monitor interval=30s timeout=90s
commit
quit
Ici la primitive se nomme exportfs-nfs et on utilise le type de resource exportfs.
clientspec dans la même primitive. Dans le cas d’un subnet cela ne pose pas de problèmes mais si l’on souhaite exporter le partage à plusieurs fqdn / ip, il devient nécessaire de créer une primitive par client. Cela rend la configuration inutilement complexe.Seconde méthode - Exporter avec NFS
La seconde méthode est de configurer l’export NFS localement sur chaque noeud, en modifiant le fichier /etc/exports.
/srv/nfs/test client1.fqdn.xxx(rw,async,wdelay,hide,no_subtree_check,mountpoint,sec=sys,secure,no_root_squash,no_all_squash)
/srv/nfs/test client2.fqdn.xxx(rw,async,wdelay,hide,no_subtree_check,mountpoint,sec=sys,secure,no_root_squash,no_all_squash)
Seul inconvénient, ce fichier doit être modifié sur chaque noeud exportant des volumes NFS.
L’export est actif dès que le service NFS est chargé et il n’y a donc pas besoin de primitive supplémentaire pour réaliser l’export.
Créer une VIP pour le serveur NFS
Pour terminer la configuration du service, on créera une VIP qui suivra les exports NFS et permettra à un serveur externe de garder une connectivité permanente avec le share.
primitive vip-nfs IPaddr2 params ip=x.x.x.x
modgroup g-nfs add vip-nfs
commit
quit
Valider la configuration
Pour visualiser la configuration finale, on peut utiliser la commande crm configure show .
node 1: nfs-1 \\
attributes standby=off
node 2: nfs-2 \\
attributes standby=off
primitive admin-ip IPaddr2 \\
params ip=192.168.10.175 \\
op monitor interval=10 timeout=20
primitive drbd-nfs ocf:linbit:drbd \\
params drbd_resource=nfs \\
op monitor interval=15 role=Promoted timeout=20 \\
op monitor interval=30 role=Unpromoted timeout=20 \\
op start timeout=240 interval=0s \\
op promote timeout=90 interval=0s \\
op demote timeout=90 interval=0s \\
op stop timeout=100 interval=0s
primitive fs-nfs-share Filesystem \\
params device="/dev/drbd1" directory="/srv/nfs/share" fstype=ext4 \\
op monitor timeout=40s interval=20s \\
op start timeout=60s interval=0s \\
op stop timeout=60s interval=0s
primitive fs-nfs-state Filesystem \\
params device="/dev/drbd0" directory="/var/lib/nfs" fstype=ext4 \\
op monitor timeout=40s interval=20s \\
op start timeout=60s interval=0s \\
op stop timeout=60s interval=0s
primitive nfsserver nfsserver \\
params nfs_server_scope=SUSE nfs_shared_infodir="/var/lib/nfs" \\
op monitor timeout=20s interval=10s \\
op start timeout=40s interval=0s \\
op stop timeout=20s interval=0s \\
meta target-role=Started
primitive vip-nfs IPaddr2 \\
params ip=192.168.10.176 \\
op monitor timeout=20s interval=10s \\
op start timeout=20s interval=0s \\
op stop timeout=20s interval=0s
group g-nfs fs-nfs-state fs-nfs-share nfsserver exportfs-nfs vip-nfs
clone cl-drbd-nfs drbd-nfs \\
meta promotable=true promoted-max=1 promoted-node-max=1 clone-max=2 clone-node-max=1 notify=true interleave=true
colocation col-nfs-on-drbd inf: g-nfs cl-drbd-nfs:Promoted
order o-drbd-before-nfs Mandatory: cl-drbd-nfs:promote g-nfs:start
property cib-bootstrap-options: \\
have-watchdog=true \\
dc-version="2.1.5+20221208.a3f44794f-150500.6.14.4-2.1.5+20221208.a3f44794f" \\
cluster-infrastructure=corosync \\
cluster-name=nfscluster \\
stonith-enabled=true \\
stonith-watchdog-timeout=-1 \\
stonith-timeout=95
rsc_defaults build-resource-defaults: \\
resource-stickiness=1 \\
migration-threshold=3 \\
priority=0
op_defaults op-options: \\
timeout=600 \\
record-pending=true
Cette configuration peut être éditer avec crm configure edit qui lancera un éditeur de texte VIM qu’il sera possible de modifier directement, toute sauvegarde du fichier équivaudra alors à un commit.
Exploitation
Etendre un disque
L’extension d’un volume se fait à chaud lorsque la partition est montée. L’opération est similaire à l’extension d’un volume traditionnelle, exemple avec /dev/sdb:
- Forcer le scan
crm cluster run 'echo 1 > /sys/class/block/sdb/device/rescan' - Etendre le PV LVM :
crm cluster run 'pvresize /dev/sdb' - Etendre le volume LVM :
crm cluster run 'lvresize -r -L +5GB /dev/yourvg/yourlv'
Il est fort probable que le redimensionnement ne fonctionne que sur le serveur primary sur la ressource DRBD**.** Dans ce cas, migrer la ressource sur l’autre noeud et procéder à l’extension directement sur le second noeud, sans le wrapper crm cluster run
Ajouter un volume sur DRBD
Dans ce scénario, on ajoute un volume supplémentaire à DRBD depuis un nouveau volume logique LVM. Ce volume peut être sur le même volume group ou sur un autre (sur un autre VMDK par exemple), cela n’a pas d’importance pour DRBD.
Modifier la configuration de DRBD en éditant le fichier /etc/drbd.d/nfs.res
resource nfs {
volume 0 {
device /dev/drbd0;
disk /dev/nfs/state;
meta-disk internal;
}
volume 1 {
device /dev/drbd1;
disk /dev/nfs/share;
meta-disk internal;
}
#Ajouter une section dans la ressource nfs et y déclarer le nouveau volume
volume 3 {
device /dev/drbd2;
disk /dev/nfs/newshare;
meta-disk internal;
}
net {
protocol C;
fencing resource-and-stonith;
}
handlers {
fence-peer "/usr/lib/drbd/crm-fence-peer.9.sh";
after-resync-target "/usr/lib/drbd/crm-unfence-peer.9.sh";
}
connection-mesh {
hosts nfs-1 nfs-2;
}
on nfs-1 {
address 192.168.10.171:7790;
node-id 0;
}
on nfs-2 {
address 192.168.10.172:7790;
node-id 1;
}
}
Créer ensuite le device DRBD
drbdadm create-md nfs/3 ### Ici, le 3 correspond à l'ID du volume
drbdadm adjust nfs ### Cette commande ajoutera le volume supplémentaire à chaud
Editer ensuite la configuration de CRM avec la commande crm configure edit pour y ajouter la nouvelle primitive correspondant au volume supplémentaire
primitive fs-nfs-newshare Filesystem \\
params device="/dev/drbd2" directory="/srv/nfs/newshare" fstype=ext4 \\
op monitor timeout=40s interval=20s \\
op start timeout=60s interval=0s \\
op stop timeout=60s interval=0s
Modifier également le groupe pour y inclure le nouveau partage
group g-nfs fs-nfs-state fs-nfs-share fs-nfs-newshare nfsserver exportfs-nfs vip-nfs