Debian 13: «Device busy» при umount — як миттєво знайти утримувача (схема lsof/fuser)

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

Ви виконуєте umount /mnt/data, а Linux відповідає тим самим беземоційним плечима: target is busy. Тепер ви розриваєтеся між тим, щоб зробити правильно (знайти утримувача), і тим, щоб зробити швидко (перезавантажити й видати це за «планове обслуговування»).

Це польовий посібник для Debian 13: як ідентифікувати точний процес, файл, каталог, простір імен або споживача ядра, що тримає монтування, використовуючи lsof, fuser і кілька трюків з /proc, які працюють навіть коли «дружні» інструменти брешуть.

Що насправді означає «device busy» (і чого це не означає)

Коли umount завершується з «device busy», ядро відмовляється від’єднати монтування, бо щось все ще посилається на нього. Тим «щось» може бути:

  • Процес з відкритим файловим дескриптором у межах цього монтування.
  • Процес з поточним робочим каталогом (cwd) десь у цьому дереві.
  • Процес, чиє root-піддерево (chroot або pivot root) знаходиться всередині нього.
  • Bind mount, overlay або propagation монтувань, що тримають піддерево «прикріпленим».
  • Споживач на рівні ядра (swapfile, loop-пристрій, ціль dm-crypt, стан клієнта NFS).
  • Інший простір імен для монтувань (контейнер, systemd-сервіс), який усе ще його використовує.

Чого це зазвичай не означає: «пристрій повільний» або «файлова система пошкоджена». Це може спричиняти зависання, I/O-помилки або таймаути, але «busy» — про посилання, а не про стан здоров’я.

Існує два види болю:

  • Миттєве «busy»: ядро знає про активні посилання і відмовляється від чистого відмонтування.
  • Зависання при відмонтуванні: ви виконали umount і воно блокується. Це часто мережеві файлові системи (NFS) або застряглий I/O, а не просто відкритий файл.

Надійне вирішення означає припинити здогадки і почати питати ядро: «Хто тримає це монтування?» Найшвидший шлях — lsof та fuser, але потрібно знати їхні «сліпі плями» і як їх обійти.

Швидкий план діагностики (перша/друга/третя перевірки)

Коли ви на виклику, часу на танці з umount немає. Робіть так.

1) Перша перевірка: чи щось тримає файли або cwd у цьому монтуванні?

  • Запустіть findmnt, щоб впевнитися, що ви орієнтуєтесь на правильне монтування.
  • Запустіть fuser -vm для швидкого списку PID і типів доступу.
  • Запустіть lsof +f -- /mountpoint, коли потрібні імена файлів і користувачі.

2) Друга перевірка: чи це насправді стек монтувань / bind / propagation?

  • Шукайте підмонтування: findmnt -R /mountpoint.
  • Шукайте bind mounts: findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS і перевірте bind, rbind, shared.
  • Якщо ви відмонтуєте батька під час наявності дитини, батько буде «busy» з поважної причини.

3) Третя перевірка: чи утримувач поза вашим простором імен (контейнери/systemd) або на рівні ядра?

  • Перевірте простори імен для монтувань: подивіться /proc/<pid>/mountinfo і lsns.
  • Перевірте swapfile і loop-пристрої: swapon --show, losetup -a.
  • Перевірте стек dm/lvm: lsblk -o NAME,TYPE,MOUNTPOINTS,PKNAME, dmsetup ls --tree.

Якщо запам’ятати лише одне: не використовуйте umount -l, поки не ідентифікували утримувача. Lazy unmount — інструмент, а не стиль життя.

Основний робочий процес: lsof → fuser → /proc → простори імен

Ось робочий процес, який я використовую на Debian-системах, коли з’являється «device busy». Він підходить від «розробник лишив shell у директорії» до «рантайм контейнерів прикріпив bind mount в іншому просторі імен».

Почніть з істини про монтування: findmnt

Вивід mount теж підходить, але findmnt більш структурований і менш оманливий. Вам потрібні: target, source, fstype і опції, а також підмонтування.

Потім запитайте про утримувачів: fuser і lsof

fuser грубий і швидкий: «ці PID торкаються цього монтування». Він може показати, чи процес має там cwd, виконує бінарник звідти, або просто має відкриті файли.

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

Коли інструменти не сходяться: /proc — джерело істини

Іноді lsof щось пропускає через права, простори імен або величезну кількість відкритих файлів. Тоді йдете в /proc і читаєте посилання процесів напряму (cwd, root, fd), або таблицю монтувань ядра (/proc/self/mountinfo).

Нарешті: усвідомлення просторів імен

