Debian 13: NFS повільніший, ніж очікували — доведіть, що це через sync/rsize/wsize, і виправте (випадок №23)

Було корисно?

Немає нічого більш деморалізуючого, ніж зовсім нова система на Debian 13 з швидким мережевим інтерфейсом і здоровим масивом зберігання, але продуктивністю NFS, яка виглядає так, ніби передається факсом. Читання — «досить нормально». Записи — трагедія. І кожен має свою теорію, жодна з яких не підкріплена доказами.

Це саме той випадок, коли треба припинити гадати і почати доводити. Ми покажемо, як продемонструвати — за допомогою статистики, трас і контрольованих тестів — що ваш вузький профіль зазвичай належить одній із трьох причин: синхронній семантиці (sync/async плюс поведінка commit), занадто малим rsize/wsize або невідповідності очікувань клієнта й реалій зберігання на сервері. Потім ми виправимо це, не брехаючи собі щодо надійності даних.

Швидкий план діагностики

Якщо ви на виклику і хтось дихає у канал інцидентів, це найкоротший шлях до правди. Не починайте з зміни опцій монтування. Почніть із вимірювання одного параметра за раз.

Перш за все: підтвердьте, що ви змонтували і що експортували

  1. Клієнт: підтвердьте версію NFS і опції монтування (nfs4 проти nfs, rsize/wsize, поведінка sync через hard/soft і таймаути).
  2. Сервер: підтвердьте опції експорту і чи змушує сервер синхронну поведінку (за замовчуванням sync) і чи може він безпечно кешувати.

По-друге: визначте, чи ви обмежені затримкою (sync) або пропускною спроможністю (розмір I/O)

  1. Запустіть тест на буферизоване читання і тест на пряму (direct) запис з клієнта. Якщо читання досягають пропускної спроможності лінії, а записи повзають — підозрюйте синхронні підтвердження.
  2. Перевірте nfsstat -c на частоту викликів COMMIT. Висока частота COMMIT — це неоновий знак.

По-третє: ізолюйте мережу від диска сервера

  1. Мережа: перевірте, чи можете ви передавати пакети простим TCP-тестом (не «ping», не надія).
  2. Диск сервера: перевірте, чи чекає NFSd на флаші зберігання (типово для HDD, RAID-контролерів з відключеним write cache або віртуалізованого зберігання).

По-четверте: лише тоді налаштовуйте

  • Якщо ви довели, що commits — обмежувач: виправте шлях до надійного зберігання (write cache на сервері, SLOG, налаштування контролера) або прийміть контрольований ризик (async тільки якщо навантаження це дозволяє).
  • Якщо ви довели, що обмежує малий розмір I/O: збільшіть rsize/wsize, перевірте MTU і offloads, які можуть вам шкодити, і переконайтесь, що сервер підтримує ці розміри.

Модель продуктивності: де NFS насправді витрачає час

Дискусії про продуктивність NFS часто емоційні, бо люди плутають «моє застосування повільне» з «мережа повільна» або «зберігання повільне». NFS — це всі три разом, зв’язані контрактом про консистентність.

На високому рівні запис у NFS може проходити так:

  • Відправлений з клієнта на сервер чанками розміром, визначеним wsize.
  • Підтверджений сервером або як «я отримав це», або як «це на стабільному носії», залежно від версії NFS, опцій експорту та реалізації сервера.
  • Проміжно закомічений (явно через операції COMMIT в NFSv3 та NFSv4 коли потрібно) на стабільне зберігання.

Якщо сервер має часто скидати дані на стабільне зберігання, ви обмежені затримкою. Якщо кожен флаш займає 2–10 ms і ви робите це тисячі разів, ваша пропускна спроможність математично приречена. Ви можете мати 100 GbE і все одно записувати «кілька десятків МБ/с», бо фізика не веде переговорів.

Якщо rsize/wsize маленькі (8K, 16K, 32K), ви обмежені накладними витратами. Вартість обробки RPC, затримка на виклик і облік у ядрі стають вузьким місцем, навіть якщо зберігання швидке.

Debian 13 не змінює ці закони. Він просто дає новіші ядра, нову поведінку клієнта NFS і трохи інші значення за замовчуванням, які можуть виставити на показ те, що раніше приховувалося везінням.

