Тай‑аути Docker NFS томів — опції монтування, що справді підвищують стабільність

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

Тайм‑аути Docker NFS томів — опції монтування, що справді підвищують стабільність

Ви запускаєте абсолютно нудний контейнер. Він записує кілька файлів. Потім, о 02:17, усе зависає, ніби чекає на довідку.
df зависає. Потоки вашого застосунку накопичуються. Логи Docker нічого корисного не кажуть. Єдина підказка — слід «nfs: сервер не відповідає».

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

Чому Docker NFS томи тайм‑аутяться (і чому це виглядає випадково)

Docker «NFS томи» — це просто Linux NFS монти, створені на хості й потім прив’язані (bind-mounted) у контейнери.
Звучить просто, і так воно є — поки ви не згадаєте, що NFS — це мережевий файловий сервіс із семантикою повторів,
RPC‑таймаутами, блокуванням і станом.

Більшість «тайм‑аутів» — це не один єдиний тайм‑аут

Коли хтось каже «NFS тайм‑аутнув», зазвичай мається на увазі одне з наступного:

  • Клієнт намагається нескінченно повторювати (жорстке монтування, hard) і потік застосунку блокується в незмінному сні (D стан). Це виглядає як зависання.
  • Клієнт здався (м’яке монтування, soft) і повернув помилку I/O. Це виглядає як корупція, невдалі записи або «мій застосунок рандомно помиляється».
  • Сервер зник і повернувся, але у клієнта більше не співпадає уява про експорт (stale file handles). Це виглядає як «вчора працювало».
  • Проблеми з RPC‑інфраструктурою (особливо NFSv3: portmapper/rpcbind, mountd, lockd) спричиняють часткові збої, коли деякі операції працюють, а інші тайм‑аутять.
  • Флапи вирішення імен або маршрутизації спричиняють періодичні затори, які самостійно зцілюються. Це найгірше, бо породжує забобони.

Docker підсилює режими відмов

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

Hard vs soft — не налаштування продуктивності, а рішення про ризик

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

Є парафразована ідея від Вернера Фогельса (CTO Amazon), яка варта пам’яті: «Усе ламається, тож проектуйте з урахуванням відмов».
NFS‑монти — саме те місце, де ця філософія перестає бути натхненням і стає чеклістом.

Цікаві факти та контекст (коротко, конкретно, корисно)

  • NFS існує набагато довше за контейнери. Він зʼявився в 1980‑х як спосіб ділитися файлами мережею без складності клієнтського стану.
  • NFSv3 — переважно безстанний. Це полегшувало відновлення в деяких аспектах, але перенесло складність в допоміжні демони (rpcbind, mountd, lockd).
  • NFSv4 обʼєднав бокові канали. v4 зазвичай використовує один добре відомий порт (2049) і інтегрує блокування та стан, що зазвичай покращує дружність з фаєрволами та NAT.
  • «Hard mount» — історичний дефолт не просто так. Тихе втрачання даних гірше за очікування; hard‑монти віддають перевагу коректності, а не життєздатності.
  • Linux NFS клієнт має кілька рівнів таймаутів. Є базовий RPC‑таймаут (timeo), кількість повторів (retrans) і вищі механізми відновлення.
  • Stale file handle — класичний податок NFS. Виникає, коли сервер змінює зіставлення inode/file handle; часто після failover чи змін експорту.
  • NFS поверх TCP не завжди був дефолтом. Раніше популярний UDP; зараз TCP — здорова опція для надійності й контролю заторів.
  • DNS важливіший, ніж здається. NFS клієнти можуть кешувати відображення імен в IP інакше, ніж ваш застосунок; зміна DNS під час роботи може спричинити «півсвіту працює» симптоми.

Жарт №1: NFS як спільний принтер в офісі — коли працює, ніхто не помічає; коли ламається, раптом у всіх термінові «бізнес‑критичні» документи.

Швидка схема діагностики (швидко знайти вузьке місце)

Мета — не «зібрати кожну метрику». Мета — швидко вирішити, чи у вас проблема маршруту мережі,
проблема сервера, семантика монтування у клієнта або проблема оркестрації Docker.
Ось порядок, що економить час.

По‑перше: підтвердіть, що це NFS, а не застосунок

  1. На хості Docker, спробуйте простий stat або ls на змонтованому шляху. Якщо це зависає — проблема не в застосунку, а в монтуванні.
  2. Перевірте dmesg на наявність server not responding / timed out / stale file handle. Повідомлення ядра грубі, але зазвичай коректні.

По‑друге: вирішіть «мережа чи сервер» одним тестом

  1. З клієнта перевірте доступність порту 2049 і (якщо використовуєте NFSv3) rpcbind/portmapper. Якщо не вдається підключитися — припиніть звинувачувати параметри монтування.
  2. З іншого хоста в тій самій мережевій підмережі протестуйте те ж саме. Якщо проблема ізольована на одному клієнті — підозрівайте локальний фаєрвол, вичерпання conntrack, MTU або поганий маршрут.

По‑третє: перевірте версію протоколу й опції монтування

  1. Переконайтеся, чи ви на NFSv3 чи NFSv4. Багато «рандомних» тайм‑аутів — це насправді проблеми rpcbind/mountd від NFSv3 у сучасних мережах.
  2. Підтвердіть hard, timeo, retrans, tcp і чи використовували ви intr (застаріла поведінка) або інші legacy‑флаги.

По‑четверте: перегляньте логи сервера та насиченість

  1. Середнє навантаження сервера не достатньо. Дивіться кількість NFS‑потоків, затримки диска та мережеві втраї.
  2. Якщо сервер — NAS, визначте, чи він навантажений ЦП (шифрування, контрольні суми) або ввід/вивід (диски, rebuild, видалення snapshot).

Якщо ви пройдете ці чотири фази, зазвичай зможете назвати клас відмови менше ніж за десять хвилин. Довгий шлях — це політика.

Патерни конфігурації Docker, що вам не шкодитимуть

Патерн 1: драйвер local Docker з NFS‑опціями (добре, але перевірте, що реально змонтувалося)

Драйвер local Docker може монтувати NFS за допомогою type=nfs і o=....
Це звично в Compose і Swarm.
Підводний камінь: люди припускають, що Docker «робить щось розумне». Ні. Він передає опції mount‑хелперу.
Якщо mount‑хелпер відкотиться до іншої версії або проігнорує опцію, ви можете не помітити.

Патерн 2: попередньо змонтувати на хості й bind‑монтувати в контейнери (часто більш передбачувано)

Якщо ви попередньо монтуєте через /etc/fstab або systemd‑unit‑и, ви можете контролювати порядок, повтори і спостерігати монтування безпосередньо.
Docker тоді просто робить bind‑mount локального шляху. Це зменшує «магію Docker», що загалом добре для сну.

Патерн 3: розділяйте монти за класом навантаження

Не використовуйте один NFS‑експорт і один набір опцій для всього.
Ставтеся до NFS як до сервісу з SLO: низьколатентні метадані (CI кеші), пропускна здатність для великих файлів (медіа), коректність перш за все (дані станових застосунків).
Різні монти — різні опції — різні очікування.

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

Це хід дій на виклику, що перетворює «NFS ненадійний» на чітке наступне завдання. Запускайте їх на хості Docker, якщо не зазначено інакше.
Кожне завдання включає (1) команду, (2) що означає вивід, (3) яке рішення прийняти.

Завдання 1: Визначити, які монти — NFS і як вони сконфігуровані

cr0x@server:~$ findmnt -t nfs,nfs4 -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET                SOURCE                     FSTYPE OPTIONS
/var/lib/docker-nfs    nas01:/exports/appdata     nfs4   rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.8.21

Значення: Підтверджує версію NFS, proto і чи ви на hard чи soft. Також показує, чи rsize/wsize великі і потенційно не співпадають.

Рішення: Якщо бачите vers=3 несподівано, плануйте перехід на v4 або перевірку портів rpcbind/mountd. Якщо бачите soft для навантажень із записом — змініть це.

Завдання 2: Підтвердити конфігурацію Docker volume (що Docker вважає, що він запросив)

cr0x@server:~$ docker volume inspect appdata
[
  {
    "CreatedAt": "2026-01-01T10:12:44Z",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/appdata/_data",
    "Name": "appdata",
    "Options": {
      "device": ":/exports/appdata",
      "o": "addr=10.10.8.10,vers=4.1,proto=tcp,hard,timeo=600,retrans=2,noatime",
      "type": "nfs"
    },
    "Scope": "local"
  }
]

Значення: Це конфігурація, а не правда. Опції Docker можуть бути вірними, тоді як фактичне монтування інше.

