Dlaczego df i du się różnią: poszukiwanie ukrytych gigabajtów w systemie Linux
Polecenie df -h pokazuje 95% zajętości dysku, a du -sh /* sumuje jedynie 20%. Pozostałe 75% jest niewidoczne dla du, ale zajmuje bloki na dysku. Przyczyną są otwarte deskryptory plików procesów do usuniętych plików. df odczytuje superblok systemu plików bezpośrednio, du przechodzi przez drzewo katalogowe i ignoruje pliki bez wpisów w nim.
Inode przechowuje metadane pliku, w tym listę bloków danych. rm usuwa tylko wpis w katalogu (hard link), licznik odwołań inode spada, ale jeśli proces trzyma deskryptor, bloki pozostają przydzielone.
Schemat:
Przed rm:
katalog inode bloki
/var/log/ --> app.log --> [blok1][blok2]
^
deskryptor nginx (pid 1234, fd 7)
Po rm:
katalog inode bloki
/var/log/ --> (usunięto) --> [blok1][blok2]
^
deskryptor nginx
du nie widzi pliku w /var/log/, df rejestruje zajęte bloki.
Klasyczne przypadki problemu
Rotacja logów: logrotate wykonuje rm starego pliku, ale nginx lub java trzyma deskryptor od momentu uruchomienia. Proces zapisuje do usuniętego pliku, df rejestruje wzrost zajętości, du — nie.
Inne scenariusze:
- Długożyjące demony z otwartymi logami.
- Błędy w skryptach czyszczenia, gdzie rm poprzedza zamknięcie deskryptora.
- Kontenery Docker, gdzie procesy wewnątrz trzymają pliki hosta.
Problem narasta: gigabajty znikają niezauważenie, aż dysk się zapełni.
Poszukiwanie procesów z usuniętymi plikami
Użyj lsof +L1 dla plików z NLINK < 1 (usunięte z FS, ale otwarte):
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 1234 www 7w REG 8,1 2147483648 0 12345 /var/log/app.log (deleted)
java 5678 app 3w REG 8,1 536870912 0 67890 /var/log/service.log (deleted)
Kluczowe pola:
- COMMAND/PID: proces i ID.
- FD: deskryptor (7w — zapis, numer 7).
- SIZE/OFF: rozmiar — źródło utraconych GB.
- NLINK=0: potwierdzenie usunięcia.
- NAME (deleted): ścieżka przed rm.
Filtr według rozmiaru: lsof +L1 | awk '$7 > 104857600 {print}' (pliki >100MB).
Zwolnienie miejsca bez restartu
Dla procesu PID 1234, FD 7:
$ > /proc/1234/fd/7
/proc — procfs w pamięci. Polecenie zeruje plik przez deskryptor, jądro zwalnia bloki natychmiast. df się zaktualizuje, proces kontynuuje zapis do fd 7.
Sprawdź: lsof +L1 | grep 1234 — SIZE/OFF=0 lub pusto.
Automatyzacja skryptem:
#!/bin/bash
lsof +L1 | awk '$7 > 104857600 && $9 ~ /deleted/ {print $2, $8}' | while read pid fd; do
echo "Zerujemy $pid/$fd"
> /proc/$pid/fd/$fd
echo "Zwolniono"
done
Zapobieganie problemowi w logrotate
Unikaj rm w rotacji. W /etc/logrotate.d/app:
/var/log/app.log {
daily
rotate 7
compress
copytruncate
}
copytruncate kopiuje log, następnie zeruje oryginał (mechanizm >). Deskryptor się nie zmienia.
Minus: mikrozatrzymanie kopiowania — możliwa utrata linii.
Alternatywa dla zero-loss:
- logrotate: mv stary → nowy plik.
- kill -HUP <pid> (SIGHUP dla reopen loga).
nginx: nginx -s reopen. systemd: systemctl reload nginx.
Monitorowanie i alerty
Skrypt do sprawdzania:
lsof +L1 -F s | awk '/^s/ && substr($0,2)+0 > 104857600 {count++} END {print count+0}'
Wypisuje liczbę usuniętych plików >100MB.
Integracja Prometheus:
node_filefd_allocated— wzrost otwartych fd.- Porównanie df/disk_used vs du sum.
Cron co 5 min: alert przy rozbieżności >10%.
Co jest ważne
- df pokazuje przydzielone bloki superbloku, du — widoczne w katalogu pliki.
lsof +L1znajduje procesy z (deleted) plikami po NLINK=0.- Zwolnić:
> /proc/PID/fd/FD— zerowanie bez restartu. - logrotate copytruncate zapobiega problemowi bez HUP.
- Monitorowanie: skrypty na lsof lub metryki node_exporter.
— Editorial Team
Brak komentarzy.