Одна цитата, яку варто тримати в голові під час налаштування у продукції:

«Надія — це не стратегія.» — генерал Gordon R. Sullivan

Цікаві факти та історія (що дійсно допомагають)

  • NFS був розроблений у 1980-х щоб робити віддалені файли відчутними локальними у LAN, які тоді були повільні в сучасних вимірюваннях. Багато його семантики припускають, що затримка прийнятна, якщо зберігається консистентність.
  • NFSv3 ввів концепт COMMIT, щоб клієнти могли пайплайнити записи, але все ще просити міцний commit пізніше, тому «COMMIT-шторми» — впізнаваний режим відмови.
  • NFSv4 інтегрував «mount» у протокол і ввів станове блокування та сесії; лічильники продуктивності та режими відмов відрізняються від v3 і це видно в nfsstat.
  • sync історично є безпечним за замовчуванням на багатьох NFS серверах, бо воно відповідає очікуванням користувача: «коли запис повернувся — ймовірно, все в безпеці». Ця безпека має ціну.
  • async експорти можуть бути дуже швидкими, бо дозволяють серверу підтверджувати записи до попадання на стабільне зберігання. Вони також можуть перетворити втрату живлення на втрату даних у спосіб, що «повністю консистентний» з порушеним контрактом.
  • Розміри rsize/wsize раніше були обмежені старими NIC, MTU і обмеженнями ядра. Сучасний Linux може працювати з великими розмірами, але вам все одно потрібна сумісність на всьому шляху.
  • «Jumbo frames» не є магічним заклинанням. Вони допомагають у деяких випадках, коли навантаження CPU є вузьким місцем, але також створюють path-MTU black hole і дивні втрати, якщо один порт комутатора залишився на 1500.
  • Linux NFS пройшов кілька поколінь клієнтського коду; «це працювало на Debian 10» не доводить, що ваше навантаження в порядку — це доводить, що ваша стара конфігурація терпіча.
  • Writeback caching — це проблема зберігання, захована під файловою системою. RAID-контролер з відключеним write cache може зробити «sync» повільним навіть на SSD.

Жарт №1: Налаштування NFS — як регулювання змішувача душу в старому готелі — один міліметр в сторону і ви або мерзнете, або отримуєте опіки.

Доведіть, що це через sync/commit

Ви не доводите «sync — проблема», голосно оголошуючи це в Slack. Ви доводите це, корелюючи латентність запису, частоту commit і час флашу зберігання на сервері.

Що тут означає «sync»

На стороні сервера поведінка експорту sync зазвичай означає, що сервер не відповість «готово», доки не зможе гарантувати, що дані надійно записані на стабільне зберігання (або принаймні настільки стабільно, наскільки стверджує стек зберігання сервера). Це часто передбачає флаш кешу або еквівалентний бар’єр.

У багатьох налаштуваннях вбивцею є не мережевий трансфер, а гарантія стабільного зберігання. Якщо ваше підлегле зберігання не може виконувати флаші швидко, кожен «безпечний» запис стає мікротранзакцією, що чекає, поки диск закінчить медитувати.

Підпис синхронного болю

  • Велика пропускна здатність читання виглядає нормально; велика пропускна здатність запису низька і рівнинна.
  • Пропускна здатність запису практично не збільшується з більшими файлами або кількома потоками (або зростає трохи і потім плато).
  • nfsstat показує нетривіальну частоту викликів COMMIT.
  • Метрики зберігання сервера показують високу латентність флашу або бар’єрів запису (навіть коли сирі IOPS здаються «добре»).

Що робити з цим підтвердженням

У вас є три чесні варіанти:

  1. Зробити стабільне зберігання швидким (правильні SSD, кеш контролера з батарейним/флешевим захистом, ZFS SLOG тощо).
  2. Змінити навантаження (пакетні записи, зменшити частоту fsync, писати локально та потім переміщувати, використовувати об’єктне зберігання для логів тощо).
  3. Послабити контракт безпеки (async або забезпечення на рівні застосунку) з відкритими очима та в письмовому вигляді.

Доведіть, що це через rsize/wsize

