Skip to main content

Cyber AI România

Joi, 29 octombrie 2026

„No space left on device”, dar df arată spațiu liber: diagnosticarea inodurilor în Linux

Un serviciu care procesează documente începe să raporteze No space left on device, deși df -h arată că volumul are zeci de megabytes disponibili. În următoarele 30–60 de minute vei reproduce situația într-o imagine ext4 izolată, vei demonstra că s-au terminat inodurile, vei identifica directorul responsabil și vei elibera controlat suficiente intrări pentru ca scrierea să funcționeze din nou.

Laboratorul nu umple partiția sistemului. Folosește o imagine de 64 MiB montată printr-un dispozitiv loop, cu un număr intenționat mic de inoduri. La final, df -h va indica spațiu disponibil, df -i va ajunge la IUse%=100%, iar crearea unui fișier va eșua. După ștergerea fișierelor temporare din laborator, verificarea va reveni la starea OK.

Fixăm laboratorul pe Ubuntu Server 24.04 LTS și ext4

Varianta de referință este Ubuntu Server 24.04 LTS, pe o mașină virtuală sau un server de test unde contul are acces la sudo. Sunt folosite utilitarele din coreutils, util-linux, e2fsprogs, findutils și psmisc.

Ai nevoie de aproximativ 100 MiB liberi în /var/tmp, 256 MiB RAM și un dispozitiv loop disponibil. Nu este necesar un GPU. Comenzile de mai jos confirmă distribuția, kernelul, arhitectura, memoria, spațiul și versiunile instalate:

cat /etc/os-release
uname -r
uname -m
free -h
df -h /var/tmp
df --version | head -n 1
mount --version | head -n 1
mke2fs -V 2>&1 | head -n 1
find --version | head -n 1
sudo losetup -f

Un rezultat de referință arată astfel; numărul kernelului, versiunile de securitate ale pachetelor și dispozitivul loop pot diferi după actualizări:

PRETTY_NAME="Ubuntu 24.04.x LTS"
x86_64
df (GNU coreutils) 9.4
mount from util-linux 2.39.3
mke2fs 1.47.0
find (GNU findutils) 4.9.0
/dev/loop0

losetup -f nu alocă dispozitivul, ci afișează primul dispozitiv loop liber. Dacă nu întoarce o cale, laboratorul nu trebuie pornit până nu este rezolvată lipsa dispozitivelor loop.

Instalarea tuturor pachetelor folosite se face într-un singur bloc:

sudo apt update
sudo apt install --yes coreutils util-linux e2fsprogs findutils psmisc

Fișierele proiectului vor avea următoarea structură:

/opt/inode-lab/
├── create-lab.sh       # creează imaginea ext4 și o montează
├── fill-inodes.sh      # produce fișiere goale până la epuizarea inodurilor
├── diagnose.sh         # compară blocurile disponibile cu inodurile disponibile
└── check-inodes.sh     # întoarce OK, WARNING sau CRITICAL
/var/tmp/inode-lab.img  # imaginea ext4 de 64 MiB
/mnt/inode-lab/         # punctul de montare al imaginii
└── queue/              # directorul care simulează coada serviciului

Un inode păstrează metadatele unui obiect din sistemul de fișiere: tip, permisiuni, proprietar, timpi și legături către date. Un fișier obișnuit sau un director nou consumă un inode chiar dacă are conținut foarte mic. Blocurile de date și inodurile sunt contabilizate separat; de aceea df -h și df -i pot spune două lucruri aparent contradictorii.

Pasul 1 — Creăm o imagine ext4 cu puține inoduri

De ce: imaginea separată permite reproducerea erorii fără a consuma inodurile partiției reale. Opțiunea -N a programului mkfs.ext4 solicită un număr determinat de inoduri la formatare.

Creează mai întâi directorul proiectului:

sudo install -d -m 0755 /opt/inode-lab

Fișierul /opt/inode-lab/create-lab.sh:

sudo tee /opt/inode-lab/create-lab.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

readonly IMAGE="/var/tmp/inode-lab.img"
readonly MOUNT_POINT="/mnt/inode-lab"
readonly IMAGE_SIZE="64M"
readonly REQUESTED_INODES="1024"