Рішення: Порівняйте з findmnt. Якщо вони різняться — дослідіть поведінку mount‑хелпера, дефолти і підтримку ядром.

Завдання 3: Пошук помилок в NFS‑клієнті ядра прямо зараз

cr0x@server:~$ dmesg -T | egrep -i 'nfs:|rpc:|stale|not responding|timed out' | tail -n 20
[Fri Jan  3 01:58:41 2026] nfs: server nas01 not responding, still trying
[Fri Jan  3 01:59:12 2026] nfs: server nas01 OK

Значення: «Not responding, still trying» вказує на hard‑монтування, що повторює спроби через розрив.

Рішення: Якщо ці події співпадають із застопореннями застосунку — досліджуйте мережеві пропадання або зависання сервера; не «чіпайте» застосунок.

Завдання 4: Підтвердити стани процесів під час зависання (чи в D‑стані?)

cr0x@server:~$ ps -eo pid,stat,comm,wchan:40 | egrep 'D|nfs' | head
 8421 D    php-fpm          nfs_wait_on_request
 9133 D    rsync            nfs_wait_on_request

Значення: D стан з nfs_wait_on_request вказує на заблокований I/O ядра в очікуванні NFS.

Рішення: Розглядайте як інфраструктурний інцидент. Перезапуск контейнерів не допоможе, якщо монтування жорстко застрягло.

Завдання 5: Перевірити базову TCP‑зʼєднаність до NFS‑сервера

cr0x@server:~$ nc -vz -w 2 10.10.8.10 2049
Connection to 10.10.8.10 2049 port [tcp/nfs] succeeded!

Значення: Порт 2049 доступний зараз.

Рішення: Якщо це падає під час інциденту — ваші опції монтування не основна проблема; виправляйте маршрутизацію, ACL, фаєрвол або доступність сервера.

Завдання 6: Якщо використовуєте NFSv3, підтвердити, що rpcbind доступний (поширена прихована залежність)

cr0x@server:~$ nc -vz -w 2 10.10.8.10 111
Connection to 10.10.8.10 111 port [tcp/sunrpc] succeeded!

Значення: rpcbind/portmapper доступний. Без нього NFSv3 монти можуть падати або зависати під час узгодження монтування.

Рішення: Якщо 111 заблокований і ви на v3 — переходьте на v4 або відкрийте потрібні порти належним чином (і документуйте це).

Завдання 7: Виявити версію NFS та адресу сервера (піймати DNS‑сюрпризи)

cr0x@server:~$ nfsstat -m
/var/lib/docker-nfs from nas01:/exports/appdata
 Flags: rw,hard,noatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.8.21,local_lock=none

Значення: Підтверджує узгоджені налаштування. Зверніть увагу на імʼя сервера проти IP і поведінку local_lock.

Рішення: Якщо монтування використовує hostname і ваш DNS нестабільний — перейдіть на IP або закешуйте запис у hosts — потім сплануйте кращу DNS‑стратегію.

Завдання 8: Виміряти кількість повторів і біль на рівні RPC (чи є втрата пакетів?)

cr0x@server:~$ nfsstat -rc
Client rpc stats:
calls      retrans    authrefrsh
148233     912        148245

Значення: Retrans вказує на RPC, які довелося повторно відправляти. Зростання retrans корелює з втратами, заторами або зависаннями сервера.

Рішення: Якщо retrans зростає під час інцидентів — перевіряйте втрати в мережі та навантаження сервера; розгляньте помірне збільшення timeo, а не зменшення.

Завдання 9: Перевірити помилки інтерфейсу і втрати (не гадати)

cr0x@server:~$ ip -s link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    RX:  bytes packets errors dropped  missed   mcast
    128G  98M     0     127      0      1234
    TX:  bytes packets errors dropped carrier collsns
    141G  92M     0      84      0      0

Значення: Втрати на RX/TX можуть бути достатніми, щоб викликати NFS «не відповідає» під навантаженням.

Рішення: Якщо кількість dropped росте — досліджуйте NIC‑кільця, MTU‑невідповідність, затори на комутаторі або навантаження CPU на хості.

Завдання 10: Швидко виявити MTU‑невідповідність (jumbo frames підозріло до доказів)

cr0x@server:~$ ping -c 3 -M do -s 8972 10.10.8.10
PING 10.10.8.10 (10.10.8.10) 8972(9000) bytes of data.
From 10.10.8.21 icmp_seq=1 Frag needed and DF set (mtu = 1500)
From 10.10.8.21 icmp_seq=2 Frag needed and DF set (mtu = 1500)
From 10.10.8.21 icmp_seq=3 Frag needed and DF set (mtu = 1500)

--- 10.10.8.10 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2043ms

Значення: Path MTU = 1500, але ваш хост думає 9000. Це спричиняє «чорні діри» й «рандомні» затрини.

Рішення: Виправте MTU по всьому шляху або зменшіть до 1500. Потім знову оцініть стабільність NFS перед правками опцій монтування.

Завдання 11: Підтвердити, що експорт на сервері існує й права адекватні (вид сервера)

cr0x@server:~$ showmount -e 10.10.8.10
Export list for 10.10.8.10:
/exports/appdata 10.10.8.0/24
/exports/shared  10.10.0.0/16

Значення: Показує експорти (корисно для NFSv3; для v4 все одно дає підказку).

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

Завдання 12: Захопити короткий пакетний трас під час події (доведіть втрату vs мовчання сервера)

cr0x@server:~$ sudo tcpdump -i eth0 -nn host 10.10.8.10 and port 2049 -c 30
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:02:11.101223 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232
10:02:12.102988 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232
10:02:13.105441 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232

Значення: Повторні ретрансмісії без відповідей сервера вказують, що сервер не відповідає або відповіді не повертаються.

Рішення: Якщо бачите клієнтські ретрансмісії без відповіді — ідіть до перевірки здоровʼя сервера / шляху повернення мережі. Якщо бачите відповіді сервера, але клієнт все ще ретрансмить — підозрюйте асиметричну маршрутизацію або фаєрвол‑стан.

Завдання 13: Перевірити логи демонa Docker на предмет спроб монтування і помилок

cr0x@server:~$ journalctl -u docker --since "30 min ago" | egrep -i 'mount|nfs|volume|rpc' | tail -n 30
Jan 03 09:32:14 server dockerd[1321]: time="2026-01-03T09:32:14.112345678Z" level=error msg="error while mounting volume 'appdata': failed to mount local volume: mount :/exports/appdata:/var/lib/docker/volumes/appdata/_data, data: addr=10.10.8.10,vers=4.1,proto=tcp,hard,timeo=600,retrans=2: connection timed out"

Значення: Підтверджує, що Docker не зміг змонтувати, а не що застосунок впав пізніше.

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

Завдання 14: Інспектувати порядок systemd (network‑online ≠ просто network)

cr0x@server:~$ systemctl status network-online.target
● network-online.target - Network is Online
     Loaded: loaded (/lib/systemd/system/network-online.target; static)
     Active: active since Fri 2026-01-03 09:10:03 UTC; 1h 2min ago

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

Рішення: Якщо бачите проблеми з порядком, перенесіть монти в systemd‑unit‑и з After=network-online.target і Wants=network-online.target, або використайте automount.

Завдання 15: Перевірити, що монтування відповідає (швидка саніті‑перевірка)

cr0x@server:~$ time bash -c 'stat /var/lib/docker-nfs/. && ls -l /var/lib/docker-nfs >/dev/null'
real    0m0.082s
user    0m0.004s
sys     0m0.012s

Значення: Базові метадані швидкі. Якщо це іноді займає секунди або зависає — у вас періодична затримка або стопи.

Рішення: Якщо метадані повільні — досліджуйте затримки диска сервера і насиченість NFS‑потоків; опції монтування не врятують тонучий масив.

Три корпоративні міні‑історії (як це насправді ламається)

1) Інцидент через неправильне припущення: «Том локальний, тому що Docker каже ‘local’»

Середня компанія вела Swarm‑кластер для внутрішніх сервісів. Команда створила Docker‑том із драйвером local і NFS‑опціями.
Усі прочитали «local» і припустили, що дані лежать на кожному вузлі локально. Це припущення визначило все: плани відновлення, вікна техобслуговування, навіть відповідальність за інциденти.

Під час мережевого техобслуговування один комутатор у стійці підгорів. Тільки деякі вузли втратили звʼязок з NAS на кілька секунд.
Вражені вузли мали hard‑монти NFS. Їхні контейнери не падали; вони просто припинили робити прогрес. Хелсчеки тайм‑аутнули.
Оркестратор почав пересаджувати, але нові таски потрапляли на ті ж самі постраждалі вузли, бо планування не знало, що проблемою є NFS.

