Ubuntu 24.04 — « Stale file handle » sur NFS : pourquoi cela arrive et comment l’empêcher

Cet article vous a aidé ?

L’erreur apparaît au pire moment possible : déploiements, sauvegardes, CI, appels d’incident où tout le monde assure « je n’ai rien changé ».
Votre processus tente de lire un fichier et le noyau hausse les épaules : poignée de fichier obsolète.

Sur Ubuntu 24.04, rien de tout cela n’est « nouveau » — mais les combinaisons qui le déclenchent évoluent : conteneurs fixant des chemins, systemd automount,
modifications agressives des exports, basculements NAS, et équipes de stockage « optimisant » des agencements en journée. Traitez-le comme un bug de production : comprenez le
mécanisme, isolez le mode de défaillance et éliminez-le avec des pratiques ennuyeuses mais répétables.

Ce que signifie réellement « Stale file handle »

Sur Linux, « Stale file handle » correspond à ESTALE. En termes NFS, c’est le client qui dit :
« Je conserve une référence vers un fichier/répertoire que le serveur ne reconnaît plus. »

Cette référence est une poignée de fichier — des octets opaques retournés par le serveur NFS qui identifient de manière unique une sorte d’inode sur ce serveur.
Le client met ces poignées en cache, car demander au serveur de résoudre les chemins à chaque opération serait une catastrophe de performance.
Quand le serveur ne peut plus résoudre la poignée, vous obtenez ESTALE.

L’important : ce n’est pas une erreur d’autorisations, et ce n’est pas simplement de la « fluctuation réseau ». C’est un décalage entre ce que le client pense
exister et ce que le serveur peut remapper à un objet. Parfois c’est dû à une suppression légitime d’objet. Le plus souvent, c’est dû au
changement d’identité côté serveur quand vous ne l’attendiez pas.

En cas d’incident, le message du noyau est sans appel : le serveur dit « je ne connais pas cette poignée », et votre processus ne peut pas continuer.
Si votre charge est un système de build, un runtime de conteneur ou une base de données qui attend une cohérence filesystèmes, vous verrez des échecs
apparemment aléatoires jusqu’à ce que vous les corréliez avec des événements de topologie NFS.

Pourquoi cela arrive (les vraies causes)

Les poignées de fichier sont des identités, pas des chemins

NFS traite principalement les fichiers comme des objets. Les chemins sont résolus en poignées de fichier, puis les opérations utilisent les poignées.
C’est pourquoi un fichier peut être renommé pendant qu’il est ouvert et toujours pouvoir être lu localement — Linux suit l’inode. Avec NFS, le client suit une identité émise par le serveur.

Déclencheur central : le serveur ne peut plus mapper la poignée

Cela se produit lorsque :

  • L’objet de base du système de fichiers a effectivement disparu (suppression, recréation du système de fichiers, rollback de snapshot, etc.).
  • Le serveur a changé son idée d’identité (export déplacé, fsid modifié, système de fichiers remonté différemment, basculement vers un backend différent).
  • La poignée mise en cache par le client se réfère à un système de fichiers différent de celui que le serveur expose désormais à ce chemin (classique erreur d’« export change »).

Événements serveur courants qui invalident les poignées

  • Reconfiguration des exports : modification de /etc/exports, déplacement d’exports, passer d’une exportation d’un répertoire à celle du parent, etc.
  • Remplacement du système de fichiers : reformatage, restauration depuis une sauvegarde, recréation d’un dataset, recréation d’un arbre de répertoires.
  • Rollback de snapshot côté serveur : le chemin existe, mais les identités d’objet ont été ramenées en arrière.
  • Basculement vers un nœud différent : clusters HA où le nouveau nœud sert le « même chemin » mais pas les mêmes IDs d’objet.
  • Traversée de points de montage dans les exports : exporter un chemin qui contient d’autres montages puis modifier ces montages ultérieurement.

Contributeurs côté client (souvent pas la cause racine, mais ils amplifient)

  • Processus longue durée qui gardent des FD de répertoire et supposent qu’ils restent valides indéfiniment (CI runners, log shippers, language servers).
  • Conteneurs faisant des bind-mounts de chemins NFS ; le conteneur survit tandis que l’identité NFS change en dessous.
  • Automounters qui cachent le churn des montages/démontages jusqu’à ce qu’un job en arrière-plan touche un FD périmé.
  • Hypothèses de cache agressives (caching d’attributs, lookup caching) qui rendent le comportement « collant ».

NFSv3 vs NFSv4 : la fausse idée de « stateful »

NFSv4 est stateful sur certains aspects par rapport à NFSv3 (verrous, délégations, sessions), mais les poignées de fichier peuvent toujours devenir obsolètes.
Le protocole peut négocier beaucoup de choses, mais il ne peut pas ressusciter une identité que le serveur ne peut plus mapper.