Якщо монтування утримується в контейнері або іншому mount namespace, ви можете дивитися lsof на хості і все одно «нічого не бачити». Використовуйте lsns і nsenter, щоб подивитися з перспективи утримувача.

Суха правда: Linux із задоволенням дозволяє двом різним світам думати, що монтування різні. Це не баг. Це вівторок.

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

Ось реальні кроки, які я виконую в продакшені, приблизно в тому порядку. Кожен включає: команду, що означає вивід, і яке рішення приймати далі.

Завдання 1: Підтвердити точне монтування і його джерело

cr0x@server:~$ findmnt /mnt/data
TARGET    SOURCE      FSTYPE OPTIONS
/mnt/data /dev/sdb1   ext4   rw,relatime

Що це означає: Ви відмонтовуєте /mnt/data, яка підкладена /dev/sdb1. Якщо SOURCE — щось на кшталт server:/export (NFS) або /dev/mapper/vg-lv (LVM), ви вже знаєте, які особливі випадки можуть застосовуватися.

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

Завдання 2: Перевірити підмонтування, що робить батька «busy»

cr0x@server:~$ findmnt -R /mnt/data
TARGET              SOURCE            FSTYPE OPTIONS
/mnt/data           /dev/sdb1         ext4   rw,relatime
/mnt/data/cache     tmpfs             tmpfs  rw,nosuid,nodev
/mnt/data/containers overlay          overlay rw,relatime,lowerdir=...

Що це означає: Ви не можете відмонтувати /mnt/data, поки /mnt/data/cache або /mnt/data/containers змонтовані. Батьківські монтування не видаляють дітей автоматично.

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

Завдання 3: Швидко «хто торкається цього монтування» за допомогою fuser

cr0x@server:~$ sudo fuser -vm /mnt/data
                     USER        PID ACCESS COMMAND
/mnt/data:           root       1321 ..c..  bash
                     www-data   1874 ..c..  nginx
                     postgres   2440 ..c..  postgres

Що це означає: Ці процеси мають щось під тим монтуванням як cwd або відкритий файл (флаги ACCESS різні; c зазвичай означає current directory). Це зазвичай достатньо, щоб пояснити «busy».

Рішення: Вирішіть, чи зупинити сервіс коректно, перемістити shell або вбити конкретні PID(и).

Завдання 4: Отримати імена файлів і дескриптори за допомогою lsof

cr0x@server:~$ sudo lsof +f -- /mnt/data | head -n 8
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx    1874 www-data   10u   REG  8,17     4096  212 /mnt/data/www/access.log
postgres 2440 postgres   12u   REG  8,17  1048576  877 /mnt/data/pg_wal/0000000100000000000000A1

Що це означає: Тепер у вас конкретні шляхи. Це різниця між «щось це використовує» і «nginx все ще логірує сюди».

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

Завдання 5: Спіймати класичний випадок «shell сидить у каталозі»

cr0x@server:~$ sudo ls -l /proc/1321/cwd
lrwxrwxrwx 1 root root 0 Dec 29 11:05 /proc/1321/cwd -> /mnt/data

Що це означає: Робочий каталог PID 1321 знаходиться в межах монтування. Це саме по собі фіксує монтування.

Рішення: Якщо це ваш shell, зробіть cd /. Якщо це чиясь сесія — координуйте або завершіть її.

Завдання 6: Знайти процеси, чиє root знаходиться в монтуванні (chroot/pivot_root)

cr0x@server:~$ sudo ls -l /proc/2890/root
lrwxrwxrwx 1 root root 0 Dec 29 11:06 /proc/2890/root -> /mnt/data/chroots/buildenv

Що це означає: Цей процес має root всередині монтування. Це часто трапляється з build-системами, інструментами відновлення і деякими налаштуваннями контейнерів.

Рішення: Зупиніть процес коректно, якщо можливо. Вбивство може залишити нащадків; будьте готові відловлювати групи процесів.

Завдання 7: Коли lsof повільний або неповний, скануйте /proc на offenders cwd/root