Реакція на виклик була класичною: перезапустити сервіс. Це лише створило більше заблокованих процесів. Хтось спробував видалити й створити том наново.
Docker підкорився, але монтування ядра все ще було застрягле. Хост перетворився на музей зависших тасків.

Виправлення не було героїчним. Документували, що «local driver» може бути віддаленим сховищем, додали превʼю‑перевірку в pipeline розгортання,
щоб верифікувати тип монтування за допомогою findmnt, і відчепили критичні сервіси від вузлів, які не могли дістатися storage VLAN.
Найбільша зміна була культурною: сховище перестало бути «чужою проблемою» з моменту появи контейнерів.

2) Оптимізація, що відкинула назад: «Ми зменшили таймаути, щоб відмови відбувалися швидше»

Інша організація мала періодичну проблему: застосунки зависали при NFS‑хиках. Хтось запропонував «прості» зміни:
перемкнутися на soft, знизити timeo і підвищити retrans, щоб клієнт здавався швидко й застосунок міг це обробити.
У квитку це виглядало розумно — бо в квитку все виглядає розумно.

На практиці застосунки не були готові обробляти EIO під час запису.
Фоновий воркер писав у тимчасовий файл, а потім перейменовував його на місце. Під soft‑монтуваннями і низькими таймаутами
запис іноді падав, але робочий процес не завжди поширював помилку. Перейменування відбувалося з частковим вмістом.
Подальші таски обробляли «сміття».

Інцидент не був чистим простою; він був гіршим. Система залишалася «увімкненою», виробляючи неправильні результати.
Це спричинило повільне реагування: відкат, повторна обробка, аудит результатів. Зрештою опції монтування відкотили.
Потім вони виправили справжню проблему: періодичну втрату пакетів через некоректну конфігурацію LACP та MTU‑невідповідність, що виявлялася лише під навантаженням.

Висновок у їхньому рукоописі був різким: «Fail fast» чудово, коли відмова надійно піднімається.
Soft‑монти зробили відмову легшою для ігнорування, а не для обробки.

3) Скучно, але правильно: pre‑mount + automount + явні залежності врятували день

Фінансова компанія запускала станкові пакетні завдання в контейнерах, записуючи артефакти на NFS.
У них була прісна правило: NFS‑монти керуються systemd, а не створенням Docker‑тома під час запуску.
Кожен монтування мав automount‑unit, визначений таймаут і залежність від network-online.target.

Одного ранку плановий перезавантаження вузла збігся з патчингом NAS. NAS був доступний, але кілька хвилин працював повільно.
Контейнери стартували, але їхні NFS‑шляхи монтувалися лише при зверненні. Спроба automount чекала й потім успішно завершилася, коли NAS відновився.
Завдання почалися з невеликим запізненням, і ніхто не прокинувся.

Різниця була не в кращому обладнанні. Відмінність у тому, що життєвий цикл монтувань не був привʼязаний до життєвого циклу контейнерів.
Docker не вирішував, коли монтування має статися, а відмови були видимі на системному рівні з чіткими логами.

Це саме те, за що керівництво не дає винагороди, бо нічого не сталося. Але це практика, що тримає вас на роботі.

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

1) Симптом: контейнери «замерзають» і не зупиняються; docker stop зависає

Корінна причина: hard‑монтування NFS застопорене; процеси в D стані чекають I/O ядра.

Виправлення: відновити конектність/здоровʼя сервера; не чекайте, що сигнали спрацюють. Якщо треба відновити вузол — відмонтуйте після повернення сервера або як крайній захід перезавантажте хост. Запобігайте повторенню стабільними мережами і розумними timeo/retrans.

2) Симптом: «працює на одному вузлі, тайм‑аутить на іншому»

Корінна причина: відмінності маршрутизації/фаєрвола/MTU по вузлах або вичерпання conntrack на підмножині вузлів.

Виправлення: порівняйте ip route, ip -s link і правила фаєрволу. Перевірте MTU за допомогою DF‑ping. Забезпечте ідентичну мережеву конфігурацію по кластеру.

3) Симптом: монти падають під час завантаження або одразу після перезавантаження хоста

Корінна причина: спроби монтування перегорають з готовністю мережі; Docker запускає контейнери до того, як network‑online стане true.

Виправлення: керуйте монтуваннями через systemd‑unit‑и з явним порядком або використайте automount. Уникайте монтувань за вимогою, ініційованих стартом контейнера.

4) Симптом: періодичні «permission denied» або дивні проблеми з ідентифікацією

Корінна причина: невідповідність UID/GID, поведінка root‑squash або проблеми idmapping у NFSv4. Контейнери це ускладнюють через user namespaces і різних користувачів у образах.

Виправлення: стандартизувати UID/GID для писачів, перевірити опції експорту на сервері і для NFSv4 перевірити конфігурацію idmapping. Не загортайте проблему в 0777; це не стабільність, це капітуляція.

5) Симптом: часті «stale file handle» після failover NAS або техобслуговування експорту

Корінна причина: серверне зіставлення file handle змінилося; клієнти тримають посилання, які більше не резольвляться.

Виправлення: уникайте переміщення/перезапису експортів під клієнтами; використовуйте стабільні шляхи. Для відновлення — перемонтуйте і перезапустіть постраждалі робочі навантаження. Для архітектури — віддавайте перевагу стабільним HA‑методам, підтримуваним вашим NAS, і тестуйте failover з реальними клієнтами.

6) Симптом: «рандомні» помилки монтувань лише в захищених мережах

Корінна причина: динамічні порти NFSv3 заблоковані; rpcbind/mountd/lockd не пропускаються через фаєрвол/секюріті групи.

Виправлення: перейдіть на NFSv4, якщо можливо. Якщо привʼязані до v3 — зафіксуйте порти демонів на сервері і відкрийте їх навмисно — і документуйте це, щоб наступний інженер не «оптимізував» ваш фаєрвол.

7) Симптом: пікові затримки, потім відновлення, що повторюється під навантаженням

Корінна причина: затримки диска сервера (rebuild/snapshot), насичення NFS‑потоків або переповненість мережевих черг.

Виправлення: виміряйте I/O‑латентність на сервері і кількість NFS‑потоків; ліквідуйте вузьке місце. Клієнтські опції як rsize/wsize не врятують перенасичений масив.

8) Симптом: перехід на soft «вирішує» зависання, але приносить загадкові проблеми з даними

Корінна причина: soft‑монти перетворюють відмови в I/O помилки; застосунки неправильно обробляють часткові збої.

Виправлення: поверніть hard для станкових записів, виправте основну конектність і оновіть застосунки для обробки помилок там, де це потрібно.

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

Покроково: стабілізувати існуючий Docker NFS деплой

  1. Зробіть інвентар монтів за допомогою findmnt -t nfs,nfs4. Запишіть vers, proto, hard/soft, timeo, retrans і чи використовувалися hostnames.
  2. Підтвердьте реальність за допомогою nfsstat -m. Якщо Docker каже одне, а ядро зробило інше — довіряйте ядру.
  3. Визначте протокол: віддавайте перевагу NFSv4.1+. Якщо ви на v3 — перелічіть залежності фаєрволу і випадки відмов, які ви не можете терпіти.
  4. Виправте мережу перед тюнінгом: перевірте MTU по всьому шляху; усуньте дропи інтерфейсу; підтвердіть симетрію маршрутизації; забезпечте стабільність порту 2049.
  5. Виберіть семантику монтування:
    • Станкові записи: hard, помірний timeo, доволі низький retrans, TCP.
    • Лише читання / кеш: розгляньте soft тільки якщо застосунок обробляє EIO і ви готові до «помилка замість зависання».
  6. Зробіть монти передбачуваними: попередньо монтуйте через systemd або використайте automount. Уникайте runtime‑монтувань, ініційованих стартом контейнера коли це можливо.
  7. Протестуйте відмову: відключіть мережу сервера (в лабораторії), перезавантажте клієнта, пропустіть маршрут, і спостерігайте. Якщо ваш тест — «почекати і сподіватися», ви не тестуєте.
  8. Операціоналізуйте: додайте дашборди для retransmits, дропів інтерфейсу, насичення NFS‑потоків сервера і затримок диска. Напишіть runbook на основі Швидкої схеми діагностики і попрактикуйте його у спокійний час.

Короткий чекліст опцій монтування (передбачуваність перш за все)

  • Використовуйте TCP: proto=tcp
  • Віддавайте перевагу NFSv4.1+: vers=4.1 (або 4.2 якщо підтримується)
  • Коректність перш за все: hard
  • Не перестарайтеся з тюнінгом: почніть з timeo=600, retrans=2 і регулюйте тільки за наявності доказів
  • Зменшіть метаданний шум: noatime для типової робооти
  • Обережно з actimeo та схожими; кешування не є «безкоштовним прискоренням»
  • Розгляньте nconnect лише після вимірювання здібностей сервера і пропускних здібностей фаєрволу