Une citation que les ops réapprennent à leurs dépens : « Everything fails, all the time. » — Werner Vogels.
C’est direct, pas cynique. Concevez pour cela.

Deux plaisanteries, parce que les humains en ont besoin

Blague n°1 : Les poignées de fichier NFS sont comme des badges d’entreprise — une fois que la sécurité invalide le vôtre, vous pouvez toujours connaître le couloir, mais vous n’entrez pas.

Blague n°2 : Si vous avez « résolu » les stale file handles en redémarrant les clients, félicitations : vous avez inventé la stratégie de invalidation de cache la plus coûteuse.

Faits intéressants et contexte historique (court, concret)

  • NFS a été créé chez Sun Microsystems dans les années 1980 ; l’idée centrale était la « transparence réseau » pour les fichiers Unix.
  • NFSv2 utilisait des tailles de fichier 32 bits ; les gros fichiers ont poussé l’évolution vers NFSv3 et au-delà.
  • NFSv3 est essentiellement sans état côté serveur, ce qui simplifiait la récupération mais chargeait plus les clients et les poignées de fichier.
  • NFSv4 a introduit une pseudo-root et un modèle de namespace plus strict ; les comportements d’export sont différents de ceux de v3 « on exporte juste un répertoire ».
  • ESTALE précède NFS comme erreur Unix pour références distantes périmées, mais NFS en a fait un terme courant en exploitation.
  • Les poignées de fichier sont volontairement opaques pour que les serveurs puissent changer leurs mappages internes d’inodes sans que les clients s’en aperçoivent — jusqu’au moment où le serveur ne peut plus les mapper.
  • Certains vendeurs NAS encodent des IDs de système de fichiers dans les poignées ; si ces IDs changent lors d’un basculement, les poignées meurent même si les chemins semblent identiques.
  • Le « subtree checking » (une option serveur) a historiquement causé des comportements étranges avec les renommages ; beaucoup d’administrateurs le désactivent pour plus de simplicité.
  • Les clients Linux mettent en cache les dentries de façon agressive ; c’est bon pour la performance et mauvais pour le débogage quand des identités changent en dessous.

Guide de diagnostic rapide (première/deuxième/troisième étape)

Vous êtes en astreinte. Vous n’avez pas de temps pour un séminaire de philosophie sur les systèmes de fichiers distribués. Voici le chemin le plus court vers la vérité.

Première étape : confirmer qu’il s’agit d’ESTALE et définir l’étendue

  • Est-ce un seul hôte ou plusieurs ?
  • Un seul montage ou tous les montages NFS ?
  • Un seul sous-arbre de répertoire ou des fichiers aléatoires partout ?

Deuxième étape : déterminer si l’identité côté serveur a changé

  • Les exports ont-ils été modifiés ?
  • Le système de fichiers a-t-il été remonté, remplacé, basculé ou restauré ?
  • Quelqu’un a-t-il « reconstruit le partage » tout en conservant le même chemin ?

Troisième étape : décider de la stratégie de récupération selon le risque de la charge

  • Si la charge est majoritairement en lecture : un remount suffit souvent.
  • Si c’est intensif en écriture ou soumis à des verrous (bases, caches de build) : arrêter proprement l’app, remonter, puis redémarrer. Évitez les demi-mesures.
  • Si ce sont de nombreux clients simultanément : traitez cela comme un événement serveur/export, pas comme une « instabilité client ». Corrigez d’abord l’identité serveur.

Puis : prévenir la récurrence

  • Ne changez pas les exports en place pendant les heures d’activité.
  • Épinglez des identités de système de fichiers stables (stratégie fsid, datasets backend stables).
  • Utilisez systemd automount de façon réfléchie, et non comme un tapis magique masquant le churn sous-jacent.

Tâches pratiques : commandes, sorties, décisions (12+)

Voici des vérifications réelles à lancer sur des clients et serveurs Ubuntu 24.04. Chaque tâche inclut : commande, sortie typique, ce que cela signifie,
et la décision à prendre.

Task 1: Verify the error is really ESTALE (client)

cr0x@server:~$ dmesg -T | tail -n 20
[Mon Dec 29 11:02:14 2025] NFS: stale file handle
[Mon Dec 29 11:02:14 2025] NFS: v4 server nfs01 returned a stale file handle

Signification : Le client NFS du noyau a reçu ESTALE du serveur. Ce n’est pas une exception au niveau applicatif ; c’est la couche filesystem.

Décision : Traitez-le comme un problème de cohérence/identité. Commencez par vérifier les événements serveur/export, pas seulement la latence réseau.

Task 2: Identify which mount is affected (client)