cr0x@server:~$ sudo bash -lc 'for p in /proc/[0-9]*; do 
  pid=${p#/proc/}
  cwd=$(readlink -f $p/cwd 2>/dev/null || true)
  root=$(readlink -f $p/root 2>/dev/null || true)
  if [[ "$cwd" == /mnt/data* || "$root" == /mnt/data* ]]; then
    printf "%s cwd=%s root=%s\n" "$pid" "$cwd" "$root"
  fi
done | head'
1321 cwd=/mnt/data root=/
2890 cwd=/ root=/mnt/data/chroots/buildenv

Що це означає: Ви швидко перерахували очевидні «закріплення» без покладання на повне сканування таблиці файлів lsof.

Рішення: Дослідіть кожний PID за допомогою ps і вирішіть, чи зупиняти/вбивати або переміщувати.

Завдання 8: Підтвердити, що процес насправді робить (і чи це очікувано)

cr0x@server:~$ ps -p 1874 -o pid,user,comm,args
  PID USER     COMMAND  COMMAND
 1874 www-data nginx    nginx: worker process

Що це означає: Без загадок: це nginx. «Busy» обґрунтовано.

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

Завдання 9: Перевірити, чи systemd створив або керує монтуванням (і може його знову змонтувати)

cr0x@server:~$ systemctl status mnt-data.mount
● mnt-data.mount - /mnt/data
     Loaded: loaded (/etc/fstab; generated)
     Active: active (mounted) since Mon 2025-12-29 10:02:10 UTC; 1h 4min ago
      Where: /mnt/data
       What: /dev/sdb1

Що це означає: systemd знає про це монтування. Якщо це пара automount, воно може змонтуватися знову, щойно щось звернеться до шляху.

Рішення: Якщо ви хочете, щоб воно залишалося відмонтуваним, зупиніть/відключіть відповідний .automount або видаліть запис у fstab перед продовженням.

Завдання 10: Виявити systemd automount, що миттєво ре-тригерить «busy»

cr0x@server:~$ systemctl status mnt-data.automount
● mnt-data.automount - Automount /mnt/data
     Loaded: loaded (/etc/fstab; generated)
     Active: active (waiting) since Mon 2025-12-29 10:01:59 UTC; 1h 4min ago
      Where: /mnt/data

Що це означає: Навіть після відмонтування наступний доступ до /mnt/data може змонтувати його знову. Також деякі «пробуючі» процеси (індексатори, моніторинг, автодоповнення shell) можуть продовжувати його чіпати.

Рішення: Для вікон обслуговування явно зупиніть automount-юнит: systemctl stop mnt-data.automount, а потім відмонтуйте.

Завдання 11: Підтвердити, чи монтування shared/slave (propagation може здивувати)

cr0x@server:~$ findmnt -o TARGET,PROPAGATION /mnt/data
TARGET    PROPAGATION
/mnt/data shared

Що це означає: Увімкнена передача монтувань. «Shared» монтування може поширювати підмонтування між просторами імен. Це часто з контейнерними рантаймами.

Рішення: Якщо ви діагностуєте хост із контейнерами, вважайте, що інший простір імен може утримувати монтування. Перейдіть до перевірки просторів імен.

Завдання 12: Ідентифікувати mount namespaces і знайти ймовірного утримувача

cr0x@server:~$ sudo lsns -t mnt | head
        NS TYPE NPROCS   PID USER    COMMAND
4026531840 mnt     168     1 root    /sbin/init
4026532501 mnt      15  6123 root    /usr/bin/containerd
4026532710 mnt       3  9012 root    /usr/sbin/cron

Що це означає: У вас кілька mount namespaces. Якщо монтування зайняте в одному просторі, воно може не відображатися так, як ви очікуєте в іншому.

Рішення: Перевірте ймовірних власників namespace (containerd, dockerd, podman, systemd-сервіси) і огляньте зсередини того простору імен.

Завдання 13: Увійти в mount namespace підозрілого PID і виконати findmnt там

cr0x@server:~$ sudo nsenter -t 6123 -m -- findmnt /mnt/data
TARGET    SOURCE      FSTYPE OPTIONS
/mnt/data /dev/sdb1   ext4   rw,relatime

Що це означає: Монтування існує й у просторі containerd. Можливо, контейнер прив’язав його bind-монтуванням і тримає.

Рішення: Використайте nsenter, щоб запустити fuser/lsof в тому просторі імен, або зупиніть відповідний контейнер/сервіс.

Завдання 14: Коли видалений, але відкритий файл тримає простір

cr0x@server:~$ sudo lsof +L1 -- /mnt/data | head
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NLINK NODE NAME
java     7777 app   25u   REG  8,17  5242880     0  901 /mnt/data/app/logs/app.log (deleted)

Що це означає: Файл видалено, але він все ще відкритий. Це не завжди блокує відмонтування само по собі, але часто підказує: процес все ще активно використовує файлову систему.

Рішення: Перезапустіть/перезавантажте процес або змусьте його закрити логи (logrotate з правильним postrotate). Не просто видаляйте файли і не надейтеся на краще.

Завдання 15: Перевірити swapfiles (рівень ядра «busy», який лsof не покаже)

cr0x@server:~$ sudo swapon --show
NAME              TYPE  SIZE USED PRIO
/mnt/data/swapfile file   8G  2G   -2

Що це означає: Swap розташований на цій файловій системі. Ядро не дозволить відмонтувати її, поки вона використовується як swap.

Рішення: swapoff /mnt/data/swapfile (і впевніться, що вистачає RAM або альтернативного swap), потім повторіть спробу відмонтування.

Завдання 16: Знайти loop-пристрої, які базуються на файлах у монтуванні

cr0x@server:~$ sudo losetup -a
/dev/loop2: [0801]:123456 (/mnt/data/images/debian.qcow2)

Що це означає: Loop-пристрій використовує файл на монтуванні. Це посилання ядра; відмонтування не вдасться, поки loop не відключено.

Рішення: Зупиніть те, що використовує loop-пристрій (VM-інструмент, монтування loop), потім losetup -d /dev/loop2.

Завдання 17: Побачити стек device-mapper/LVM, який тримає блочний пристрій

cr0x@server:~$ lsblk -o NAME,TYPE,SIZE,MOUNTPOINTS,PKNAME
sdb              disk  500G
└─sdb1           part  500G /mnt/data

Що це означає: Проста ситуація: пряма партиція. Якщо ви бачите вузли dm-* або логічні томи LVM, «busy» може бути через утримувачів вище або нижче шару файлової системи.

Рішення: Якщо залучено LVM/dm-crypt, переконайтеся, що спочатку відмонтовано файлову систему, потім деактивуйте LV/закрийте luks лише після відмонтування.

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

cr0x@server:~$ sudo ls -l /proc/1874/fd | head
total 0
lr-x------ 1 www-data www-data 64 Dec 29 11:10 0 -> /dev/null
l-wx------ 1 www-data www-data 64 Dec 29 11:10 10 -> /mnt/data/www/access.log
lr-x------ 1 www-data www-data 64 Dec 29 11:10 11 -> /mnt/data/www/index.html

Що це означає: Конкретний доказ: nginx має дескриптори до цього монтування. Жодних дискусій.

Рішення: Зупиніть/перезавантажте nginx, або змініть конфігурацію так, щоб він перестав використовувати ці шляхи.

Завдання 19: Спробувати чисте відмонтування з контекстом

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

Що це означає: Все ще прикріплено. Тепер ви не метушитеся; ви ітеруєте на підставі доказів утримувачів.

Рішення: Усуньте утримувачів (зупиніть сервіси, від’єднайте loop, swapoff, відмонтуйте підмонтування) і повторіть спробу.

Завдання 20: Використати lazy unmount, коли ви вирішили, що це прийнятно

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

Що це означає: Монтування від’єднано від простору імен зараз, але фактичне очищення відкладено до моменту, коли посилання зникнуть. Процеси можуть і далі звертатися до файлової системи через існуючі дескриптори.

Рішення: Робіть це лише коли можете терпіти «зомбі-доступ» і впевнені, що базовий пристрій не зникне під активним I/O. Якщо ви збираєтеся від’єднати сховище, lazy unmount може перетворити «обслуговування» на «післяслідство».

Завдання 21: Примусове відмонтування (остання інстанція, переважно для мережевих монтувань)

cr0x@server:~$ sudo umount -f /mnt/data
umount: /mnt/data: umount failed: Operation not permitted

Що це означає: На Linux -f має сенс головним чином для деяких мережевих файлових систем і специфічних ситуацій; для локальних ext4/xfs воно часто не спрацює так, як ви сподіваєтесь.

Рішення: Не вважайте -f універсальною важільною дією. Виправте утримувача або використайте lazy unmount з розумінням наслідків.

Завдання 22: Перевірити, що воно справді зникло (і не перемонтовано automount)

cr0x@server:~$ findmnt /mnt/data

Що це означає: Немає виводу: в поточному просторі імен воно не змонтоване.

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

Жарт 1: «Device busy» — це спосіб ядра сказати, що в нього є плани на вечір, а ви не в списку запрошених.

Особливі випадки: systemd, NFS, контейнери, bind mounts, loop/LVM/dm-crypt

systemd mounts і automount: у файлової системи тепер є менеджер

На Debian 13 systemd зазвичай залучений, навіть якщо ви про це не просили. Записи в /etc/fstab перетворюються на згенеровані юніти. Це добре для консистентного завантаження, але може здивувати під час обслуговування:

  • Automount робить відмонтування нестабільним: ви відмонтовуєте, потім випадковий процес статист шлях, і воно знову змонтується.
  • Залежності юнітів можуть утримувати монтування активним, бо сервіс Requires= їх.

Коли потрібне тривале відключення, зупиняйте automount передусім і розгляньте тимчасове маскування юніту, якщо якийсь сервіс постійно його відновлює.

NFS та «umount hangs» проти «device busy»

NFS — інший різновид проблеми. Ви можете бачити «device busy», але частіше umount просто чекає… і чекає…, бо ядро намагається скинути або вирішити очікувані операції.

Корекції робочого процесу для NFS:

  • Якщо umount блокується, перевірте доступність мережі й стан сервера перш за все. «Busy» може бути вторинним.
  • Частіше використовуйте umount -l для NFS, коли сервер недоступний і потрібно відновити клієнта, але розумійте компроміси.
  • Stale file handles і жорсткі монтовання можуть зробити звичайний перелік процесів оманливим.

Контейнери: утримувач у іншому mount namespace

Сучасні стекі контейнерів люблять bind mounts і overlayfs. Вони також люблять окремі mount namespaces. Ось класичний сценарій:

  • Хост: «Ніхто не використовує /mnt/data
  • Ядро: «Не погоджуюся.»
  • Реальність: контейнер має bind-монтування /mnt/data у своєму просторі імен, і там якийсь процес тримає файл.

Виправлення банальне: ідентифікуйте простір імен, зайдіть туди, знайдіть утримувача, зупиніть контейнер/сервіс. Спроби «перехитрити» простори імен інструментами лише на хості — витрата часу.

Bind mounts і поширення монтувань: невидиме клеїть

Bind mounts можуть створювати «busy»-ситуації, що виглядають ірраціонально, якщо ви дивитесь лише на очевидний target. Дерево монтувань має значення. Так само й propagation:

  • Bind mount каталогу всередині /mnt/data кудись ще означає, що процеси можуть тримати файлову систему, не торкаючись напряму шляхів під /mnt/data.
  • Shared монтування може поширювати підмонтування і тримати посилання між просторами імен.

Використовуйте findmnt -R для візуалізації піддерева. Використовуйте findmnt -o TARGET,SOURCE,OPTIONS,PROPAGATION, щоб зрозуміти, чому воно поводиться як гідра.

Loop-пристрої, swapfiles, dm-crypt, LVM: споживачі ядра не виглядають як «відкритий файл»

Якщо ви маєте справу з образами, зашифрованими томами або «тимчасовими» swap-файлами, у вас можуть бути посилання на рівні ядра:

  • Swapfile забороняє відмонтування, поки не буде виконано swapoff.
  • Loop device, підкладений файлом, забороняє відмонтування, поки не буде відключено.
  • Device-mapper stack може утримувати блочні пристрої навіть після відмонтування файлової системи, що важливо при видаленні базового пристрою.

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

Це шаблони, які я бачу в тикетах, чатах і хроніках інцидентів. Симптоми знайомі; корені зазвичай буденні.

1) Симптом: «umount каже busy, але lsof нічого не показує»

Корінь: Інший mount namespace тримає монтування; або ви запускали lsof без достатніх привілеїв; або «busy» — це споживач ядра (swap/loop), а не відкритий файл у userland.

Виправлення: Виконуйте з sudo. Перевірте простори імен з lsns -t mnt і огляньте з nsenter -t PID -m. Перевірте swapon --show та losetup -a.

2) Симптом: Батьківське монтування не відмонтовується, хоча ви «зупинили всі додатки»

Корінь: Десь під батьком є підмонтування (tmpfs, overlay, bind mount, контейнерне монтування).

Виправлення: findmnt -R /mnt/data, відмонтуйте найглибших дітей спочатку. Якщо ви робите демонтаж, розгляньте umount -R, але розумійте наслідки.

3) Симптом: Ви відмонтували успішно, але воно одразу змонтовується знову

Корінь: systemd automount, autofs або сервіс, що монтує на вимогу. Іноді це ваш shell або агент моніторингу, що торкається директорії.

Виправлення: Зупиніть automount-юнит (systemctl stop mnt-data.automount). Підтвердіть через systemctl status. Тимчасово замаскуйте, якщо потрібно для вікна обслуговування. Перенаправте проби подалі від шляху.

4) Симптом: «device busy» тільки при від’єднанні блочного пристрою (не під час відмонтування)