rsize і wsize визначають максимальний розмір корисного навантаження для операцій NFS READ і WRITE. Занадто малі — і ви тонуєте в RPC-накладних витратах. Достатньо великі — і ви використовуєте пропускну спроможність, за яку платите.

Але є підступ: великі розміри I/O допомагають лише якщо ви не обмежені затримкою commit, і якщо шлях (клієнт, сервер, мережа) справно їх обробляє.

Підпис болю від rsize/wsize

  • Використання CPU на клієнті або сервері швидко зростає з ростом пропускної спроможності.
  • У nfsstat видно багато дрібних операцій NFS в секунду.
  • Пропускна спроможність значно покращується при збільшенні rsize/wsize.
  • Мережа недовикористана, незважаючи на низьку латентність диска і достатній CPU.

Як виглядають «хороші» розміри на сучасному Linux

На типовій сучасній мережі (1/10/25 GbE) і ядрах значення на кшталт 262144 (256 KiB) є частими точками початку для NFSv4. Деякі середовища йдуть далі, але гнатися за максимальними значеннями менш корисно, ніж перевірити, що погоджений розмір відповідає вашим очікуванням.

Практичні завдання: команди, виводи, що вони означають, і рішення

Це завдання, які я реально запускаю, коли хтось каже «NFS повільний» і очікує виправлення. Кожне завдання має точку рішення. Якщо ви не змінюєте рішення на основі виводу — ви не діагностуєте, ви екскурсіонуєте.

Завдання 1: Підтвердьте, що ви змонтували NFS (версія, 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

Значення: Це істина про те, що ядро погодило. Якщо ви очікували NFSv3 або інших розмірів — реальність не погоджується.

Рішення: Якщо rsize/wsize малі (наприклад, 32768), плануйте тестування з більшими розмірами. Якщо версія не та, що ви очікували — виправте це спочатку.

Завдання 2: Підтвердьте опції експорту сервера (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)

Значення: Експорт — sync. Це безпечно, і може бути повільним залежно від продуктивності флашу зберігання.

Рішення: Не перемикайтесь на async ще. Спочатку доведіть, що sync — вузьке місце за допомогою доказів commit/латентності.

Завдання 3: Перевірте базові навантаження нитей NFS та RPC на сервері

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

Значення: Якщо commit нетривіальний відносно write — можливо, ви платите за стабільне зберігання часто.

Рішення: Якщо викликів commit багато — пріоритезуйте дослідження sync/флашу. Якщо commit майже нульовий — зосередьтесь на розмірі I/O або мережі.

Завдання 4: Шукайте COMMIT-шторм на клієнті теж

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

Значення: Існують виклики commit, а retrans малі. Це не проблема втрат в мережі; ймовірно це вартість забезпечення надійності/флашу.

Рішення: Рухайтесь до вимірювання латентності флашу в стеку зберігання сервера.

Завдання 5: Перевірте, чи ви випадково не використовуєте додібну до «sync» поведінку застосунку (fsync-важкий)

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>

Значення: Застосунок багато викликає fsync. На NFS це може перетворюватись у commits/флаші. Ви можете налаштовувати NFS весь день і все одно програти.

Рішення: Якщо навантаження fsync-важке (бази даних, журналювання), ви повинні зробити серверне стабільне зберігання швидким або змінити стратегію надійності застосунку.

Завдання 6: Запустіть контрольований тест запису, що обходить 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

Значення: ~41 MB/s на ймовірно швидшому лінку — підозріло. oflag=direct робить цей тест більш чутливим до поведінки commit сервера.

Рішення: Якщо direct-записи повільні при швидкій мережі — зосередьтесь на флаші зберігання сервера і семантиці sync.

Завдання 7: Запустіть контрольований тест на буферизоване читання для порівняння

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

Значення: Читання близькі до швидкості лінії; записи — ні. Така асиметрія кричить «шлях commit/флаш/надійність».

Рішення: Припиніть звинувачувати мережу.

Завдання 8: Підтвердіть пропускну здатність мережі окремо (прямий TCP-тест)

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

Значення: Мережа в порядку. Ваші 40 MB/s NFS-записів — не проблеми кабелю.

Рішення: Інвестуйте час у латентність флашу зберігання і семантику запису NFS.

Завдання 9: Перевірте узгодженість MTU (бо хтось завжди «допоміг»)

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

Значення: Невідповідність MTU. Це може викликати фрагментацію, втрати і дивну поведінку продуктивності — особливо якщо проміжне обладнання налаштоване інакше.

Рішення: Або встановіть MTU 1500 скрізь (нудно, надійно), або ввімкніть jumbo frames у всіх точках і підтвердіть це захопленням пакету та конфігурацією комутаторів.

Завдання 10: Спостерігайте латентність на операцію за допомогою 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

Значення: Записи мають набагато більший час виконання, ніж RTT. Це часто вказує на очікування на стороні сервера (флаш/commit), а не на мережеву затримку.

Рішення: Зосередьтесь на стороні сервера: латентність зберігання, політика кеша запису, поведінка файлової системи та ниті NFSd.

Завдання 11: Спостерігайте латентність флашу диска сервера в реальному часі

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

Значення: Високий await при помірному використанні означає, що латентність приходить від флашу/бар’єрів або поведінки стеку зберігання, а не від сирого насичення каналу.

Рішення: Дослідіть політику кеша запису контролера, файлову систему і чи є «брехня» про стабільне зберігання (або її відсутність).

Завдання 12: Перевірте, чи сервер не вичерпав ниті NFS або не досягає меж CPU

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

Значення: 8 нитей може бути достатньо або недостатньо в залежності від навантаження і CPU. Якщо ниті nfsd завантажені, поки зберігання проста — ви обмежені CPU/нитями.

Рішення: Якщо ви обмежені CPU: збільшіть кількість нитей і налаштуйте мережу; якщо обмежені зберіганням: ниті не врятують вас.

Завдання 13: Підтвердіть погоджені rsize/wsize шляхом явного перемонтування (тестуйте, не припускайте)

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

Значення: Ядро прийняло розміри 1 MiB. Якщо воно тихо понизить значення — ви побачите це тут.

Рішення: Якщо продуктивність значно покращується і частота commit залишається розумною — залиште більші розміри. Якщо бачите помилки або retransmits — поверніться назад і дослідіть мережу/MTU/offloads.

Завдання 14: Перевірте retransmits і таймаути ( «повільно», яке насправді — втрата пакетів)

cr0x@client:~$ nfsstat -rc
Client rpc stats:
calls      retrans    authrefrsh
221004     932        0

Значення: Retransmits високі. Це не «NFS як він є»; це затори, втрати, проблеми MTU або насичений шлях сервера.

Рішення: Призупиніть налаштування. Виправте втрати пакетів перш ніж робити інші кроки: лічильники комутаторів, статистику NIC, драйвер/прошивка, узгодженість MTU та QoS.

Завдання 15: Спостерігайте nfsd на сервері в ядрі (швидка перевірка здоров’я)

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

Значення: Якщо ви бачите великі черги або проблеми з розбудженням, можливо ви обмежені нитями або бачите контенцію.

Рішення: Якщо pool stats натякають на накопичення черг — збільшіть ниті і перевірте розподіл CPU/NUMA.

Завдання 16: Підтвердіть файлову систему сервера і опції монтування (бар’єри та журналювання важливі)

cr0x@nfs1:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /export/shared
/dev/md0 ext4 rw,relatime,errors=remount-ro

Значення: Тип файлової системи і опції впливають на поведінку флашу. Ext4 підходить, але ваша блочна політика важливіша за інтернет-міфи.

Рішення: Якщо блочний пристрій — RAID-контролер, перевірте політику кеша і здоров’я батареї/модуля. Якщо це віртуалізовано — перевірте стек зберігання гіпервізора і режим кешування.

Виправлення, які працюють (клієнт, сервер і зберігання)

Після того як ви довели вузьке місце, виправлення стають простими. Не завжди легкими. Простими.

Категорія виправлень A: правильна робота з rsize/wsize

Якщо ви довели, що обмежує накладні витрати (малий розмір I/O), налаштуйте монтування. Тримайте це нудним і відтворюваним.

  • Почніть з vers=4.2,proto=tcp,hard,timeo=600.
  • Встановіть rsize=262144,wsize=262144 (або 1 MiB, якщо ви це валідували).
  • Використовуйте noatime лише якщо розумієте наслідки; зазвичай воно безпечне для загальних даних, але може здивувати деякі робочі процеси.
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

Чого уникати: випадкові мікси застарілих опцій, скопійованих з блогу 2009 року. Якщо ви не можете пояснити, навіщо опція — не випускайте її в продакшн.

Категорія виправлень B: чесний шлях зробити sync швидким

Коли sync — вузьке місце, потрібно зменшити вартість міцних commits.

1) Виправте політику кеша запису на зберіганні (поширено у RAID / SAN)

