Rien n’est aussi démoralisant qu’une machine Debian 13 neuve avec une carte réseau rapide, un ensemble de stockage sain et des performances NFS qui donnent l’impression de voyager par fax. Les lectures sont « à peu près correctes ». Les écritures sont tragiques. Et tout le monde a une théorie, sans preuves.
C’est le cas où vous arrêtez de deviner et commencez à prouver. Nous allons montrer comment démontrer — à l’aide de statistiques, traces et tests contrôlés — que votre goulot d’étranglement est généralement l’un des trois coupables : la sémantique synchrone (sync/async et le comportement de commit), des rsize/wsize trop petits, ou un décalage entre les attentes du client et la réalité du stockage serveur. Puis nous le corrigeons sans nous mentir sur la durabilité.
Feuille de route pour un diagnostic rapide
Si vous êtes en astreinte et que quelqu’un respire dans le canal d’incident, voici le chemin le plus court vers la vérité. Ne commencez pas par changer les options de montage. Commencez par mesurer une chose à la fois.
Première étape : confirmez ce que vous avez monté et ce que le serveur exporte
- Client : confirmez la version NFS et les options de montage (
nfs4vsnfs,rsize/wsize, comportementsyncviahard/softet timeouts). - Serveur : confirmez les options d’export et si le serveur force le comportement
sync(par défaut) et s’il peut mettre en cache en toute sécurité.
Deuxième étape : déterminez si vous êtes lié par la latence (sync) ou par la bande passante (taille d’E/S)
- Exécutez un test de lecture en mémoire tampon et un test d’écriture directe depuis le client. Si les lectures atteignent le débit ligne mais que les écritures rampent, suspectez les commits synchrones.
- Vérifiez
nfsstat -cpour le taux d’appelsCOMMIT. Un taux élevé de COMMIT est un panneau au néon.
Troisième étape : isolez réseau vs disque serveur
- Réseau : vérifiez que vous pouvez pousser des paquets avec un test TCP simple (pas « ping », pas de l’espoir).
- Disque serveur : vérifiez si NFSd attend des flushs de stockage (commun avec HDD, contrôleurs RAID avec cache d’écriture désactivé, ou stockage virtualisé).
Quatrième étape : ne touchez au tuning qu’après preuve
- Si vous prouvez que les commits sont le facteur limitant : corrigez le chemin de durabilité (cache stockage serveur, SLOG, réglages du contrôleur) ou acceptez un risque contrôlé (
asyncseulement si la charge le permet). - Si vous prouvez que ce sont de petites E/S : augmentez
rsize/wsize, vérifiez que MTU et offloads ne vous sabotent pas, et confirmez que le serveur supporte ces tailles.
Le modèle de performance : où NFS passe réellement du temps
Les débats sur les performances NFS sont souvent émotionnels parce que les gens confondent « mon appli est lente » avec « le réseau est lent » ou « le stockage est lent ». NFS est les trois, liés par un contrat de cohérence.
À un haut niveau, une écriture NFS peut être :
- Envoyée du client au serveur en morceaux de la taille définie par
wsize. - Accusée de réception par le serveur soit comme « je l’ai reçue » soit comme « elle est sur un stockage stable », selon la version NFS, les options d’export et l’implémentation serveur.
- Commitée (explicitement via des opérations
COMMITen NFSv3 et NFSv4 quand nécessaire) vers un stockage stable.
Si le serveur doit vider sur un stockage stable fréquemment, vous êtes lié par la latence. Si chaque flush prend 2–10 ms et que vous le faites des milliers de fois, votre débit est mathématiquement condamné. Vous pouvez avoir du 100 GbE et écrire à « quelques dizaines de Mo/s » parce que la physique ne négocie pas.
Si rsize/wsize sont minuscules (8K, 16K, 32K), vous êtes lié par les frais généraux. Le coût CPU du traitement RPC, la latence par appel et la gestion du noyau deviennent le goulot même si le stockage est rapide.
Debian 13 ne change pas ces lois. Il vous donne juste des noyaux plus récents, un comportement client NFS plus récent, et des valeurs par défaut légèrement différentes qui peuvent révéler ce qui était auparavant masqué par la chance.
Une citation à garder en tête pendant toute optimisation en production :
« L’espoir n’est pas une stratégie. » — Général Gordon R. Sullivan
Faits et historique utiles
- NFS a été conçu dans les années 1980 pour rendre les fichiers distants semblables à des fichiers locaux sur des LANs qui étaient lents selon les standards d’aujourd’hui. Beaucoup de ses sémantiques supposent que la latence est acceptable si la cohérence est préservée.
- NFSv3 a introduit le concept de COMMIT pour que les clients puissent pipeline des écritures tout en demandant un commit durable plus tard, ce qui explique pourquoi les « COMMIT storms » sont un mode de défaillance reconnaissable.
- NFSv4 a intégré le « mount » dans le protocole et introduit le verrouillage avec état et les sessions ; les compteurs de performance et modes de défaillance diffèrent de v3 de façon visible dans
nfsstat. syncest historiquement le défaut sûr sur de nombreux serveurs NFS car il correspond aux attentes des utilisateurs : « quand une écriture retourne, c’est probablement sûr. » Cette sécurité a un prix.asyncpeut être très rapide parce qu’il permet au serveur d’accuser réception avant que les données n’atteignent le stockage stable. Il peut aussi transformer une panne de courant en perte de données d’une manière cohérente avec le contrat que vous venez de violer.- rsize/wsize étaient autrefois limités par d’anciens NIC, MTU et contraintes noyau. Linux moderne peut gérer de grandes tailles, mais vous avez toujours besoin d’une compatibilité de bout en bout.
- Les « jumbo frames » ne sont pas une formule magique. Elles aident dans certains cas liés au CPU, mais elles créent aussi des trous de chemin-PMTU et des pertes bizarres si un port de commutateur reste à 1500.
- Le client NFS Linux a connu plusieurs générations de code ; « ça marchait sur Debian 10 » ne prouve pas que votre charge est OK — ça prouve que votre ancienne configuration la tolérait.
- Le writeback caching est un problème de stockage déguisé en problème de système de fichiers. Un contrôleur RAID avec le cache d’écriture désactivé peut rendre
syncaussi lent que de la mélasse même sur des SSD.
Blague #1 : le tuning NFS, c’est comme régler le mitigeur d’une douche dans un vieil hôtel — un millimètre de trop et vous êtes glacial ou ébouillanté.
Prouvez que c’est le comportement sync/commit
Vous ne prouvez pas « sync est le problème » en le criant sur Slack. Vous le prouvez en corrélant la latence d’écriture, la fréquence des commits et le temps de flush du stockage serveur.
Ce que « sync » signifie réellement ici
Côté serveur, le comportement d’export sync signifie généralement que le serveur ne répondra pas « terminé » tant qu’il ne peut garantir que les données sont sur un stockage stable (ou du moins aussi stable que la pile de stockage du serveur le prétend). Cela implique souvent un flush de cache ou une barrière équivalente.
Sur beaucoup de configurations, le tueur n’est pas le transfert réseau. C’est la garantie de stockage stable. Si votre stockage sous-jacent ne peut pas compléter rapidement les flushs, chaque écriture « sûre » devient une micro-transaction qui attend qu’un disque ait fini de méditer.
La signature de la douleur liée au sync
- Le débit en lecture élevé semble correct ; le débit en écriture est bas et plat.
- Le débit d’écriture n’augmente pas beaucoup avec des fichiers plus gros ou plusieurs threads (ou il augmente légèrement puis plafonne).
nfsstatmontre un taux non négligeable d’appelsCOMMIT.- Les métriques de stockage serveur montrent une latence élevée sur les flushs ou barrières (même si les IOPS d’écriture bruts semblent « corrects »).
Que faire avec cette preuve
Vous avez trois options honnêtes :
- Rendre le stockage stable rapide (SSD appropriés, cache contrôleur avec protection batterie/flash, ZFS SLOG, etc.).
- Changer la charge (écritures par lots, réduire la fréquence des fsync, écrire localement puis déplacer, utiliser du stockage objet pour les logs, etc.).
- Relâcher le contrat de sécurité (
async, ou durabilité au niveau application) en connaissance de cause et par écrit.
Prouvez que c’est rsize/wsize
rsize et wsize définissent la taille maximale de payload pour les opérations NFS READ et WRITE. Trop petits, et vous êtes noyé sous les frais RPC. Assez grands, et vous exploitez la bande passante pour laquelle vous avez payé.
Mais il y a un piège : de grandes tailles d’E/S n’aident que si vous n’êtes pas déjà lié par la latence des commits et si le chemin (client, serveur, réseau) les gère proprement.
Signature de la douleur liée à rsize/wsize
- L’utilisation CPU sur le client ou le serveur augmente rapidement avec le débit.
- Beaucoup de petites opérations NFS par seconde apparaissent dans
nfsstat. - Le débit s’améliore considérablement lorsque vous augmentez
rsize/wsize. - Le réseau est sous-utilisé malgré une faible latence disque et un CPU suffisant.
À quoi ressemblent de « bonnes » tailles sur Linux moderne
Sur des réseaux modernes typiques (1/10/25 GbE) et des noyaux, des valeurs comme 262144 (256 KiB) sont des points de départ courants pour NFSv4. Certains environnements vont plus haut, mais poursuivre les valeurs maximales est moins utile que vérifier que la taille négociée correspond à ce que vous pensez.
Tâches pratiques : commandes, sorties, ce qu’elles signifient et vos décisions
Voici les tâches que j’exécute réellement quand quelqu’un dit « NFS est lent » et attend une correction. Chaque tâche a un point de décision. Si vous ne changez pas de décision en fonction de la sortie, vous ne faites pas un diagnostic ; vous faites du tourisme.
Task 1: Confirm what NFS you mounted (version, rsize/wsize, sec, proto)
cr0x@client:~$ nfsstat -m
/mnt/shared from nfs1:/export/shared
Flags: rw,relatime,vers=4.2,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.10.21,local_lock=none
Signification : C’est la vérité terrain de ce que le noyau a négocié. Si vous attendiez NFSv3 ou d’autres tailles, la réalité est différente.
Décision : Si rsize/wsize sont petits (par ex. 32768), planifiez de tester des tailles plus grandes. Si la version n’est pas celle attendue, corrigez cela d’abord.
Task 2: Confirm the server export options (sync/async, fsid, subtree, security)
cr0x@nfs1:~$ sudo exportfs -v
/export/shared
10.10.10.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,ro,secure,root_squash,no_all_squash)
/export/shared
10.10.10.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,root_squash,no_all_squash)
Signification : L’export est en sync. C’est sûr, et cela peut être lent selon la performance des flushs de stockage.
Décision : Ne passez pas à async tout de suite. D’abord, prouvez que le sync est le goulot avec des preuves de commit/latence.
Task 3: Check server NFS thread and RPC load basics
cr0x@nfs1:~$ sudo nfsstat -s
Server rpc stats:
calls badcalls badfmt badauth badclnt
10293841 0 0 0 0
Server nfs v4:
null compound open close read write
0 854123 1102 1102 302194 181004
commit getattr setattr fsinfo renew fsstat
42001 701223 0 21 12944 98
Signification : Si commit est non trivial par rapport aux écritures, vous payez peut-être fréquemment pour un stockage stable.
Décision : Si les appels commit sont élevés, priorisez l’enquête sur le sync/flush. Si commit est proche de zéro, concentrez-vous sur la taille d’E/S ou le réseau.
Task 4: Look for COMMIT storms on the client too
cr0x@client:~$ nfsstat -c
Client rpc stats:
calls retrans authrefrsh
221004 12 0
Client nfs v4:
null compound read write commit getattr
0 199321 100112 40123 9800 32119
Signification : Des appels commit existent et les retransmissions sont faibles. Ce n’est pas un problème de perte réseau ; c’est probablement un coût de durabilité/flush.
Décision : Orientez-vous vers la mesure de la latence de flush sur la pile de stockage serveur.
Task 5: Check if you’re accidentally using “sync-like” app behavior (fsync-heavy)
cr0x@client:~$ sudo strace -f -tt -T -e trace=fdatasync,fsync,openat,write -p 23817
15:12:09.441201 fsync(7) = 0 <0.012341>
15:12:09.453901 write(7, "....", 4096) = 4096 <0.000221>
15:12:09.454301 fsync(7) = 0 <0.010998>
Signification : L’application appelle fsync fréquemment. Sur NFS, cela peut se traduire par des commits/flushs. Vous pouvez tuner NFS toute la journée et perdre quand même.
Décision : Si la charge est fsync-heavy (bases de données, loggers avec journalisation), vous devez rendre le stockage serveur stable rapide ou changer la stratégie de durabilité de l’appli.
Task 6: Run a controlled write test that bypasses client page cache
cr0x@client:~$ dd if=/dev/zero of=/mnt/shared/ddtest.bin bs=1M count=4096 oflag=direct status=progress
3229614080 bytes (3.2 GB, 3.0 GiB) copied, 78 s, 41.4 MB/s
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB, 4.0 GiB) copied, 104.2 s, 41.2 MB/s
Signification : ~41 MB/s sur un lien supposément plus rapide est suspect. oflag=direct rend ce test plus sensible au comportement de commit serveur.
Décision : Si les écritures directes sont lentes alors que les tests réseau sont rapides, concentrez-vous sur le flush du stockage serveur et la sémantique sync.
Task 7: Run a controlled buffered read test to compare
cr0x@client:~$ dd if=/mnt/shared/ddtest.bin of=/dev/null bs=4M status=progress
4173336576 bytes (4.2 GB, 3.9 GiB) copied, 6 s, 695 MB/s
4294967296 bytes (4.3 GB, 4.0 GiB) copied, 6.2 s, 693 MB/s
Signification : Les lectures sont proches du débit ligne ; les écritures ne le sont pas. Cette asymétrie hurle « chemin de commit/flush/durabilité ».
Décision : Arrêtez de blâmer le réseau.
Task 8: Verify network capacity independently (simple TCP throughput)
cr0x@nfs1:~$ iperf3 -s
-----------------------------------------------------------
Server listening on 5201 (test #1)
-----------------------------------------------------------
cr0x@client:~$ iperf3 -c 10.10.10.11 -P 4
[SUM] 0.00-10.00 sec 36.8 GBytes 31.6 Gbits/sec 0 sender
[SUM] 0.00-10.00 sec 36.7 GBytes 31.5 Gbits/sec receiver
Signification : Le réseau est bon. Vos écritures NFS à 40 MB/s ne sont pas un problème de câble.
Décision : Investissez du temps dans la latence de flush stockage et la sémantique d’écriture NFS.
Task 9: Check MTU consistency (because someone always “helped”)
cr0x@client:~$ ip -d link show dev enp65s0
2: enp65s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 3c:fd:fe:aa:bb:cc brd ff:ff:ff:ff:ff:ff
cr0x@nfs1:~$ ip -d link show dev enp65s0
2: enp65s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 3c:fd:fe:11:22:33 brd ff:ff:ff:ff:ff:ff
Signification : Incohérence MTU. Cela peut provoquer de la fragmentation, des pertes et des comportements étranges — surtout si un équipement intermédiaire est à 1500.
Décision : Soit régler MTU à 1500 partout (ennuyeux, fiable), soit activer jumbo frames de bout en bout et le prouver avec capture et config de commutateur.
Task 10: Observe per-operation latency with nfsiostat
cr0x@client:~$ nfsiostat 2 3
10.10.10.11:/export/shared mounted on /mnt/shared:
read: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms)
85.00 696320 8192.00 0.00 1.20 1.35
write: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms)
40.00 40960 1024.00 0.00 10.80 24.50
Signification : Les écritures ont un temps d’exécution bien plus élevé que le RTT. Cela indique souvent une attente côté serveur (flush/commit), pas une latence réseau.
Décision : Concentrez-vous sur le serveur : latence de stockage, cache d’écriture, comportement du système de fichiers et threads NFSd.
Task 11: Watch server disk flush latency in real time
cr0x@nfs1:~$ iostat -x 2 3
avg-cpu: %user %nice %system %iowait %steal %idle
6.23 0.00 4.12 21.44 0.00 68.21
Device r/s w/s rkB/s wkB/s aqu-sz await svctm %util
nvme0n1 12.00 420.00 896.0 43008.0 8.42 19.8 1.1 48.0
Signification : Un await élevé avec une utilisation modérée implique que la latence provient des flushs/barrières ou du comportement de la pile stockage, pas d’une saturation brute de bande passante.
Décision : Enquêter sur les politiques de cache d’écriture, le système de fichiers et s’il existe un « mensonge de stockage stable » (ou son absence).
Task 12: Verify if the server is running out of NFS threads or hitting CPU limits
cr0x@nfs1:~$ ps -eo pid,comm,psr,pcpu,stat | grep -E 'nfsd|rpc'
812 nfsd 2 6.5 S
813 nfsd 3 6.2 S
814 nfsd 5 6.1 S
815 nfsd 7 6.4 S
501 rpc.svcgssd 1 0.1 S
476 rpc.mountd 0 0.0 S
cr0x@nfs1:~$ cat /proc/fs/nfsd/threads
8
Signification : 8 threads peuvent suffire ou être insuffisants selon la charge et le CPU. Si les threads nfsd sont saturés alors que le stockage est inactif, vous êtes lié par le CPU/threads.
Décision : Si CPU-bound : augmentez les threads et ajustez le réseau ; si storage-bound : les threads ne vous sauveront pas.
Task 13: Confirm negotiated rsize/wsize by remounting explicitly (test, don’t assume)
cr0x@client:~$ sudo mount -o remount,vers=4.2,rsize=1048576,wsize=1048576 nfs1:/export/shared /mnt/shared
cr0x@client:~$ nfsstat -m | sed -n '1,3p'
/mnt/shared from nfs1:/export/shared
Flags: rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.10.21,local_lock=none
Signification : Le noyau a accepté des tailles de 1 MiB. S’il négocie silencieusement à une valeur inférieure, vous le verrez ici.
Décision : Si le débit s’améliore significativement et que le taux de commit reste raisonnable, conservez les tailles plus grandes. Si vous observez des erreurs ou retransmissions, revenez en arrière et examinez le réseau/MTU/offloads.
Task 14: Check for retransmits and timeouts (the “slow” that’s actually loss)
cr0x@client:~$ nfsstat -rc
Client rpc stats:
calls retrans authrefrsh
221004 932 0
Signification : Les retransmissions sont élevées. Ce n’est pas « NFS qui est NFS » ; c’est de la congestion, des pertes, des problèmes de MTU ou une voie serveur saturée.
Décision : Pausez le tuning. Corrigez la perte de paquets d’abord : compteurs du commutateur, stats NIC, pilote/firmware, cohérence MTU et QoS.
Task 15: Observe server-side NFSd in the kernel (quick sanity)
cr0x@nfs1:~$ sudo cat /proc/fs/nfsd/pool_stats
# pool packets arrived sockets enqueued woken queued timers
cpu0 120391 0 0 0 0
cpu1 118220 0 0 0 0
cpu2 119004 0 0 0 0
cpu3 121188 0 0 0 0
Signification : Si vous voyez de grosses files ou des problèmes de réveil, vous pouvez être limité par les threads ou faire face à de la contention.
Décision : Si les pool stats suggèrent une montée en file, augmentez les threads et vérifiez l’ordonnancement CPU/placement NUMA.
Task 16: Validate the server filesystem and mount options (barriers and journaling matter)
cr0x@nfs1:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /export/shared
/dev/md0 ext4 rw,relatime,errors=remount-ro
Signification : Le type de système de fichiers et les options influencent le comportement de flush. Ext4 est correct, mais votre périphérique bloc sous-jacent importe plus que la folklore Internet.
Décision : Si le périphérique bloc est un contrôleur RAID, vérifiez sa politique de cache et la protection batterie. S’il est virtualisé, vérifiez la pile stockage de l’hyperviseur et le mode de cache.
Corrections efficaces (client, serveur et stockage)
Une fois que vous avez prouvé le goulot, les corrections deviennent simples. Pas toujours faciles. Simples.
Catégorie A : rsize/wsize correctement configurés
Si vous avez prouvé un comportement lié aux frais généraux (tailles d’E/S petites), ajustez le montage. Restez sobre et reproductible.
- Commencez par
vers=4.2,proto=tcp,hard,timeo=600. - Définissez
rsize=262144,wsize=262144(ou 1 MiB si vous l’avez validé). - Utilisez
noatimeseulement si vous en comprenez les implications ; c’est généralement correct pour des données partagées mais peut surprendre certains workflows.
cr0x@client:~$ sudo mount -t nfs4 -o vers=4.2,proto=tcp,hard,timeo=600,retrans=2,rsize=262144,wsize=262144 nfs1:/export/shared /mnt/shared
cr0x@client:~$ nfsstat -m | head -n 2
/mnt/shared from nfs1:/export/shared
Flags: rw,relatime,vers=4.2,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.10.21,local_lock=none
À éviter : mélanges aléatoires d’options héritées copiées depuis un blog de 2009. Si vous ne pouvez pas expliquer pourquoi une option existe, ne la déployez pas.
Catégorie B : la manière honnête de rendre sync rapide
Quand sync est le goulot, il faut réduire le coût des commits durables.
1) Corriger la politique de cache d’écriture du stockage (commun dans RAID / SAN)
Si le cache d’écriture de votre contrôleur RAID est désactivé, les écritures sync vont ramer. Beaucoup de contrôleurs le désactivent quand le module batterie/flash est manquant ou dégradé. C’est le comportement correct, mais c’est aussi un événement de performance.
Action : vérifiez la santé du cache du contrôleur et la politique via les outils du vendeur. Puis retestez dd oflag=direct et surveillez la fréquence des commits.
2) Utiliser un journal d’intention dédié / dispositif de journal rapide quand approprié
Sur des systèmes de fichiers qui le supportent (notamment ZFS avec SLOG), vous pouvez décharger les écritures synchrones sur un dispositif à faible latence. Ce n’est pas « plus de SSD » ; c’est « le bon SSD au bon endroit ».
3) Vérifier que votre pile de virtualisation ne ment pas
Des disques virtuels avec « writeback cache » à un niveau et des attentes sync à un autre peuvent conduire soit à une sécurité factice soit à une lenteur factice. Décidez de ce qui est vrai, puis alignez les réglages de bout en bout.
Catégorie C : exporter en async (seulement si tolérable)
Soyons clairs : async peut rendre les écritures NFS ultra-rapides. Il peut aussi transformer une coupure d’alimentation en données corrompues d’une manière qui semble « correcte » jusqu’à ce qu’elle ne le soit plus. Si vous le faites, faites-le parce que la charge est jetable (artéfacts de build, caches, espace scratch) ou parce que la durabilité est assurée ailleurs.
cr0x@nfs1:~$ sudoedit /etc/exports
cr0x@nfs1:~$ sudo exportfs -ra
cr0x@nfs1:~$ sudo exportfs -v | grep -A1 '/export/shared'
/export/shared
10.10.10.0/24(async,wdelay,hide,no_subtree_check,sec=sys,rw,secure,root_squash,no_all_squash)
Décision : Ne déployez async que si le propriétaire des données l’a approuvé explicitement et que votre histoire de durabilité est documentée. Si l’histoire est « probablement OK », ce n’est pas une histoire ; c’est un futur incident.
Blague #2 : async c’est comme enlever la ceinture pour arriver plus vite à une réunion — vous serez en avance jusqu’au moment où vous ne l’êtes plus.
Catégorie D : tuner la concurrence du serveur NFS (lorsque CPU/threads sont limités)
Si vos tests montrent que le stockage est rapide mais que NFSd est limité par le CPU ou les threads, augmentez les threads et confirmez que vous ne créez pas de contention de locks.
cr0x@nfs1:~$ sudo systemctl edit nfs-server
cr0x@nfs1:~$ sudo systemctl show -p ExecStart nfs-server
ExecStart={ path=/usr/sbin/rpc.nfsd ; argv[]=/usr/sbin/rpc.nfsd 32 ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
Signification : Ici le serveur est configuré pour 32 threads. Cela peut aider les charges parallèles.
Décision : Si augmenter les threads améliore le débit sans augmenter latence ou retransmits, conservez-le. Si cela augmente la contention ou le CPU steal (en VM), revenez en arrière.
Catégorie E : arrêtez de vous saboter avec MTU et offloads incohérents
La cohérence MTU de bout en bout compte plus que les slogans « 9000 partout ». Si vous ne pouvez pas la garantir, gardez 1500 et passez à autre chose.
cr0x@client:~$ sudo ip link set dev enp65s0 mtu 1500
cr0x@client:~$ ip link show dev enp65s0 | grep mtu
2: enp65s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
Décision : Si les retransmissions diminuent et que NFS se stabilise, le plan « jumbo frames » n’était pas prêt pour la production.
Trois mini-récits du monde de l’entreprise
Mini-récit 1 : l’incident causé par une fausse supposition
L’entreprise avait une ferme de build écrivant des artéfacts sur un partage NFS. Les mises à jour Debian sont passées silencieusement, et l’équipe a célébré : noyau plus récent, NFS plus récent. Puis les builds ont ralenti. Pas échoué — juste douloureusement lents. La profondeur de file a augmenté, les ingénieurs ont relancé des jobs, ce qui a empiré la situation. Un ralentissement auto-amplifié classique.
La mauvaise supposition était simple : « les performances NFS, c’est surtout le réseau. » Ils ont passé une journée entière à changer des câbles, déplacer vers un autre commutateur top-of-rack, et discuter du hashing LACP. Les graphiques réseau étaient propres. Les compteurs de commutateur étaient propres. La seule chose qui n’était pas propre était le canal d’incident.
Quand quelqu’un a enfin exécuté un test d’écriture directe (dd oflag=direct) et l’a comparé à iperf3, la nature du problème est devenue claire. Les écritures étaient limitées par la latence. Les lectures allaient bien. L’équipe a lancé nfsstat et a vu des appels COMMIT. Sur le serveur, le stockage était un volume RAID dont le cache d’écriture avait été désactivé après qu’un module batterie soit entré dans un cycle « learning » et n’en soit jamais ressorti sans intervention manuelle.
La correction n’était pas exotique : restaurer la protection du cache d’écriture, vérifier la santé de la batterie, et retester. Les performances sont revenues. Le postmortem était encore plus ennuyeux : séparer toujours le débit réseau du coût de durabilité du stockage. Ils ont ajouté une étape standard « iperf + direct-write dd » à leur runbook, et l’incident suivant a été plus court et moins théâtral.
Mini-récit 2 : l’optimisation qui s’est retournée contre eux
Une autre organisation avait un cluster data science. Quelqu’un a lu que « async rend NFS rapide » et a modifié les exports pour le dataset principal partagé. C’était effectivement plus rapide. Tout le monde a applaudi. Les jobs finissaient plus tôt. Les tableaux de bord étaient beaux. Ils ont étendu le déploiement.
Deux semaines plus tard, un incident électrique a touché un rack. Pas catastrophique — juste assez pour redémarrer plusieurs nœuds y compris le serveur NFS. Le dataset ne semblait pas corrompu au début. Puis les runs de training ont commencé à produire des résultats incohérents. Certains runs échouaient avec des erreurs de format de fichier étranges. D’autres produisaient des modèles subtilement faux. C’est le genre de défaillance sournoise : qui passe en CI et échoue en production.
L’enquête a été douloureuse parce que rien n’annonçait « corruption ». Les fichiers étaient présents. Les permissions normales. Les checksums ne correspondaient pas aux valeurs historiques, mais seulement pour certains shards. La cause racine était l’export async : le serveur avait accusé réception d’écritures qui n’avaient jamais atteint le stockage stable avant la coupure. L’application avait supposé que « écriture retournée » signifiait durable. Cette supposition était raisonnable — jusqu’à ce que quelqu’un change le contrat.
Ils sont revenus à sync pour les datasets durables et ont créé un export distinct async pour l’espace scratch. La perte de performance a été acceptée car l’alternative était le chaos épistémologique. Ils ont aussi introduit des contrôles d’intégrité périodiques pour les datasets critiques. Cela ne les a pas rendus plus rapides, mais cela les a rendus corrects.
Mini-récit 3 : la pratique ennuyeuse mais correcte qui a sauvé la mise
Une équipe fintech utilisait NFS pour des bundles de configuration partagés et des artéfacts de déploiement. Ce n’était pas du stockage glamour. Le salut venait du fait qu’ils traitaient NFS comme une dépendance de production : montages documentés, versions figées, et un seul profil de montage « known-good » pour les clients Linux.
Pendant un déploiement Debian 13, une équipe applicative s’est plainte d’un démarrage lent, en accusant NFS. Au lieu d’un argument, le SRE de garde a suivi le runbook standard : confirmer la négociation de montage, lancer nfsiostat, comparer à la ligne de base. La ligne de base a compté — parce qu’ils en avaient une.
La sortie montrait quelque chose de subtil : des retransmissions montaient aux heures de pointe. Pas folles, mais non nulles. L’équipe réseau a trouvé un port de commutateur avec des erreurs intermittentes. Parce que l’équipe stockage pouvait prouver « c’est de la perte, pas un coût de sync », la correction a été un simple remplacement matériel, pas une semaine de tuning.
La pratique ennuyeuse était « garder une ligne de base et un profil de montage connu ». Cela a sauvé des jours de spéculation inter-équipes et évité le rituel habituel de sacrifier des options de montage au hasard.
Erreurs fréquentes : symptôme → cause → correctif
1) Écritures bloquées entre 20–80 MB/s sur des liens rapides
Symptôme : Les lectures saturent le lien, les écritures sont plates et basses.
Cause racine : Latence sync/commit dominée par le temps de flush du stockage serveur (cache d’écriture désactivé, journal lent, chemin sync sur disque dur).
Correctif : Mesurez le taux de COMMIT et le await serveur ; réparez le chemin de durabilité du stockage (protection cache contrôleur, dispositif de log plus rapide), ou isolez les workloads scratch en async.
2) Le débit augmente avec plus de clients, mais chaque client reste lent
Symptôme : Le débit agrégé grimpe, le flux unique reste décevant.
Cause racine : wsize/rsize trop petits, surcharge d’un flux unique, ou application effectuant de petites écritures synchrones.
Correctif : Augmentez rsize/wsize ; testez avec dd bs=1M et validez les valeurs négociées ; corrigez le pattern d’écriture de l’application si elle est fsync-heavy.
3) Arrêts aléatoires, messages « server not responding », puis récupération
Symptôme : Hangs périodiques, puis reprise.
Cause racine : Perte de paquets/retransmissions, incohérence MTU, bugs d’offload NIC, ou threads serveur surchargés entraînant des timeouts.
Correctif : Vérifiez nfsstat -rc retrans ; validez MTU de bout en bout ; contrôlez les compteurs d’erreurs NIC ; augmentez les threads serveur seulement après avoir écarté la perte.
4) Tuner rsize/wsize aggrave les choses
Symptôme : Des tailles plus grandes réduisent le débit ou augmentent la latence.
Cause racine : Problèmes de Path MTU, fragmentation, ou serveur incapable de traiter efficacement de grosses E/S (CPU bound, pression mémoire).
Correctif : Corrigez la cohérence MTU ; vérifiez les offloads NIC ; choisissez une taille modérée (256 KiB) et validez avec nfsstat -m.
5) « Async a corrigé » puis des incohérences de données plus tard
Symptôme : Écritures rapides, puis corruption ou mises à jour récentes manquantes après crash/panne de courant.
Cause racine : Vous avez changé le contrat de durabilité. Le système s’est comporté en conséquence.
Correctif : Utilisez sync pour les données durables ; isolez les exports scratch en async ; documentez les attentes de durabilité et testez le comportement en cas de crash si vous devez déroger.
6) Pics CPU sur le serveur NFS à débit modéré
Symptôme : Le stockage est plutôt inactif, le réseau n’est pas saturé, mais le CPU serveur est chaud.
Cause racine : Trop d’opérations petites (tailles d’E/S petites), threads nfsd insuffisants, ou chiffrement/Kerberos consommant le CPU.
Correctif : Augmentez rsize/wsize, augmentez les threads et mesurez à nouveau ; si vous utilisez Kerberos, budgetez le CPU en conséquence.
Checklists / plan pas à pas
Pas à pas : prouver que sync est le limiteur
- Sur le client : capturez la négociation de montage avec
nfsstat -m. - Sur le client : lancez
iperf3vers le serveur pour confirmer la capacité réseau. - Sur le client : lancez
dd oflag=directen écriture etdden lecture ; comparez les débits. - Sur le client et le serveur : vérifiez
nfsstatpour le taux d’appels COMMIT. - Sur le client : utilisez
nfsiostatpour comparer RTT vs temps d’exécution pour les écritures. - Sur le serveur : utilisez
iostat -xpour trouver un await/iowait élevé lors des écritures. - Décision : si les écritures sont liées à la latence avec commits, corrigez le chemin de stockage durable ou acceptez
asyncpour des workloads non durables.
Pas à pas : prouver que rsize/wsize est le limiteur
- Enregistrez les
rsize/wsizeactuels depuisnfsstat -m. - Faites un test lecture/écriture avec de grands blocs (
bs=4Men lecture,bs=1M oflag=directen écriture). - Remontez temporairement avec des
rsize/wsizeplus grands et vérifiez que la négociation n’a pas été rétrogradée. - Relancez les mêmes tests et comparez débit et utilisation CPU.
- Décision : conservez la plus petite taille atteignant le débit requis sans augmenter retransmits/latence.
Checklist de déploiement en production (pour ne pas « tuner » vers une panne)
- Choisissez un client et un share comme canari.
- Mesurez la ligne de base :
nfsstat -m,nfsstat -c,nfsiostat,iostat -xsur le serveur pendant la charge. - Changez une variable à la fois : soit
rsize/wsize, soit le comportement d’export sync, pas les deux. - Gardez une commande de rollback prête (anciennes options de montage, ancienne ligne d’export).
- Surveillez les retransmissions et la latence, pas seulement les Mo/s.
- Rédigez le contrat de durabilité pour chaque export (durable vs scratch).
FAQ
1) Dois-je utiliser NFSv3 ou NFSv4.2 sur Debian 13 ?
Par défaut, choisissez NFSv4.2 sauf contrainte de compatibilité spécifique. v4 offre une meilleure intégration (modèle à connexion TCP unique, état). Mais ne comptez pas sur le changement de version seul pour résoudre la latence de flush.
2) Si je mets rsize/wsize à 1 MiB, est-ce que ce sera toujours plus rapide ?
Non. Cela aide quand vous êtes lié par les frais généraux. Si vous êtes limité par la latence commit/flush, un wsize plus grand ne résoudra pas ce coût fondamental d’attente pour le stockage stable. De plus, si le chemin réseau a des problèmes MTU ou pertes, de grandes opérations peuvent amplifier la douleur.
3) Quelle est la différence entre « le serveur est lent » et « le stockage est lent » ?
Pour les écritures NFS, « le serveur est lent » signifie souvent « le serveur attend le flush du stockage ». Utilisez nfsiostat : un temps d’exécution élevé avec un RTT faible pointe vers une attente côté serveur, généralement le stockage.
4) async est-il toujours dangereux ?
C’est dangereux pour des workloads qui supposent que la complétion d’écriture implique la durabilité. Pour les caches, artéfacts de build, fichiers temporaires, cela peut être acceptable. Vous ne le rendez pas « sûr » en espérant ; vous le rendez « risque accepté » en choisissant les bonnes données.
5) Pourquoi je vois des appels COMMIT même en NFSv4 ?
NFSv4 peut encore exiger un comportement de type commit selon la manière dont client et serveur gèrent les garanties de stockage stable. Si le serveur accuse réception des données comme instables, les clients émettront des commits pour les rendre stables.
6) Mon appli est lente seulement sur NFS, mais le disque local va bien. Quelle est la cause la plus fréquente ?
Des patterns fsync-heavy. Le disque local peut avoir un cache d’écriture rapide et des flushs à faible latence, tandis que le chemin de stockage stable du serveur NFS est plus lent. Prouvez-le avec strace sur l’appli et iostat sur le serveur.
7) Dois-je désactiver atime ou utiliser noatime pour accélérer ?
Ça peut réduire les écritures metadata, mais c’est rarement le goulot principal dans des incidents « écritures à 40 MB/s ». Réglez d’abord la latence des commits et la taille d’E/S. Ensuite, envisagez atime si la charge metadata est mesurée et réelle.
8) J’ai augmenté les threads serveur NFS et rien n’a changé. Pourquoi ?
Parce que vous étiez lié par la latence du stockage, pas par les threads. Plus de threads vous donnent juste plus d’attente concurrente. Utilisez nfsiostat et iostat -x serveur pour voir si vous attendez des flushs disques.
9) Les jumbo frames peuvent-elles réparer un NFS lent ?
Parfois, pour des cas CPU-bound à haut débit. Mais une incohérence MTU crée des pertes et retransmits qui ressemblent à des pauses aléatoires. Si vous ne pouvez pas garantir le MTU de bout en bout, restez à 1500 et concentrez-vous sur le limiteur réel.
Étapes suivantes concrètes
Un NFS lent sur Debian 13 n’est rarement mystérieux. C’est généralement l’un des trois : coût sync/commit, rsize/wsize sous-dimensionnés, ou perte réseau masquée derrière « ça va probablement ». L’astuce est de refuser de traiter des suppositions comme des données.
Faites ceci ensuite, dans cet ordre :
- Capturez la négociation de montage client (
nfsstat -m) et les options d’export serveur (exportfs -v). - Prouvez le réseau avec un test de débit TCP, puis arrêtez de parler des câbles.
- Exécutez des tests appariés d’écriture directe et de lecture. Si les écritures sont lentes et les lectures rapides, orientez-vous vers les preuves de commit/flush.
- Utilisez
nfsstatetnfsiostatpour décider si vous êtes lié par la latence ou par les frais généraux. - Corrigez le goulot prouvé : chemin stockage durable pour workloads sync, ou taille de montage pour workloads liés aux frais généraux.
- Si vous choisissez
async, faites-le uniquement pour des données périssables, et documentez ce choix comme un adulte.
Si vous suivez cette discipline, « NFS est lent » passe d’une plainte vague à un comportement système mesurable avec une vraie correction. C’est le travail.