cr0x@server:~$ findmnt -t nfs,nfs4 -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET        SOURCE                 FSTYPE OPTIONS
/mnt/build    nfs01:/exports/build   nfs4   rw,relatime,vers=4.1,hard,timeo=600,retrans=2
/mnt/archive  nfs02:/exports/archive nfs4   ro,relatime,vers=4.2,hard,timeo=600,retrans=2

Signification : Affiche le server:path exact et les options. La chaîne source est importante pour corréler aux changements d’export.

Décision : Si un seul montage est impliqué, concentrez-vous sur le backend de cet export. Si plusieurs, suspectez un événement affectant tout le serveur ou un failover.

Task 3: Reproduce and capture the failing path (client)

cr0x@server:~$ ls -la /mnt/build/workdir
ls: cannot access '/mnt/build/workdir/.': Stale file handle
total 0

Signification : La poignée du répertoire est périmée. Indique souvent un renommage/déplacement, un remappage d’export ou un remplacement du système de fichiers derrière ce répertoire.

Décision : Ne redémarrez pas bêtement les applis. Collectez des preuves : quel répertoire, quel montage, et si d’autres chemins fonctionnent.

Task 4: Check whether the NFS server is reachable and responding (client)

cr0x@server:~$ rpcinfo -t nfs01 nfs 4
program 100003 version 4 ready and waiting

Signification : Vérifie la joignabilité RPC de base. Cela ne prouve pas que votre export est sain, mais écarte le « serveur mort ».

Décision : Si cela échoue, traitez d’abord comme un problème de connectivité/firewall/service RPC. ESTALE peut être un bruit secondaire.

Task 5: List exports from the server perspective (client)

cr0x@server:~$ showmount -e nfs01
Export list for nfs01:
/exports/build   10.20.0.0/16
/exports/shared  10.20.0.0/16

Signification : Confirme ce que le serveur affirme exporter (souvent plus utile avec NFSv3 ; toujours utile pour une vérification de cohérence).

Décision : Si l’export a disparu ou a changé, vous avez une preuve évidente : votre montage client pointe vers une ancienne hypothèse.

Task 6: Confirm NFS version and negotiated parameters (client)

cr0x@server:~$ nfsstat -m
/mnt/build from nfs01:/exports/build
 Flags: rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.20.3.41,local_lock=none

Signification : Affiche les paramètres de montage négociés. Cela vous aide à repérer des montages « soft », des timeouts étranges ou des versions anciennes.

Décision : Si vous voyez soft pour des données critiques, planifiez un changement. Les montages soft peuvent transformer des problèmes transitoires en corruption ou écritures partielles.

Task 7: Determine whether the problem is confined to one subtree (client)

cr0x@server:~$ (cd /mnt/build && ls -la . && ls -la ./workdir) 2>&1 | sed -n '1,6p'
total 8
drwxr-xr-x  4 root root 4096 Dec 29 10:58 .
drwxr-xr-x  3 root root 4096 Dec 29 10:58 ..
ls: cannot access './workdir': Stale file handle

Signification : La racine du montage est OK ; un sous-répertoire a une poignée périmée. Causé souvent par un renommage côté serveur, un rollback de snapshot, ou le remplacement de ce sous-arbre.

Décision : Escaladez vers le propriétaire serveur/stockage : « L’identité de ce répertoire a changé. » Le remount peut aider ; mais cherchez aussi l’événement côté serveur.

Task 8: Identify processes holding FDs on the stale path (client)

cr0x@server:~$ sudo lsof +D /mnt/build/workdir 2>/dev/null | head
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
python3  4112 ci     cwd  DIR  0,1234      0  862  /mnt/build/workdir (stale)
make     4188 ci     cwd  DIR  0,1234      0  862  /mnt/build/workdir (stale)

Signification : Ces processus ont leur répertoire courant ou un FD ouvert pointant vers le répertoire périmé.

Décision : Pour une récupération sûre, arrêtez ces processus avant de remonter. Sinon ils continueront d’échouer, ou pire, de relancer et bloquer des pipelines.

Task 9: Try a clean remount on the client (client)

cr0x@server:~$ sudo umount /mnt/build
umount: /mnt/build: target is busy.

Signification : Quelque chose garde le montage occupé (fichiers ouverts, cwd, bind mounts dans des conteneurs).

Décision : Ne forcez pas aveuglément. Trouvez les détenteurs (tâche suivante) et arrêtez-les, ou utilisez un démontage lazy uniquement en mode tactique.

Task 10: Find what keeps the mount busy (client)

cr0x@server:~$ sudo fuser -vm /mnt/build
                     USER        PID ACCESS COMMAND
/mnt/build:          root     kernel mount /mnt/build
                     ci        4112 ..c..  python3
                     ci        4188 ..c..  make

Signification : Liste les processus utilisant le montage. Les drapeaux ACCESS indiquent comment ils l’utilisent (cwd, fichier ouvert, etc.).