Якщо кеш запису на RAID-контролері вимкнений, синхронні записи будуть повзати. Багато контролерів вимикають його, коли батарея/флеш-модуль відсутні або деградовані. Це правильна поведінка, але також подія продуктивності.

Дія: перевірте здоров’я кешу контролера і політику за допомогою інструментів вендора. Потім повторно протестуйте dd oflag=direct і спостерігайте частоту commit.

2) Використайте окремий пристрій журналу/інтент-лог, коли доречно

На файлових системах, що це підтримують (особливо ZFS з SLOG), ви можете відвантажити synchronous intent на низьколатентний пристрій. Це не «ще більше SSD»; це «правильний SSD у правильному місці».

3) Переконайтесь, що стек віртуалізації не бреше

Віртуальні диски з «writeback cache» на одному рівні і очікуванням «sync» на іншому можуть призвести або до фіктивної безпеки, або до фіктивної повільності. Визначте, що істинне, і вирівняйте налаштування по всьому шляху.

Категорія виправлень C: експортування як async (тільки якщо ви це можете дозволити)

Будьмо чесні: async може зробити NFS-записи дуже швидкими. Він також може перетворити раптову втрату живлення на корумповані дані застосунку, що виглядають «нормально», поки не перестануть. Якщо ви це робите — робіть це тільки для навантажень, що можуть втратити дані (build артефакти, кеші, тимчасова область), або коли надійність обробляється деінде.

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)

Рішення: Впроваджуйте async лише якщо це офіційно погоджено власником даних і ваша історія надійності записана. Якщо історія «ймовірно, ок» — це не історія; це майбутній інцидент.

Жарт №2: async — це як зняти ремінь безпеки, щоб приїхати на зустріч швидше — ви будете ранiше, поки не станеться лихо.

Категорія виправлень D: налаштування конкуренції сервера NFS (коли ви довели обмеження CPU/нитей)

Якщо тести показують, що зберігання швидке, але NFSd обмежений CPU або нитями, збільште ниті і переконайтесь, що ви не створюєте контенцію замків.

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 }

Значення: Тут сервер сконфігурований на 32 ниті. Це може допомогти паралельним навантаженням.

Рішення: Якщо збільшення нитей покращує пропускну здатність без підвищення латентності або retransmits — залиште так. Якщо це підвищує контенцію або steal CPU (у ВМ), поверніться назад.

Категорія виправлень E: припиніть саботувати себе невідповідним MTU та offloads

Узгодженість MTU по всьому шляху важливіша за лозунги «9000 скрізь». Якщо ви не можете це гарантувати — тримайтесь 1500 і живіть далі.

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

Рішення: Якщо retransmits зменшаться і NFS стабілізується — план з jumbo frames не був готовий для продукції.

Три міні-історії з корпоративного світу

Міні-історія 1: Інцидент через неправильне припущення

Компанія мала фарм збірок, що писав артефакти на NFS-шару. Оновлення Debian пройшли тихо, і команда святкувала: новіше ядро, новіший NFS. Потім збірки сповільнились. Не впали — просто болісно повільні. Черга зросла, інженери почали перезапускати задачі, що ситуацію погіршило. Класичне само-підсилююче уповільнення.

Неправильне припущення було просте: «продуктивність NFS — це більше мережа». Вони витратили цілий день на перестановку кабелів, переміщення до іншого top-of-rack комутатора і суперечки про LACP hashing. Графіки мережі були чисті. Лічильники комутатора чисті. Єдине, що було не в порядку — канал інцидентів.

