Ви виконуєте 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)
- Підтвердити монтування і підмонтування:
findmnt /mnt/datafindmnt -R /mnt/data
Рішення: Якщо є підмонтування, заплануйте порядок відмонтування (глибші шляхи першими).
- Знайти userland утримувачів:
sudo fuser -vm /mnt/datasudo lsof +f -- /mnt/data
Рішення: Зупиніть сервіси коректно; не вбивайте наосліп.
- Перевірити cwd/root pins:
- Проскануйте
/proc/*/cwdі/proc/*/rootна шляхи під монтуванням.
Рішення: Перемістіть shell, вийдіть з chroot, зупиніть build-завдання.
- Проскануйте
- Перевірити споживачів ядра:
swapon --showlosetup -a
Рішення: swapoff, від’єднайте loop-и, потім повторіть.
- Відмонтувати дітей, потім батька:
sudo umount /mnt/data/cacheтощо.sudo umount /mnt/data
Рішення: Якщо все ще busy — запустіть знову fuser; щось усе ще живе.
- Лише тоді розгляньте lazy unmount:
sudo umount -l /mnt/data
Рішення: Якщо збираєтеся видаляти сховище, не залишайте утримувачів працювати.
Контрольний список B: Відмонтування на хості контейнерів (очікуються простори імен)
- Ідентифікуйте propagation і bind mounts:
findmnt -o TARGET,SOURCE,OPTIONS,PROPAGATION /mnt/datafindmnt -R /mnt/data
Рішення: Якщо shared, вважайте, що інші простори імен можуть брати участь.
- Знайдіть власників простору імен:
sudo lsns -t mnt
Рішення: Націльте PID(и) рантайму контейнерів для
nsenter. - Огляньте зсередини підозрілого простору імен:
sudo nsenter -t <pid> -m -- fuser -vm /mnt/data
Рішення: Зупиніть конкретний контейнер або навантаження рантайму, що тримає монтування.
- Коректно зупиняйте оркестровані сервіси:
Рішення: Зупиняйте через systemd або оркестратор, а не методом SIGKILL-рулетки.
Контрольний список C: Відмонтування NFS-клієнта (коли з’являються таймаути і зависання)
- Перевірте, чи це «busy» чи «hung»:
- Чи
umountвідразу помиляється, чи блокується?
Рішення: Якщо блокується, спочатку дослідіть стан сервера/мережі.
- Чи
- Знайдіть процеси:
sudo fuser -vm /mnt/nfs
Рішення: Зупиніть утримувачів; для застряглих клієнтів розгляньте lazy unmount.
- Тактика відновлення:
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», зробіть невеликі виправлення, щоб уникнути повторення болю:
- Зафіксуйте робочий процес у ранбуку:
findmnt→findmnt -R→fuser→lsof→ простори імен → споживачі ядра. - Припиніть вбивати PIDи як першу реакцію. Надавайте перевагу зупинці відповідного systemd-юниту, контейнера або таймера. Процеси відновлюються.
- Аудитуйте на предмет swapfile і образів на loop під змінними монтуваннями. Якщо їх використовуєте, документуйте і маємо процедуру демонтажу.
- Для automounts визначте режим обслуговування: відома послідовність команд, що зупиняє automount і подавляє «дружні» проби.
- Навчіть команду по роботі з просторами імен. Це вже не просунутий рівень; це нормальний Linux. «Я не бачу» не те саме, що «його немає».