Décision : Arrêtez/tuez les contrevenants, ou coordonnez avec les propriétaires de charge. Sur un hôte de build partagé, vider proprement est préférable à un kill surprise.

Task 11: Use lazy unmount when you must (client)

cr0x@server:~$ sudo umount -l /mnt/build

Signification : Détache le montage du namespace ; les références existantes subsistent jusqu’à ce que les processus les libèrent.

Décision : N’utilisez cela que si vous êtes déjà en mode incident et devez rétablir le service. Ensuite, redémarrez les services affectés pour lâcher les FD périmés.

Task 12: Remount with explicit options and test (client)

cr0x@server:~$ sudo mount -t nfs4 -o vers=4.1,hard,timeo=600,retrans=2 nfs01:/exports/build /mnt/build
cr0x@server:~$ stat /mnt/build/workdir
  File: /mnt/build/workdir
  Size: 4096       Blocks: 8          IO Block: 1048576 directory
Device: 0,1234     Inode: 862         Links: 12

Signification : Si stat fonctionne de nouveau, la récupération côté client a réussi. Si l’erreur persiste, le serveur présente toujours une identité différente ou un export cassé.

Décision : Si le remount corrige, planifiez quand même un travail préventif. Le remount est une récupération, pas un remède définitif.

Task 13: Check export configuration and reload events (server)

cr0x@server:~$ sudo exportfs -v
/exports/build   10.20.0.0/16(rw,wdelay,root_squash,no_subtree_check,sec=sys,fsid=101)
/exports/shared  10.20.0.0/16(rw,wdelay,root_squash,no_subtree_check,sec=sys,fsid=102)

Signification : Montre les options effectives d’export. Notez la présence de fsid — la stabilité compte, surtout avec NFSv4 et HA.

Décision : Si fsid est absent ou a changé récemment, envisagez de l’épingler. Si les chemins d’export ont changé, attendez-vous à des stale handles jusqu’à ce que les clients remontent.

Task 14: Inspect server logs for re-export/restart/FS events (server)

cr0x@server:~$ sudo journalctl -u nfs-server -u rpcbind -u nfs-idmapd --since "2 hours ago" | tail -n 25
Dec 29 10:41:08 nfs01 systemd[1]: Stopped NFS server and services.
Dec 29 10:41:08 nfs01 systemd[1]: Starting NFS server and services...
Dec 29 10:41:09 nfs01 exportfs[2143]: exporting 10.20.0.0/16:/exports/build
Dec 29 10:41:09 nfs01 systemd[1]: Started NFS server and services.

Signification : Confirme les redémarrages de service et les rechargements d’export. Les redémarrages seuls ne causent pas toujours ESTALE, mais ils se corrèlent avec des remontages backend et des actions de failover.

Décision : Si vous voyez des redémarrages autour du moment de l’incident, cherchez ce qui les a déclenchés : maintenance, gestion de configuration, mises à jour de paquets, scripts de basculement.

Task 15: Confirm the exported path is the filesystem you think it is (server)

cr0x@server:~$ findmnt -T /exports/build -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET        SOURCE          FSTYPE OPTIONS
/exports/build /dev/sdb1      ext4   rw,relatime

Signification : Vérifie ce qui supporte l’export. Si ce device/dataset de backing a changé depuis hier, des poignées peuvent devenir obsolètes même si le chemin est resté identique.

Décision : Si le système de fichiers de backing a changé, considérez l’événement comme une modification rompante. Communiquez aux propriétaires clients et planifiez des fenêtres de remount/restart.

Task 16: Detect snapshot rollback / dataset recreation hints (server)

cr0x@server:~$ sudo tune2fs -l /dev/sdb1 | sed -n '1,12p'
tune2fs 1.47.0 (5-Feb-2023)
Filesystem volume name:   builddata
Filesystem UUID:          6f8f2b7f-7c3f-4b0e-9b8b-3a8e5f7e0c2a
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit

Signification : Les changements d’UUID sont un indice fort que le système de fichiers a été recréé. Un rollback peut ne pas changer l’UUID, mais une recréation le fait généralement.

Décision : Si l’UUID a changé, cessez toute discussion sur le « pas de changement ». Quelque chose a remplacé le système de fichiers. Attendez-vous à des poignées périmées jusqu’à ce que les clients remontent et que les applis rouvrent les chemins.

Task 17: Check client RPC and retrans stats for a noisy network (client)

cr0x@server:~$ nfsstat -rc
Client rpc stats:
calls      retrans    authrefrsh
153209     18         153227

Signification : Un nombre élevé de retrans peut indiquer perte de paquets ou congestion. Cela ne provoque pas directement ESTALE, mais peut ralentir la récupération et rendre les symptômes plus confus.