Коли хтось нарешті запустив direct-write тест (dd oflag=direct) і порівняв його з iperf3, форма проблеми стала очевидною. Записи були обмежені затримкою. Читання були в порядку. Команда запустила nfsstat і побачила COMMIT виклики. На сервері зберігання був RAID-том, у якого кеш запису був відключений після того, як батарея перейшла в «learning cycle» і ніколи не повернулась до нормального стану без ручного втручання.

Виправлення не було екзотичним: відновити захист кешу запису, перевірити стан батареї і повторно протестувати. Продуктивність повернулась. Постмортем урок був нудніший: завжди розділяйте мережеву пропускну здатність від вартості зберігання для sync. Вони додали стандартний крок «iperf + direct-write dd» у свій runbook, і наступний інцидент був коротшим і менш театральним.

Міні-історія 2: Оптимізація, що дала відкат

Інша організація мала кластер для Data Science. Хтось прочитав, що «async робить NFS швидким» і змінив експорти для основного спільного датасету. Це стало швидким. Всі аплодували. Завдання завершувались раніше. Дашборди виглядали чудово. Вони розгорнули це ширше.

Два тижні потому подія з електропостачанням в стійці сталася. Не катастрофа — просто рестарт кількох вузлів включно з NFS-сервером. Набір даних спочатку не виглядав корумпованим. Потім тренування моделей почали давати неконсистентні результати. Деякі запуски падали з дивними помилками формату файлів. Інші давали модель з тонкими помилками. Це гарний вид відмови: той, що проходить CI і провалює реальність.

Розслідування було складним, бо нічого не кричало «корупція». Файли були на місці. Права в порядку. Контрольні суми не відповідали історичним значенням, але лише для певних шардів. Корінна причина — async експорт: сервер підтверджував записи, які ніколи не потрапили на стабільне зберігання перед відключенням живлення. Застосунок припускав, що «запис повернувся» означає, що дані надійні. Це припущення було розумним — поки контракт не змінили.

Вони повернули sync для критичних датасетів і створили окремий async експорт для тимчасової області. Прийняли падіння продуктивності, бо альтернатива була епістемологічним хаосом. Також введено періодичні перевірки цілісності для критичних наборів даних. Це не зробило їх швидшими, але зробило правильними.

Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію

Фінтех-команда використовувала NFS для спільних конфігураційних бандлів і артефактів деплойменту. Це не було гламурним зберіганням. Спасінням було те, що вони ставилися до NFS як до продакшн-залежності: задокументовані монтування, зафіксовані версії і один «відомо робочий» профіль монтування для Linux-клієнтів.

Під час rollout Debian 13 одна команда поскаржилась на повільний старт, звинувачуючи NFS. Замість суперечки SRE взяв стандартний runbook: підтвердити погодження монтування, запустити nfsiostat, порівняти з базою. База була важливою — бо вона існувала.

Вивід показав тонку деталь: retransmits піднімались у пікові години. Не критично, але не нуль. Команда мережі знайшла порт комутатора з періодичними помилками. Оскільки команда зберігання могла довести «це втрата, не вартість sync», виправлення було простим — заміна апаратної частини — а не тиждень тонких налаштувань.

Нудна практика — «тримати базу і відомий профіль монтування» — врятувала дні переконань між командами і запобігла звичайному ритуалу випадкового набору опцій.

Типові помилки: симптом → корінна причина → виправлення

1) Записи застрягли на 20–80 MB/s на швидких лінках

Симптом: Читання насичують лінк, записи плоскі й низькі.

Корінна причина: Затримка sync/commit домінує через флаш зберігання сервера (write cache вимкнено, повільний журнал, шлях sync на HDD).

Виправлення: Виміряйте частоту COMMIT і await на сервері; виправте шлях надійного зберігання (захист кеша контролера, швидший log-пристрій) або ізолюйте тимчасові навантаження в async.

2) Пропускна спроможність зростає з кількістю клієнтів, але кожен клієнт повільний

Симптом: Агрегована пропускна здатність росте, але один потік лишається повільним.

Корінна причина: Малий wsize/rsize, накладні витрати для одного потоку або застосунок робить дрібні синхронні записи.

Виправлення: Збільшіть rsize/wsize; тестуйте з dd bs=1M і перевіряйте погоджені розміри; виправте патерн запису застосунку, якщо він fsync-важкий.

3) Випадкові паузи, повідомлення «server not responding», потім відновлення