Корінь: Ви могли відмонтувати файлову систему, але блочний пристрій все ще утримують dm-crypt, LVM, multipath, MD RAID або loop-пристрої.

Виправлення: Використайте lsblk для картографування утримувачів; перевірте dmsetup ls --tree; закрийте luks-отвори або деактивуйте LV після чистого відмонтування.

5) Симптом: «umount зависає» замість швидкої помилки

Корінь: Зазвичай проблеми мережевих файлових систем (NFS server недоступний, жорстке монтування), або блокований I/O під час скидання. Це не те саме, що «busy».

Виправлення: Перевірте dmesg -T на предмет завислостей NFS або I/O-помилок. Переконайтеся в мережевому здоров’ї. Розгляньте lazy unmount для NFS як тактику відновлення, якщо ви приймаєте компроміси.

6) Симптом: Ви вбили PID, але «busy» лишається

Корінь: Ви вбили не той процес (керівний потік vs worker), або нащадки успадкували cwd, або сервіс одразу перезапустився через systemd, або інший простір імен утримує монтування.

Виправлення: Запустіть знову fuser -vm. Подивіться дерева процесів (ps --ppid або pstree, якщо встановлено). Зупиніть systemd-юнит замість грати в whack-a-mole з PID.

7) Симптом: «busy» після завдання на основі chroot