Питання й відповіді

1) Чи використовувати NFSv3 чи NFSv4 для Docker томів?

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

2) Чи soft коли‑небудь прийнятний?

Так — для даних лише для читання або кеш‑даних, де помилка I/O краща за зависання, і де застосунок побудований оброблювати EIO як норму. Для станкових записів це міна.

3) Чому docker stop зависає, коли NFS впав?

Тому що процеси заблоковані в I/O ядра на жорстко змонтованій файловій системі. Сигнали не можуть перервати потік у незмінному сні. Виправте доступність монтування.

4) Що саме роблять timeo і retrans?

Вони керують поведінкою повторів RPC. timeo — базовий таймаут для RPC; retrans — скільки повторів відбувається перед тим, як клієнт повідомить «не відповідає» (і для soft‑монтувань — до повернення помилки I/O).

5) Чи варто тюнити rsize і wsize до величезних значень?

Не за приписами. Сучасні дефолти часто хороші. Забагато може зіпсувати взаємодію з MTU, лімітами сервера або мережевими втратами. Тюнити тільки після вимірювання пропускної здатності і retransmits.

6) Чи допомагає використання IP замість імені хоста?

Може допомогти. Якщо DNS хиткий, повільний або змінюється несподівано, використання IP усуває вирішення імен як точку відмови. Мінус — втрата легкості міграції сервера, якщо ви не керуєте IP як стабільну ціль.

7) Що спричиняє «stale file handle» і як це запобігти?

Зазвичай спричинене серверними змінами, що інвалідовують file handles: переміщення експорту, поведінка failover або зміни файлової системи під експортом. Запобігайте, тримаючи експорти стабільними і застосовуючи HA‑методи вашого NAS, тестуючи failover з реальними клієнтами.

8) Монтованти через Docker volumes чи попередньо на хості?

Попереднє монтування (systemd mounts/automount) часто більш передбачуване й легше для дебагу. Docker‑volume монтування працює, але звʼязує життєвий цикл монтування з життєвим циклом контейнера — і це не найкраще місце для вашої історії надійності.

9) А nolock як фікс для зависань?

Уникайте, якщо не впевнені, що навантаження не покладається на блокування. Воно може «вирішити» деякі lockd‑проблеми в NFSv3, але обмінює відмови на баги коректності.

10) Якщо мій NFS‑сервер у порядку, чому лише деякі клієнти бачать тайм‑аути?

Бо «сервер у порядку» часто значить «відповів на ping». Локальні проблеми клієнта — MTU‑невідповідність, асиметрична маршрутизація, ліміти conntrack та дропи NIC — можуть окремо ламати NFS, лишивши інший трафік в основному працездатним.

Висновок: наступні кроки, що зменшують навантаження на виклик

Якщо ви боретеся з тайм‑аутами Docker NFS томів, не починайте крутити timeo як радіодіал.
Почніть з іменування відмови: шлях мережі, насыщення сервера, тертя версій протоколу або таймінг оркестрації.
Потім зробіть свідомий вибір щодо семантики: коректність перш за все — hard‑монти для станкових записів, і лише ретельно обґрунтовані soft‑монти для одноразових даних.

Практичні наступні кроки на цей тиждень:

  1. Перевірити кожен хост Docker за допомогою findmnt і nfsstat -m; зафіксувати фактичні опції і версію NFS.
  2. Стандартизувати NFSv4.1+ поверх TCP, якщо нема причин інакше.
  3. Виправити MTU і лічильники падінь до зміни тюнінгу монтувань.
  4. Перевести критичні монти в systemd‑керовані монти (найкраще — automount) з явним порядком network‑online.
  5. Написати runbook на основі Швидкої схеми діагностики і відрепетирувати його раз, поки все тихо.

Кінцева мета — не «NFS ніколи не підглючить». Кінцева мета: коли він підглючить, поведінка передбачувана, відновлення чисте і ваші контейнери не перетворюються на сучасне мистецтво.

← Попередня
ZFS ZVOL проти Dataset: рішення, що визначає ваші майбутні проблеми
Наступна →
VoIP через VPN: припиніть роботизований звук — MTU, джиттер і основи QoS

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