Décision : Si retrans est élevé, vous avez deux problèmes : un problème d’identité et un problème de transport. Corrigez l’identité d’abord, puis stabilisez le réseau.

Task 18: If systemd automount is involved, confirm mount unit state (client)

cr0x@server:~$ systemctl status mnt-build.automount --no-pager
● mnt-build.automount - Automount mnt-build
     Loaded: loaded (/etc/systemd/system/mnt-build.autommount; enabled; preset: enabled)
     Active: active (waiting) since Mon 2025-12-29 10:12:10 UTC; 51min ago
      Where: /mnt/build

Signification : Automount peut démonter quand inactif et remonter plus tard. C’est acceptable — jusqu’à ce que quelque chose garde un FD périmé pendant que le montage est remplacé.

Décision : Si vous voyez un churn fréquent des montages, réfléchissez à la compatibilité de votre charge. Daemons longue durée + automount peuvent former un mauvais couple.

Erreurs courantes : symptôme → cause → correction

« Un seul répertoire affiche l’erreur ; le reste du partage est OK »

Symptôme : ls fonctionne à la racine du montage, mais un sous-répertoire donne « Stale file handle ».

Cause : Ce sous-arbre a été renommé, remplacé, restauré via snapshot, ou est un point de montage qui a changé sous un parent exporté.

Correction : Remontez le client pour vider les poignées. Si ça se répète, arrêtez d’exporter des chemins contenant des points de montage volatils ; exportez des frontières de système de fichiers stables.

« Ça a commencé juste après qu’on ait ‘nettoyé’ /etc/exports »

Symptôme : De nombreux clients voient ESTALE peu après un changement d’export.

Cause : La sémantique des chemins d’export a changé (parent vs enfant, export traversant un mount, changement de fsid). Les clients conservent des poignées sur des objets servis sous l’ancien mapping d’export.

Correction : Traitez les changements d’export comme ruptures. Coordonnez une fenêtre de remount/restart pour les clients. Épinglez les valeurs fsid et maintenez une topologie d’export stable.

« Redémarrer le serveur NFS a résolu, mais c’est revenu »

Symptôme : Soulagement temporaire, puis reprise des poignées périmées.

Cause : Failover du backend ou cycles de vie de stockage (rollback, rebuild, basculement de réplication) qui changent l’identité d’objet de façon répétée.

Correction : Corrigez le processus de lifecycle : backends stables, IDs cohérents de systèmes de fichiers et cutovers contrôlés avec coordination des clients. Les reboots gèrent le symptôme.

« Ça arrive pendant les tests de basculement »

Symptôme : Un événement HA déclenche des ESTALE largement répandus.

Cause : Le nœud de basculement présente le même chemin d’export mais une identité de système de fichiers différente ou un mapping fsid incohérent.

Correction : Concevez le HA avec une identité stable : même backend répliqué avec des IDs cohérents, ou acceptez que les clients doivent remonter et les applis redémarrer comme partie du runbook de basculement.

« C’est un problème Docker/Kubernetes »

Symptôme : Des pods rapportent des handles périmés ; les nœuds semblent OK ; l’équipe stockage pointe vers Kubernetes.

Cause : Les conteneurs gardent des montages longue durée et des FD de répertoire ; le côté serveur NFS a changé d’identité. Kubernetes facilite juste le maintien des processus assez longtemps pour remarquer l’erreur.

Correction : Arrêtez de traiter NFS comme un store mutable à l’infini. Si vous devez changer exports/backends, roulez pods/nœuds pour rafraîchir les poignées. Préférez des PV stables et évitez de déplacer des exports.

« On a utilisé des montages ‘soft’ pour éviter les blocages, maintenant on a des artefacts de build étranges »

Symptôme : Échecs intermittents, sorties partielles, et parfois des poignées périmées après forte charge.

Cause : Les montages soft permettent aux opérations d’échouer en plein flux ; l’application n’était pas conçue pour gérer des I/O partielles.

Correction : Pour des données critiques, utilisez hard et ajustez les timeouts. Si vous avez besoin d’un comportement non-bloquant, redessinez le workflow (staging local, retries côté application).

Trois mini-récits d’entreprise depuis le terrain

Incident n°1 : la mauvaise hypothèse (« Les chemins sont des identités »)

Une entreprise faisait tourner une ferme de build monorepo sur Ubuntu. Les workers de build montaient /mnt/build depuis un serveur NFS.
Un vendredi, un ingénieur stockage a migré le partage vers un nouveau volume. Ils ont gardé le même chemin serveur : /exports/build.
L’hypothèse était simple et très humaine : si le chemin est le même, les clients ne s’en soucieront pas.