if mountpoint -q "$MOUNT_POINT"; then
    printf 'EROARE: %s este deja montat.n' "$MOUNT_POINT" >&2
    exit 1
fi

truncate -s "$IMAGE_SIZE" "$IMAGE"
mkfs.ext4 -q -F -N "$REQUESTED_INODES" "$IMAGE"

install -d -m 0755 "$MOUNT_POINT"
mount -o loop "$IMAGE" "$MOUNT_POINT"
install -d -m 0755 "$MOUNT_POINT/queue"

printf 'Laborator montat la %sn' "$MOUNT_POINT"
df -h "$MOUNT_POINT"
df -i "$MOUNT_POINT"
EOF

sudo chmod 0755 /opt/inode-lab/create-lab.sh
sudo /opt/inode-lab/create-lab.sh

truncate -s 64M creează imaginea cu dimensiunea logică de 64 MiB. mkfs.ext4 -F formatează fișierul ca ext4, iar -N 1024 solicită aproximativ 1.024 de inoduri. Numărul final poate fi ajustat de mke2fs pentru structurile sistemului de fișiere, motiv pentru care valoarea reală se citește întotdeauna cu df -i.

mount -o loop asociază automat imaginea cu un dispozitiv loop. Operațiile de formatare și montare cer privilegii administrative.

Ieșirea trebuie să conțină punctul de montare și două rapoarte distincte:

Laborator montat la /mnt/inode-lab
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0       ...   ...   ...   ... /mnt/inode-lab
Filesystem      Inodes IUsed IFree IUse% Mounted on
/dev/loop0         ...   ...   ...    ... /mnt/inode-lab

Valorile și numele /dev/loopN sunt exemple. Punct de control: dacă /mnt/inode-lab nu apare în ambele rapoarte, verifică findmnt /mnt/inode-lab înainte de a continua.

Pasul 2 — Umplem tabela de inoduri fără să umplem blocurile de date

De ce: fișierele goale consumă inoduri, dar aproape deloc spațiu pentru conținut. Astfel izolăm cauza pe care vrem să o diagnosticăm.

Fișierul /opt/inode-lab/fill-inodes.sh:

sudo tee /opt/inode-lab/fill-inodes.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

readonly MOUNT_POINT="/mnt/inode-lab"
readonly QUEUE="$MOUNT_POINT/queue"

if ! mountpoint -q "$MOUNT_POINT"; then
    printf 'EROARE: %s nu este montat.n' "$MOUNT_POINT" >&2
    exit 1
fi

counter=0

while true; do
    file=$(printf '%s/job-%06d.tmp' "$QUEUE" "$counter")

    if error=$(touch "$file" 2>&1); then
        ((counter += 1))
    else
        printf 'Crearea s-a oprit după %d fișiere.n' "$counter"
        printf '%sn' "$error"
        break
    fi
done

df -h "$MOUNT_POINT"
df -i "$MOUNT_POINT"
EOF

sudo chmod 0755 /opt/inode-lab/fill-inodes.sh
sudo /opt/inode-lab/fill-inodes.sh

Numele sunt generate determinist, de la job-000000.tmp în sus. touch creează câte un fișier gol, iar bucla se oprește la prima eroare. Mesajul este capturat și afișat, nu ignorat.

Rezultatul așteptat este:

Crearea s-a oprit după ... fișiere.
touch: cannot touch '/mnt/inode-lab/queue/job-....tmp': No space left on device
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0       ...   ...   ...   ... /mnt/inode-lab
Filesystem      Inodes IUsed IFree IUse% Mounted on
/dev/loop0         ...   ...     0  100% /mnt/inode-lab

Numărul de fișiere și valorile în MiB pot diferi, deoarece ext4 folosește inoduri și pentru propriile directoare. Condițiile care trebuie să coincidă sunt IFree=0, IUse%=100% și existența spațiului în coloana Avail.

Punct de control: dacă raportul pentru inoduri nu ajunge la 100%, confirmă că fill-inodes.sh a ieșit din cauza lui touch, nu pentru că sesiunea a fost întreruptă.

Pasul 3 — Separăm lipsa blocurilor de epuizarea inodurilor