Симптом: Періодичні зависання, потім відновлення.

Корінна причина: Втрата пакетів/retransmits, невідповідність MTU, баги NIC offload або перевантажені ниті сервера викликають таймаути.

Виправлення: Перевірте nfsstat -rc на retrans; підтвердіть MTU; перевірте лічильники NIC; збільшіть ниті сервера лише після виключення втрат.

4) Налаштування rsize/wsize робить все гірше

Симптом: Більші розміри зменшують пропускну здатність або підвищують латентність.

Корінна причина: Проблеми Path MTU, фрагментація або сервер не вміє ефективно обробляти великі I/O (обмеження CPU, тиск пам’яті).

Виправлення: Виправте узгодженість MTU; підтвердіть offloads NIC; оберіть помірний розмір (256 KiB) і валідуйте з nfsstat -m.

5) «Async це вирішило», а потім пізніше — дивні дані

Симптом: Швидкі записи, пізніше корупція або відсутні останні оновлення після аварії/відключення живлення.

Корінна причина: Ви змінили контракт надійності. Система повелася відповідно.

Виправлення: Використовуйте sync для надійних даних; ізолюйте тимчасові експорти в async; документуйте очікування щодо надійності і тестуйте поведінку при збоях.

6) Сплески CPU на NFS-сервері при помірній пропускній здатності

Симптом: Зберігання відносно спокійне, мережа не насичена, але CPU сервера гарячий.

Корінна причина: Забагато дрібних операцій (малі розміри I/O), недостатньо нитей nfsd або накладні витрати на шифрування/krb.

Виправлення: Збільшіть rsize/wsize, збільшіть ниті, і знову виміряйте; якщо використовуєте Kerberos, сплануйте витрати CPU відповідно.

Чек-листи / покроковий план

Покроково: доведіть, що sync — обмежувач

  1. На клієнті: зафіксуйте погодження монтування за допомогою nfsstat -m.
  2. На клієнті: запустіть iperf3 до сервера, щоб підтвердити мережеву пропускну здатність.
  3. На клієнті: запустіть dd oflag=direct запис і dd читання; порівняйте пропускні здатності.
  4. На клієнті та сервері: перевірте nfsstat на частоту COMMIT викликів.
  5. На клієнті: використайте nfsiostat, щоб порівняти RTT та час виконання для записів.
  6. На сервері: використайте iostat -x, щоб знайти високий await/iowait під час записів.
  7. Рішення: якщо записи обмежені затримкою з commits — виправте шлях надійного зберігання або прийміть контрольований async для недовговічних навантажень.

Покроково: доведіть, що rsize/wsize — обмежувач

  1. Запишіть поточні rsize/wsize з nfsstat -m.
  2. Запустіть read/write тест з великими розмірами блоків (bs=4M читання, bs=1M oflag=direct запис).
  3. Тимчасово перемонтуйтесь з більшими rsize/wsize і перевірте, що погодження не було понижене.
  4. Повторіть ті ж тести і порівняйте пропускну здатність та використання CPU.
  5. Рішення: залишайте найменший розмір, який дає потрібну пропускну здатність без підвищення retransmits/латентності.

Чек-лист для розгортання в продукції (щоб ви не «налаштували» собі відмову)

  • Виберіть одного клієнта і одну шару як канарку.
  • Виміряйте базовий стан: nfsstat -m, nfsstat -c, nfsiostat, iostat -x на сервері під навантаженням.
  • Змінюйте одну змінну за раз: або rsize/wsize, або поведінку експорту sync/async, а не обидва одночасно.
  • Тримайте команду відкату готовою (старі опції монтування, старий рядок експорту).
  • Слідкуйте за retransmits і латентністю, а не тільки за MB/s.
  • Запишіть контракт надійності для кожного експорту (надійні vs тимчасові).

Поширені запитання

1) Чи варто використовувати NFSv3 або NFSv4.2 на Debian 13?

За замовчуванням віддавайте перевагу NFSv4.2, якщо немає конкретної сумісної причини інакше. v4 має кращу інтеграцію (модель одного TCP-з’єднання, станове управління). Але не очікуйте, що зміна версії сама по собі виправить латентність флашу.