Lundi matin, la CI a commencé à échouer de manière qui ressemblait à un compilateur capricieux. Des jobs mouraient au hasard en tentant des stat() sur des répertoires temporaires.
Certains jobs réussissaient, d’autres non. Les réessais fonctionnaient parfois. L’équipe build accusait l’ordonnanceur CI. L’équipe d’ordonnanceurs accusait les workers.
L’équipe stockage accusait « le cache Linux ».

L’indice était que seuls les workers longue durée échouaient. Les workers fraîchement redémarrés allaient bien. Sur les hôtes affectés,
lsof montrait d’anciennes poignées de cwd dans l’espace de travail pointant vers des répertoires périmés. Le remount a corrigé instantanément.
La migration serveur n’a pas « cassé NFS ». Elle a changé les identités d’objet derrière un chemin.

La correction n’a rien d’héroïque : coordonner les migrations de partage avec une fenêtre de rafraîchissement client. Cela a signifié vider contrôlé des workers,
unmount/remount, puis rejoinder. L’action post-mortem était encore plus ennuyeuse : traiter les backends NFS comme des versions d’API.
Si vous changez la map d’identité, les clients doivent être recyclés.

Incident n°2 : l’optimisation qui a échoué (exporter le parent pour « simplifier »)

Une autre organisation avait plusieurs exports : /exports/app1, /exports/app2, /exports/app3.
Quelqu’un a décidé que c’était « trop de montages ». La proposition : exporter simplement /exports et laisser les clients utiliser des sous-répertoires.
Un montage pour les gouverner tous. Le changement a été déployé progressivement, car la gestion de configuration facilitait le « simple switch ».

Un mois plus tard, des stale file handles sont apparus dans une seule appli, et seulement pendant les déploiements. C’était rageant.
Le processus de déploiement créait un nouveau répertoire release, basculait un symlink, puis nettoyait les anciennes releases. Standard.
Sur disques locaux c’est OK. Sur NFS, c’est globalement OK — jusqu’à exporter un parent qui contient d’autres points de montage et que vous les réorganisiez.

Le détail caché : /exports/app2 n’était plus un répertoire sur le même système de fichiers ; il était devenu un montage séparé.
L’« export unique » traversait maintenant des frontières de systèmes de fichiers. Pendant une fenêtre de maintenance, le montage backing app2 a été démonté puis remonté.
Les clients qui avaient mis en cache des poignées dans ce sous-arbre ont commencé à recevoir ESTALE, tandis que d’autres applis restaient saines.

Revenir aux exports séparés a corrigé le problème. Pas parce que « plus de montages est mieux », mais parce que chaque export correspondait désormais à une frontière de système de fichiers stable.
L’optimisation a réduit la complexité de configuration et augmenté la complexité des incidents. Ce compromis paye rarement.

Incident n°3 : la pratique ennuyeuse qui a sauvé la mise (IDs stables et remounts coordonnés)

Une entreprise du secteur financier utilisait NFS pour des outils partagés et des artefacts de build. Ils avaient une paire HA et effectuaient des tests de basculement réguliers.
Au début, ils acceptaient que les basculements soient chaotiques : les clients lançaient parfois des stale handles, et les gens « redémarraient juste des trucs ».
Puis ils se sont sérieux et ont rédigé un runbook qui traitait la stabilité d’identité comme une exigence prioritaire.

Ils ont épinglé des valeurs fsid dans les exports, gardé les chemins d’export attachés à des frontières de systèmes de fichiers stables, et refusé les changements de topologie en place.
Quand les backends devaient bouger, ils le planifiaient comme une migration de schéma. Il y avait un ticket de changement, une vidange client et un remount coordonné.
Pas d’héroïsme, juste de la discipline.

Lors d’un incident ultérieur sur un firmware de stockage, la paire HA a basculé de manière inattendue. Certains clients ont encore dû remonter,
mais le rayon d’impact était plus petit : moins de poignées périmées, récupération plus rapide, et moins de processus « fantômes » tenant d’anciens FD de répertoire.
Le runbook a fonctionné parce qu’il ne dépendait ni de la chance ni du savoir tacite.

Le constat : la fiabilité n’est pas une option secrète à activer. C’est un ensemble de contraintes qu’on accepte de ne pas violer, même quand on est pressé.

Comment l’empêcher : modèles de prévention efficaces

1) Gardez les exports stables et ennuyeux

Le levier le plus puissant : ne changez pas la map d’identité derrière un chemin d’export à la légère.
Si vous devez migrer des données, faites-le de façon à préserver l’identité d’objet (souvent impossible), ou acceptez que les clients doivent se rafraîchir.

  • Évitez de swapper des systèmes de fichiers sous le même chemin d’export sans plan de maintenance client.
  • Évitez d’exporter un répertoire parent qui contient des points de montage susceptibles d’être remontés indépendamment.
  • Privilégiez l’export de la racine du système de fichiers de backing (ou d’une frontière de dataset stable), pas d’un répertoire quelconque.