De ce: df -h măsoară spațiul ocupat de blocuri, în timp ce df -i afișează totalul, consumul și disponibilul de inoduri. Pentru mesajul ENOSPC, trebuie verificate ambele.

Fișierul /opt/inode-lab/diagnose.sh:

sudo tee /opt/inode-lab/diagnose.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

readonly MOUNT_POINT="/mnt/inode-lab"

if ! mountpoint -q "$MOUNT_POINT"; then
    printf 'EROARE: %s nu este montat.n' "$MOUNT_POINT" >&2
    exit 1
fi

printf '%sn' '=== Blocuri de date ==='
df -h "$MOUNT_POINT"

printf '%sn' '=== Inoduri ==='
df -i "$MOUNT_POINT"

printf '%sn' '=== Tipul sistemului de fișiere ==='
findmnt --noheadings --output SOURCE,FSTYPE,TARGET "$MOUNT_POINT"

printf '%sn' '=== Numărul obiectelor pe directoare-părinte ==='
find "$MOUNT_POINT" -xdev -mindepth 1 -printf '%hn' |
    sort |
    uniq -c |
    sort -nr
EOF

sudo chmod 0755 /opt/inode-lab/diagnose.sh
sudo /opt/inode-lab/diagnose.sh

findmnt confirmă că este analizat volumul ext4 corect. În comanda find, -xdev împiedică traversarea altor sisteme de fișiere, -mindepth 1 exclude rădăcina căutării, iar %h afișează directorul-părinte. Numărarea arată unde sunt concentrate obiectele.

Ieșirea relevantă trebuie să semene cu aceasta:

=== Blocuri de date ===
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0       ...   ...   ...   ... /mnt/inode-lab
=== Inoduri ===
Filesystem      Inodes IUsed IFree IUse% Mounted on
/dev/loop0         ...   ...     0  100% /mnt/inode-lab
=== Tipul sistemului de fișiere ===
/dev/loop0 ext4 /mnt/inode-lab
=== Numărul obiectelor pe directoare-părinte ===
   ... /mnt/inode-lab/queue

Aceasta este dovada diagnosticului: nu lipsesc blocurile afișate de df -h; lipsește capacitatea de a crea încă un obiect. Un ghid despre eliberarea spațiului fără ștergerea fișierelor importante rezolvă consumul de blocuri, dar în cazul de față indicatorul decisiv este df -i.

Punct de control: dacă directorul cu cele mai multe obiecte nu este queue, oprește-te și verifică dacă ai rulat comenzile exclusiv în imaginea de laborator.

Pasul 4 — Eliberăm controlat 200 de inoduri și testăm o scriere nouă

Avertisment: comanda următoare șterge fișiere. Ruleaz-o numai asupra căii exacte /mnt/inode-lab/queue, creată de laborator.

De ce: remedierea nu constă în mărirea arbitrară a volumului. Pe ext4, numărul de inoduri este stabilit la crearea sistemului de fișiere; pentru incidentul curent trebuie eliminate, arhivate sau mutate obiectele identificate ca temporare și dispensabile.

sudo find /mnt/inode-lab/queue 
    -maxdepth 1 
    -type f 
    -name 'job-*.tmp' 
    -printf '%fn' |
    sort |
    sed -n '1,200p' |
    while IFS= read -r name; do
        sudo rm -- "/mnt/inode-lab/queue/$name"
    done

sudo touch /mnt/inode-lab/queue/recovery-probe.tmp
sudo sh -c 'printf "scriere reușităn" > /mnt/inode-lab/queue/recovery-probe.tmp'
sudo cat /mnt/inode-lab/queue/recovery-probe.tmp
df -i /mnt/inode-lab

-maxdepth 1 limitează operația la fișierele direct din queue. -type f exclude directoarele, iar -name 'job-*.tmp' selectează numai obiectele generate de laborator. sed -n '1,200p' transmite primele 200 de nume sortate către bucla de ștergere.

Trebuie să vezi:

scriere reușită
Filesystem      Inodes IUsed IFree IUse% Mounted on
/dev/loop0         ...   ...   ...    ... /mnt/inode-lab