2) Якщо я встановлю rsize/wsize в 1 MiB, чи буде це завжди швидше?

Ні. Це допомагає, коли ви обмежені накладними витратами. Якщо ви обмежені латентністю commit/флашу, більший wsize не вирішить основну вартість очікування на стабільне зберігання. Також якщо шлях мережі має проблеми MTU або втрати, більші операції можуть посилити проблему.

3) У чому різниця між «сервер повільний» і «зберігання повільне»?

Для записів NFS «сервер повільний» часто означає «сервер чекає на флаш зберігання». Використайте nfsiostat: високий час виконання з низьким RTT вказує на очікування на стороні сервера, зазвичай на зберігання.

4) Чи є async завжди небезпечним?

Це небезпечно для навантажень, що припускають, що повернення запису означає довговічність. Для кешів, артефактів збірки, тимчасових даних це може бути прийнятним. Ви не робите його «безпечним», поки не сподіваєтесь; ви робите його «прийнятим ризиком», обираючи правильні дані.

5) Чому я бачу COMMIT виклики навіть на NFSv4?

NFSv4 може все одно вимагати поведінки, схожої на commit, залежно від того, як клієнт і сервер керують гарантіями стабільного зберігання. Якщо сервер підтверджує дані як нестабільні, клієнти будуть ініціювати commit, щоб зробити їх стабільними.

6) Мій застосунок повільний лише на NFS, але на локальному диску все гаразд. Яка найпоширеніша причина?

Патерни з великою кількістю fsync. Локальний диск може мати швидкий кеш запису і низьку латентність флашу, тоді як шлях до стабільного зберігання на NFS-сервері повільніший. Доведіть це за допомогою strace на застосунку і iostat на сервері.

7) Чи варто відключити atime або використовувати noatime для прискорення?

Це може зменшити метадані записи, але рідко є основним вузьким місцем у інцидентах «записи 40 MB/s». Спочатку виправте латентність commit і розмір I/O. Потім розгляньте atime, якщо метадані реально виміряно навантажують систему.

8) Я збільшив ниті NFS сервера і нічого не змінилось. Чому?

Тому що ви були обмежені латентністю зберігання, а не нитями. Більше нитей просто дає більше одночасних очікувань. Використайте nfsiostat і iostat -x на сервері, щоб побачити, чи ви дійсно чекаєте на диск.

9) Чи можуть jumbo frames виправити повільний NFS?

Іноді, у випадках, коли вузьким місцем є CPU при високій пропускній здатності. Але невідповідність MTU створює втрати і retransmits, що виглядають як випадкові паузи. Якщо ви не можете гарантувати end-to-end MTU — тримайтесь 1500 і зосередьтесь на реальному обмежувачі.

Наступні кроки, які ви реально можете виконати

Повільний NFS на Debian 13 рідко буває містичним. Зазвичай це одне з трьох: вартість sync/commit, занадто малі rsize/wsize або втрата пакетів, яку ховають під «мабуть, усе гаразд». Секрет — відмова трактувати припущення як дані.

Зробіть це наступним чином, у порядку:

  1. Зафіксуйте погодження монтування на клієнті (nfsstat -m) і опції експорту на сервері (exportfs -v).
  2. Доведіть мережу TCP-тестом пропускної здатності, потім припиніть говорити про кабелі.
  3. Запустіть парні direct-write і read тести. Якщо записи повільні, а читання — швидкі, переключайтесь на докази commit/флашу.
  4. Використайте nfsstat і nfsiostat, щоб вирішити, чи ви обмежені затримкою, чи накладними витратами.
  5. Виправте доведений обмежувач: шлях до надійного зберігання для sync-навантажень або розмір монтування для навантажень, обмежених накладними витратами.
  6. Якщо обираєте async, робіть це лише для даних, які можна втратити, і задокументуйте це як дорослі люди.

Якщо ви дотримуєтесь цієї дисципліни, «NFS повільний» перетворюється з розмитої скарги на вимірювану поведінку системи з реальним виправленням. Ось ваша робота.

← Попередня
Штучний інтелект на GPU: як ваша відеокарта стала домашнім суперкомп’ютером
Наступна →
Ubuntu 24.04: Як посилити SSH, не втративши доступ — прагматичний чекліст

Залишити коментар