2) Utilisez NFSv4 avec une stratégie de namespace cohérente

Pour NFSv4, traitez le namespace serveur comme une API. Modifier la structure de la pseudo-root ou déplacer des exports casse les clients de façon non locale.
Gardez le namespace stable. Si vous avez besoin d’un nouveau layout, créez un nouvel export et migrez les clients délibérément.

3) Épinglez fsid quand approprié (serveur)

Sur les serveurs NFS Linux, des valeurs explicites fsid= dans /etc/exports peuvent aider à garder l’identité d’export cohérente à travers les reboots et les événements HA
(selon l’architecture). Ce n’est pas une baguette magique, mais ça prévient un renumérotage accidentel.

4) N’utilisez pas les montages « soft » pour éviter l’ingénierie

Pour la fiabilité, les montages hard sont généralement le choix par défaut car ils préservent des attentes POSIX : les écritures ne doivent pas échouer aléatoirement en plein vol.
Si vous avez besoin d’un échec borné, implémentez-le dans l’application (timeouts, retries, staging local), pas en laissant le filesystem mentir.

5) Planifiez le rafraîchissement client : redémarrez les services qui tiennent des références périmées

Même après un remount, les démons longue durée peuvent garder des descripteurs de fichiers pointant sur des objets périmés. Identifiez-les et redémarrez-les dans le cadre de la récupération.
Ceci est particulièrement vrai pour : CI runners, language servers, serveurs applicatifs lisant templates/config, et tout processus surveillant des répertoires.

6) Traitez les opérations du cycle de vie du stockage comme des changements rompants

Rollback de snapshot, restauration de système de fichiers, cutover de réplication et « rebuild du partage » ne sont pas transparents.
Si votre processus de stockage change l’identité d’objet, il a besoin de :

  • une annonce (qui est impacté),
  • un plan de récupération (remount/restart),
  • une étape de validation (tests depuis un client canari).

7) Si vous exécutez NFS sous Kubernetes, acceptez le recyclage

Quand l’identité serveur change, les pods ne vont pas se réparer magiquement. Votre meilleure défense est un pattern opérationnel :
rouler les déploiements (ou même les nœuds) de façon contrôlée après des événements de stockage. Ça paraît brutal. C’est efficace.

8) Observabilité : consignez les « événements d’identité », pas seulement la latence

Tout le monde métrique IOPS et débit. Moins d’équipes surveillent : rechargements d’export, changements d’UUID de FS, événements de failover, rollback de snapshot.
Ce sont ces événements qui se corrèlent avec les poignées périmées. Intégrez-les dans votre timeline d’incident.

Listes de contrôle / plan étape par étape

Checklist A : Pendant un incident (côté client)

  1. Confirmer que le noyau le voit : vérifier dmesg pour les messages de poignée périmée.
  2. Identifier le montage : utiliser findmnt et nfsstat -m.
  3. Définir la portée : un sous-arbre ou tout le montage ?
  4. Trouver les détenteurs : lsof +D (attention sur de grands arbres) ou fuser -vm.
  5. Arrêter les workloads qui tiennent des FD périmés (ou vider le nœud).
  6. Démonter proprement ; si occupé et que vous devez agir, utilisez umount -l puis redémarrez les services en cause.
  7. Remonter et vérifier avec stat et un petit test lecture/écriture adapté au partage.
  8. Si ça revient rapidement, stoppez : c’est probablement un problème d’identité serveur/export.

Checklist B : Pendant un incident (côté serveur)

  1. Vérifier les redémarrages/rechargements d’export dans journalctl.
  2. Vérifier les exports effectifs avec exportfs -v.
  3. Confirmer ce qui supporte l’export : findmnt -T sur le chemin exporté.
  4. Confirmer l’absence d’indices récents de recréation de FS (changements d’UUID, nouveaux datasets, logs de remontage).
  5. Si HA : confirmer si un basculement a eu lieu et si le nouveau nœud sert une identité backend identique.
  6. Communiquer clairement : « Les clients doivent remonter et les services doivent redémarrer » est une instruction opérationnelle valide quand l’identité a changé.

Checklist C : Déploiement préventif (plan de changement)

  1. Inventorier les exports et leurs systèmes de fichiers de backing. Documenter quelles sont les frontières stables.
  2. Décider d’une stratégie de namespace (surtout pour NFSv4) : pseudo-root stable, éviter de déplacer des exports.
  3. Épingler les valeurs fsid quand nécessaire ; les garder stables à travers les nœuds dans les designs HA.
  4. Définir la procédure de récupération client : remount + redémarrage des services spécifiques.
  5. Ajouter des canaris : un ou deux clients qui exécutent périodiquement stat/find et alertent sur ESTALE.
  6. Organiser une journée de test de basculement ou de changement d’export en journée, avec les propriétaires présents.
  7. Rédiger le runbook et le rendre par défaut, pas folklore.

FAQ

1) Est-ce que « Stale file handle » est un bug d’Ubuntu 24.04 ?

Généralement non. C’est le client NFS Linux qui rapporte fidèlement que le serveur a invalidé une identité d’objet. Ubuntu 24.04 exécute simplement des noyaux modernes
et des charges modernes qui rendent le churn d’identité plus facile à déclencher et plus difficile à ignorer.

2) Pourquoi le remount corrige-t-il le problème ?

Le remount force le client à vider les poignées en cache et à résoudre à nouveau les chemins. Si le serveur est désormais cohérent et stable, de nouvelles poignées fonctionneront.
Si le serveur continue de changer d’identité, le problème revient.

3) Les options de caching d’attributs peuvent-elles prévenir les poignées périmées ?

Pas vraiment. Le caching d’attributs affecte la vitesse à laquelle les métadonnées sont visibles (mtime/taille/permissions). Les poignées périmées surviennent quand le serveur
ne peut plus mapper la poignée du tout. Vous pouvez modifier le caching et quand même obtenir ESTALE si les identités changent.

4) NFSv4 élimine-t-il les poignées périmées ?

Non. NFSv4 améliore beaucoup de choses (sessions, modèle de verrouillage, namespace), mais il ne peut pas garder une poignée valide si l’identité backend du serveur change.

5) Est-ce causé par des problèmes réseau ?

La perte de paquets et la congestion peuvent rendre NFS problématique, mais elles ne produisent pas habituellement ESTALE directement. Elles peuvent en revanche augmenter le churn de montage,
les timeouts, retransmissions et actions de « recovery » qui coïncident avec des changements d’identité. Diagnostiquez les deux, mais ne les confondez pas.

6) Quel est l’ensemble d’options de montage client le plus sûr pour la production ?

Il n’existe pas d’ensemble universel, mais une base sensée pour les montages critiques est : NFSv4.1+, hard, TCP, timeo raisonnable,
et évitez les réglages exotiques tant que vous n’avez pas une raison mesurée. La fiabilité prime sur la subtilité.

7) Pourquoi seuls les processus longue durée échouent alors que de nouveaux shells fonctionnent ?

Les processus longue durée gardent des descripteurs de fichiers ou un répertoire courant pointant vers des objets devenus invalides.
Les nouveaux shells résolvent les chemins à neuf et obtiennent de nouvelles poignées, donc ils semblent « fonctionner ». Cette dichotomie est une signature classique d’une poignée périmée.

8) Comment gérer les poignées périmées dans Kubernetes ?

Supposez que vous devrez recycler quelque chose : redémarrez les pods qui ont touché le partage, possiblement videz/rebootez des nœuds dans les pires cas.
Plus important : empêchez le churn d’identité en gardant les exports/backends stables et en traitant les cutovers de stockage comme des opérations coordonnées.

9) Peut-on « nettoyer » les poignées périmées sans démonter ?

Parfois vous pouvez contourner en faisant cd hors du répertoire périmé, en rouvrant des fichiers, ou en redémarrant le service.
Mais si les références périmées sont étendues, le démontage/remontage est le reset propre.

10) Que dire aux équipes applicatives quand cela arrive ?

Dites-leur la vérité en termes opérationnels : « L’identité du serveur NFS a changé ; votre processus détient des références périmées.
Nous allons remonter et redémarrer votre service pour rouvrir les chemins. » Ne présentez pas cela comme de la « flakiness NFS aléatoire ».

Conclusion : étapes pratiques suivantes

« Stale file handle » arrive quand vous traitez NFS comme un nom de dossier magique plutôt que comme un système distribué avec une identité.
La correction est rarement exotique. C’est généralement de la discipline : exports stables, backends stables et rafraîchissement coordonné des clients quand les identités changent.

  1. Immédiatement : utilisez le guide de diagnostic rapide, identifiez le montage et le sous-arbre, et remontez proprement après avoir arrêté les détenteurs de FD.
  2. Cette semaine : auditez la topologie des exports et arrêtez d’exporter des chemins qui traversent des points de montage volatils. Épinglez des identités stables quand approprié.
  3. Ce trimestre : transformez migrations, rollbacks et basculements en runbooks avec validation canari et étapes explicites de redémarrage/remount client.

Vous n’êtes pas obligé d’aimer NFS. Vous devez simplement l’exploiter comme une vraie infrastructure. Parce que ça l’est.

← Précédent
Glossaire ZFS : VDEV, TXG, ARC, SPA — Tout ce que vous croyiez savoir
Suivant →
Routage par politique Debian 13 : déboguer ip rule et ip route sans douleur

Laisser un commentaire