IFree trebuie acum să fie mai mare decât zero. Într-un incident real, nu șterge fișiere doar pentru că sunt mici sau numeroase. Confirmă mai întâi aplicația care le deține, politica de retenție și existența unui backup sau a unei arhive, dacă datele trebuie păstrate.

Punct de control: dacă touch eșuează în continuare, rulează din nou df -i /mnt/inode-lab și verifică dacă filtrul a găsit fișiere cu find /mnt/inode-lab/queue -maxdepth 1 -type f -name 'job-*.tmp' | head.

Pasul 5 — Transformăm diagnosticul într-un control repetabil

De ce: o verificare bazată numai pe df -h nu avertizează asupra acestei situații. Scriptul următor raportează separat procentul de inoduri folosite și folosește coduri de ieșire potrivite pentru monitorizare.

Fișierul /opt/inode-lab/check-inodes.sh:

sudo tee /opt/inode-lab/check-inodes.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

readonly PATH_TO_CHECK="${1:-/mnt/inode-lab}"
readonly WARNING_PERCENT=80
readonly CRITICAL_PERCENT=90

if [[ ! -e "$PATH_TO_CHECK" ]]; then
    printf 'UNKNOWN: calea %s nu existăn' "$PATH_TO_CHECK"
    exit 3
fi

read -r usage target < <(
    df --output=ipcent,target "$PATH_TO_CHECK" |
        tail -n 1
)

used=${usage%%%}

if (( used >= CRITICAL_PERCENT )); then
    printf 'CRITICAL: inoduri folosite=%s%% pe %sn' "$used" "$target"
    exit 2
elif (( used >= WARNING_PERCENT )); then
    printf 'WARNING: inoduri folosite=%s%% pe %sn' "$used" "$target"
    exit 1
else
    printf 'OK: inoduri folosite=%s%% pe %sn' "$used" "$target"
    exit 0
fi
EOF

sudo chmod 0755 /opt/inode-lab/check-inodes.sh
sudo /opt/inode-lab/check-inodes.sh /mnt/inode-lab
printf 'Cod de ieșire: %dn' "$?"

Câmpul ipcent cerut prin df --output reprezintă procentul inodurilor folosite. Expresia ${usage%%%} elimină semnul %. Scriptul întoarce 0 pentru OK, 1 pentru WARNING, 2 pentru CRITICAL și 3 dacă nu poate verifica acea cale.

După ștergerea celor 200 de fișiere, rezultatul ar trebui să fie OK sau WARNING, în funcție de numărul real de inoduri creat de ext4:

OK: inoduri folosite=...% pe /mnt/inode-lab
Cod de ieșire: 0

Punct de control: dacă primești CRITICAL, rulează df -i /mnt/inode-lab; scriptul funcționează, dar nu au fost eliberate suficiente inoduri pentru pragul de 90%.

Acest incident este diferit de limita descriptorilor deschiși. Dacă mesajul aplicației este Too many open files, urmează diagnosticul dedicat pentru limitele de proces, systemd și kernel, nu procedura pentru inoduri.

Mesajele care localizează defecțiunea în acest laborator

touch: cannot touch '...': No space left on device

Cauza așteptată este IFree=0. Confirmă și recuperează spațiul de metadate astfel:

df -i /mnt/inode-lab
sudo find /mnt/inode-lab/queue -maxdepth 1 -type f -name 'job-*.tmp' | wc -l

mount: /mnt/inode-lab: failed to setup loop device for /var/tmp/inode-lab.img

Nu există un dispozitiv loop disponibil sau mediul nu permite montarea lui. Verifică fără a modifica alte dispozitive:

sudo losetup -f
lsmod | grep '^loop'
sudo modprobe loop
sudo losetup -f

Într-un container fără privilegiul de montare, laboratorul trebuie rulat într-o mașină virtuală Ubuntu, nu prin slăbirea izolării containerului.

EROARE: /mnt/inode-lab este deja montat.

Scriptul refuză să reformateze imaginea asociată unei montări active. Inspectează montarea și continuă cu laboratorul existent sau curăță-l:

findmnt /mnt/inode-lab
sudo umount /mnt/inode-lab
sudo /opt/inode-lab/create-lab.sh