Корінь: Chroot-shell або допоміжний процес залишив root всередині монтування.

Виправлення: Знайдіть винуватців через перевірки /proc/*/root; вийдіть з chroot; завершіть залишкові процеси.

8) Симптом: Ви використали umount -l, відключили диск, і додатки злетіли пізніше

Корінь: Lazy unmount дозволив подальший доступ через існуючі FD. Коли пристрій зник, ті FD почали видавати помилки у вражаючий спосіб.

Виправлення: Не робіть lazy unmount для файлової системи, яку збираєтеся видалити, якщо одночасно не завершуєте утримувачів або не перебуваєте в контрольованому шляхові завершення роботи.

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

Це план «не імпровізуйте під тиском». Використовуйте для локальних файлових систем і адаптуйте під NFS та хости з контейнерами.

Контрольний список A: Стандартне відмонтування локальної файлової системи (ext4/xfs/btrfs)

  1. Підтвердити монтування і підмонтування:
    • findmnt /mnt/data
    • findmnt -R /mnt/data

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

  2. Знайти userland утримувачів:
    • sudo fuser -vm /mnt/data
    • sudo lsof +f -- /mnt/data

    Рішення: Зупиніть сервіси коректно; не вбивайте наосліп.

  3. Перевірити cwd/root pins:
    • Проскануйте /proc/*/cwd і /proc/*/root на шляхи під монтуванням.

    Рішення: Перемістіть shell, вийдіть з chroot, зупиніть build-завдання.

  4. Перевірити споживачів ядра:
    • swapon --show
    • losetup -a

    Рішення: swapoff, від’єднайте loop-и, потім повторіть.

  5. Відмонтувати дітей, потім батька:
    • sudo umount /mnt/data/cache тощо.
    • sudo umount /mnt/data

    Рішення: Якщо все ще busy — запустіть знову fuser; щось усе ще живе.

  6. Лише тоді розгляньте lazy unmount:
    • sudo umount -l /mnt/data

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

Контрольний список B: Відмонтування на хості контейнерів (очікуються простори імен)

  1. Ідентифікуйте propagation і bind mounts:
    • findmnt -o TARGET,SOURCE,OPTIONS,PROPAGATION /mnt/data
    • findmnt -R /mnt/data

    Рішення: Якщо shared, вважайте, що інші простори імен можуть брати участь.

  2. Знайдіть власників простору імен:
    • sudo lsns -t mnt

    Рішення: Націльте PID(и) рантайму контейнерів для nsenter.

  3. Огляньте зсередини підозрілого простору імен:
    • sudo nsenter -t <pid> -m -- fuser -vm /mnt/data

    Рішення: Зупиніть конкретний контейнер або навантаження рантайму, що тримає монтування.

  4. Коректно зупиняйте оркестровані сервіси:

    Рішення: Зупиняйте через systemd або оркестратор, а не методом SIGKILL-рулетки.

Контрольний список C: Відмонтування NFS-клієнта (коли з’являються таймаути і зависання)

  1. Перевірте, чи це «busy» чи «hung»:
    • Чи umount відразу помиляється, чи блокується?

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

  2. Знайдіть процеси:
    • sudo fuser -vm /mnt/nfs

    Рішення: Зупиніть утримувачів; для застряглих клієнтів розгляньте lazy unmount.

  3. Тактика відновлення:
    • sudo umount -l /mnt/nfs

    Рішення: Використовуйте, коли сервер пішов і потрібно повернути працездатність клієнта; прибирайте наслідки пізніше.

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

Міні-історія 1: Інцидент через хибне припущення

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

На першому хості umount повернув «target is busy». На виклику припустили, що це «якийсь залишковий процес», запустили lsof на монтуванні, нічого не побачили і вирішили, що можна безпечно lazy unmount. Монтування зникло, скрипт продовжився, фізичний том відключили через кілька хвилин.

Через приблизно десять хвилин застосунок почав давати випадкові I/O-помилки. Не на кожному запиті. Не послідовно. Лише на фонових задачах, що використовували довгоживучі воркери. Основний шлях обробки виглядав нормально, бо ті процеси частіше перезапускалися і «забували» старі дескриптори.

Хибне припущення було тонким: «якщо lsof нічого не показує, значить нічого не використовує». Насправді утримувач був в іншому mount namespace, створеному допоміжним сервісом, який запускав воркерів в ізольованому контексті. Lsof на хості не бачив повної картини.

Виправлення було простим, коли припинили гадати: ідентифікували mount namespaces через lsns, зайшли через nsenter в namespace воркерів, запустили fuser, зупинили потрібний юніт коректно і потім відмонтували нормально. Дія постмортему не була «не використовувати lazy unmount». Вона була «не використовувати lazy unmount, поки не доведено, хто тримає, включно з іншими просторами імен».

Міні-історія 2: Оптимізація, яка обернулась проти

Інша організація мала звичку: щоб вікна обслуговування були коротші, вони попередньо використовували automount у /etc/fstab для важких датасетів. Ідея була слушною: не монтувати величезні томи при завантаженні; монтувати на вимогу; економити час завантаження; зменшити зону впливу. У них навіть був моніторинг latентності монтувань.

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

Крок відмонтування почав флапати: відмонтування спрацювало, через секунду знову змонтувалося, і umount повертав busy, бо зараз сканер тримав файли. Команда реагувала по-людськи: вбивали процес сканера. Він перезапускався. Більше вбивань. Більше перезапусків. Зрештою хтось додумався замаскувати automount-юнит.

Оптимізація дала зворотний ефект, бо automount перетворює «директорія існує» на «пристрій має бути змонтований». Зручно до того часу, як ви хочете, щоб стан залишався відмонтуваним. Просте виправлення — явно зупиняти/відключати automount під час обслуговування і виключити ці шляхи зі сканерів. Культурне виправлення — визнати automount як функцію, що потребує операційних контролів, а не дефолт для усіх великих наборів даних.

Жарт 2: Automount — чудовий, доки не перетвориться на надто старанного стажера, який постійно підключає речі, бо «здалося, що воно відключене».

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

Був сервіс, що важив сховище й мав строгий контроль змін. SRE-команда мала плейбук: ідентифікувати утримувачів двома незалежними методами, зупиняти сервіси за назвою юніту (не PID), і перевіряти відсутність монтувань через findmnt у правильному просторі імен. Всі нарікали, що це повільно. Здавалося бюрократією під маскою ретельності.

Під час апаратної відмови треба було евакуювати дані й від’єднати збійний диск. Файлова система не відмонтовувалася. Молодший на виклику почав панікувати і тягнувся до umount -l, бо бачив це в інтернеті. Керівник попросив дотримуватися плейбука. Жодних суперечок. Просто кроки.

fuser показав процес агента бекапу. lsof виявив, що він писав у тимчасовий файл на монтуванні. Зупинка агента через systemd не допомогла, бо його породжував timer-юнит, що одразу перезапускав новий екземпляр. Другий метод плейбука це впіймав: systemctl list-timers і статус юніту показали винуватця.

Вони зупинили таймер, зупинили сервіс, перевірили відсутність утримувачів, відмонтували чисто, потім від’єднали диск без сюрпризів. Ніхто не святкував. І це правильно. У інцидент-репосі мета — «нудно», а не «модно».

Цікаві факти й історичний контекст

  • 1) «EBUSY» — це старий Unix-генетичний код. Помилка «device or resource busy» існує десятиліттями; це відмова ядра від від’єднання чогось, на що ще є посилання.
  • 2) Простори імен монтувань в Linux змінили зміст «хто його використовує». Відтоді монтування може бути зайняте «деінде», навіть якщо ваша оболонка його не бачить.
  • 3) lsof — не системний виклик; це розслідувач. Він проходить таблиці процесів і дескрипторів, щоб вивести відкриті файли, отже мають значення права і масштаб системи.
  • 4) fuser старший за контейнери, але все ще швидкий. Він часто швидший для «дай мені PID-и», бо питає ядро інакше, ніж повне дозвілкове відображення шляхів lsof.
  • 5) Lazy unmount існує, бо реальність буває брудною. umount -l створений для випадків, коли треба від’єднати шлях, хоча посилання ще є, зазвичай в процесі відновлення.
  • 6) «Busy» не тільки про файли. Cwd/root процесу — це посилання на іноді inode файлової системи. Досить посилання на каталог, щоб блокувати від’єднання.
  • 7) Swapfiles — класична пастка. Виглядають як «просто файл», поки не дізнаєшся, що ядро сприймає їх як пристрій підкачки і не дозволяє відмонтування.
  • 8) Loop-пристрої перетворюють файл на блочний пристрій. Тому образи на loop-ах тримають файлові системи зайнятими, навіть якщо ніякий userland-процес не показує очевидного відкриття файлу.
  • 9) systemd, що генерує юніти з fstab, змінив операційні очікування. Монтування стали «керованими об’єктами» з залежностями, таймаутами і automount-поведінкою.

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

Q1: Яка найшвидша одинична команда, щоб знайти, хто блокує umount?

В: Почніть з sudo fuser -vm /mnt/data. Це швидко, читабельно і каже, які PIDи залучені. Далі використайте lsof для імен файлів.

Q2: Чому lsof іноді працює вічно на великих хостах?

В: Час може рости пропорційно «процеси × файлові дескриптори», і воно часто розв’язує шляхи. На хостах з великою кількістю контейнерів і тисячами FD на процес воно виконує велику роботу. Використовуйте fuser першим, або таргетований lsof по PID, коли у вас є підозри.

Q3: Чому umount -f не працює для локальних файлових систем?

В: На Linux «force» має сенс переважно для деяких мережевих файлових систем або спеціальних випадків. Для локальних ext4/xfs ядро не просто вирве монтування, якщо воно зайняте — це створює проблеми консистентності.

Q4: Чи безпечний umount -l?

В: Може бути безпечним у контрольованих сценаріях: вам потрібно від’єднати шлях і ви знаєте, що підлегле сховище залишатиметься доступним, поки утримувачі не завершать роботу. Небезпечно, якщо ви збираєтеся видалити сховище, поки утримувачі ще працюють.

Q5: Як дізнатися, чи контейнер тримає моє монтування?

В: Перевірте mount namespaces: sudo lsns -t mnt, знайдіть ймовірні PIDи рантайму, потім nsenter -t PID -m і запустіть findmnt/fuser там. Якщо знайдете утримувачів — зупиніть контейнер/сервіс коректно.

Q6: Чи може «поточний каталог» блокувати відмонтування навіть без відкритих файлів?

В: Так. cwd процесу — це посилання на inode у тій файловій системі. Ядро не від’єднає ФС, якщо є активні посилання.

Q7: Чому воно змонтовується знову одразу після відмонтування?

В: Automount (systemd або autofs) або сервіс торкається шляху. Вимкніть/зупиніть automount-юнит під час обслуговування і знайдіть, хто пробує каталог.

Q8: Що якщо fuser показує PIDи, а вбивання їх не допомагає?

В: Можливо, ви вбиваєте воркерів, а супервізор їх перезапускає (systemd, cron, таймери), або утримувач у іншому просторі імен. Зупиніть юніт, а не симптом, і перевірте простори імен.

Q9: Який надійний спосіб довести, що воно відмонтувалося?

В: Використайте findmnt /mnt/data (відсутність виводу означає, що в поточному просторі імен воно не змонтоване). Також перевірте findmnt -R /mnt/data на залишкові підмонтування. Якщо є простори імен, підтвердьте всередині відповідного простору імен також.

Q10: Є якась операційна філософія, окрім команд?

В: Так: ідентифікуйте утримувача перед вибором методу розблокування. Як підкреслював системний мислитель John Allspaw (парафраз), стійкість походить із розуміння того, як робота дійсно виконується, а не як ви сподівалися, що вона виконується.

Подальші кроки, які слід виконати

Коли сторінка заспокоїться і ви більше не будете дивитися на «device busy», зробіть невеликі виправлення, щоб уникнути повторення болю:

  1. Зафіксуйте робочий процес у ранбуку: findmntfindmnt -Rfuserlsof → простори імен → споживачі ядра.
  2. Припиніть вбивати PIDи як першу реакцію. Надавайте перевагу зупинці відповідного systemd-юниту, контейнера або таймера. Процеси відновлюються.
  3. Аудитуйте на предмет swapfile і образів на loop під змінними монтуваннями. Якщо їх використовуєте, документуйте і маємо процедуру демонтажу.
  4. Для automounts визначте режим обслуговування: відома послідовність команд, що зупиняє automount і подавляє «дружні» проби.
  5. Навчіть команду по роботі з просторами імен. Це вже не просунутий рівень; це нормальний Linux. «Я не бачу» не те саме, що «його немає».
← Попередня
Виявлення невідповідності ashift у ZFS: як перевірити існуючі пули
Наступна →
Чому низький сегмент ринку GPU важливіший, ніж ви думаєте

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