umount: /mnt/inode-lab: target is busy.

Un proces are directorul curent sau un fișier deschis în montare. Ieși din acel director și identifică procesul înainte de o nouă încercare:

cd /
sudo fuser -vm /mnt/inode-lab
sudo umount /mnt/inode-lab

Nu folosi demontarea forțată ca primă soluție.

CRITICAL: inoduri folosite=...% pe /mnt/inode-lab

Acesta nu este un defect al scriptului, ci rezultatul pragului de 90%. Afișează consumul și directoarele dominante:

df -i /mnt/inode-lab
sudo /opt/inode-lab/diagnose.sh

Elimină numai obiectele temporare confirmate sau mărește retenția operațională prin arhivare pe alt sistem de fișiere.

Demontăm imaginea și eliminăm toate urmele laboratorului

Avertisment: următoarele comenzi șterg imaginea și toate fișierele create în laborator. Verifică exact că țintele sunt /mnt/inode-lab, /var/tmp/inode-lab.img și /opt/inode-lab.

cd /

if mountpoint -q /mnt/inode-lab; then
    sudo umount /mnt/inode-lab
fi

sudo rm -f /var/tmp/inode-lab.img
sudo rmdir /mnt/inode-lab
sudo rm -rf /opt/inode-lab

findmnt /mnt/inode-lab || true
test ! -e /var/tmp/inode-lab.img
test ! -e /opt/inode-lab
printf 'Laborator eliminat.n'

Nu a fost modificat niciun fișier de configurare existent, deci nu există o copie .bak de restaurat. Imaginea conținea integral sistemul de fișiere experimental.

Lista de acceptanță pentru diagnosticul ENOSPC

  • ☐ df -h /mnt/inode-lab arată blocuri disponibile.
  • ☐ df -i /mnt/inode-lab ajunge la IFree=0 în testul controlat.
  • ☐ touch produce No space left on device numai în imaginea de laborator.
  • ☐ findmnt confirmă că ținta este ext4 și este montată printr-un dispozitiv loop.
  • ☐ Numărarea cu find -xdev indică directorul queue.
  • ☐ Sunt șterse numai fișierele job-*.tmp generate de laborator.
  • ☐ recovery-probe.tmp poate fi creat și scris după remediere.
  • ☐ check-inodes.sh întoarce starea și codul de ieșire corespunzător.
  • ☐ Imaginea este demontată înainte de ștergere.

Următorul pas util este integrarea lui check-inodes.sh într-un sistem de monitorizare care citește codurile de ieșire și păstrează separat alertele pentru blocuri și inoduri. Pentru volume ext4 noi care vor găzdui foarte multe fișiere mici, proiectarea trebuie făcută înainte de formatare, pe baza numărului estimat de obiecte și a documentației mke2fs; un volum mai mare nu repară automat un sistem de fișiere deja creat cu prea puține inoduri.

Întrebări frecvente

De ce spune Linux „No space left on device” dacă df -h arată spațiu?

Mesajul poate apărea când nu mai există inoduri libere, chiar dacă mai sunt disponibile blocuri pentru conținut. Verifică aceeași cale atât cu df -h, cât și cu df -i.

Un fișier gol consumă un inode?

Da. Fișierul are nevoie de metadate și de un inode chiar dacă nu conține date utile, motiv pentru care multe fișiere goale pot epuiza inodurile fără să umple volumul în MiB sau GiB.

Pot crește numărul de inoduri pe volumul ext4 deja montat?

În laborator, numărul este stabilit la formatarea cu mkfs.ext4. Remedierea incidentului constă în eliminarea, arhivarea sau mutarea controlată a obiectelor dispensabile; pentru un volum nou, numărul trebuie proiectat înainte de formatare.

Este suficient să monitorizez procentul din df -h?

Nu. Controlul din tutorial citește separat câmpul ipcent, deoarece utilizarea blocurilor și utilizarea inodurilor sunt resurse distincte.

Surse

Facebook
X
WhatsApp
„No space left on device”, dar df arată spațiu liber: diagnosticarea inodurilor în Linux

Te-ar putea interesa si: