Dyski i miejsce
Po tej lekcji: kursant sprawdza, ile miejsca i ile i-węzłów zostało na dyskach, znajduje folder, który zjada miejsce, rozpoznaje dyski po nazwie i UUID i rozwiązuje zagadkę „dysk pełny, a du pokazuje mniej”.
Wstęp
Trzecia w nocy. Monitoring krzyczy, że dysk na serwerze jest pełny. Logujesz się, znajdujesz winowajcę: pięciogigabajtowy log aplikacji. Usuwasz. Sprawdzasz.
Dalej sto procent.
Liczysz ręcznie, co jest na dysku. Wychodzi dwa gigabajty. Dysk ma dwadzieścia. Gdzie jest osiemnaście?
Linux kłamie? Nie. Linux nigdy nie kłamie. Po prostu odpowiada dokładnie na pytanie, które zadałeś, a nie na to, które miałeś na myśli. Dziś nauczymy się zadawać właściwe pytania i na końcu lekcji sami odtworzymy tę zagadkę, krok po kroku, z rozwiązaniem.
Zaczynamy moduł o dyskach. Komendy z tej lekcji tylko patrzą, nic nie psują, więc możesz je spokojnie wpisywać na swoim komputerze. Od następnej lekcji będziemy formatować i partycjonować, i wtedy przesiadamy się na maszynę wirtualną.
df: ile miejsca zostało w budynku
Pamiętasz, że folder to budynek z pokojami? Teraz wyobraź sobie, że cały dysk to biurowiec. Przy wejściu siedzi zarządca z księgą. W księdze ma zapisane, ile metrów jest zajętych, a ile wolnych. Nie chodzi po pokojach. Po prostu patrzy w swoje liczniki.
Tym zarządcą jest df, od „disk free”.
df -h /srv/daneFilesystem Size Used Avail Use% Mounted on
/dev/loop0 452M 106M 312M 26% /srv/dane-h znaczy „dla ludzi”: zamiast bloków po kilobajcie dostajesz M i G. Kolumny po kolei: jakie urządzenie, ile ma razem, ile zajęte, ile wolne, procent zajętości i gdzie jest podłączone w drzewie folderów. Ta ostatnia kolumna jest ważna. Montowanie to podłączanie skrzydła budynku, a df mówi o całym skrzydle, nie o jednym pokoju. Jak wpiszesz df -h /var/log, dostaniesz skrzydło, w którym leży /var/log. Często to po prostu /.
u mnie dysk testowy nazywa się loop0, bo to plik udający dysk. Na Twojej maszynie zobaczysz /dev/sda2, /dev/vda1, /dev/nvme0n1p2 albo /dev/mapper/...
Samo df -h bez ścieżki pokaże wszystkie skrzydła naraz, łącznie z kilkoma tmpfs, czyli systemami plików w pamięci RAM. Żeby nie zaśmiecały widoku:
df -hT -x tmpfs -x devtmpfs-T dodaje kolumnę z typem systemu plików, a -x wyklucza wskazany typ. O typach, czyli ext4, XFS i spółce, opowiem w następnej lekcji.
Spostrzegawczy kursant policzy: 106 plus 312 to nie 452. Gdzie reszta? To nie błąd. System plików ext4 domyślnie trzyma 5% miejsca w rezerwie dla roota, żeby nawet przy pełnym dysku usługi systemowe i administrator mieli gdzie oddychać. Jak miejsce parkingowe dla karetki: niby puste, ale nie dla ciebie.
df -i: numerki w szatni
Teraz zagadka numer dwa, prawdziwa, z życia.
Wyobraź sobie szatnię w teatrze. Wieszaków jest mnóstwo. Ale numerków jest sto. Kiedy sto osób odda płaszcze, szatnia jest „pełna”, mimo że połowa wieszaków stoi pusta. Szatniarz nie przyjmie płaszcza bez numerka.
Na dysku numerki nazywają się i-węzły, po angielsku inodes. Każdy plik, nawet pusty, potrzebuje jednego. Liczbę i-węzłów w ext4 ustala się przy zakładaniu systemu plików i sama nie rośnie, kiedy plików przybywa.
df -i /srv/malyFilesystem Inodes IUsed IFree IUse% Mounted on
/dev/loop6 112 11 101 10% /srv/malyZrobiłem sobie specjalnie malutki system plików, który ma tylko 112 i-węzłów. Teraz tworzę w pętli puste pliki, jak aplikacja, która zapisuje każdą sesję użytkownika w osobnym pliku:
cd /srv/maly
for i in $(seq 1 200); do touch "sesja_$i" || break; done
echo test > nowy.txt
df -h /srv/maly
df -i /srv/malytouch: cannot touch 'sesja_102': No space left on device
bash: nowy.txt: No space left on device
Filesystem Size Used Avail Use% Mounted on
/dev/loop6 16M 24K 15M 1% /srv/maly
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/loop6 112 112 0 100% /srv/maly„Brak miejsca na urządzeniu”. A df -h mówi: zajęty jeden procent. Jeśli nie znasz df -i, możesz tak siedzieć do rana. W prawdziwym życiu robią to miliony plików sesji PHP, kolejka poczty albo cache, którego nikt nie czyści.
Jak znaleźć folder z największą liczbą plików? du umie liczyć numerki zamiast metrów:
sudo du --inodes -x -d1 /var | sort -n | tail -5I już wiesz, gdzie siedzi tłum.
du: miarka chodząca po pokojach
df to zarządca z księgą. du, od „disk usage”, to ktoś z miarką, kto chodzi po pokojach i mierzy, co w nich stoi. Wolniej, ale wiesz dokładnie, który pokój jest zawalony.
du -sh /srv/dane106M /srv/dane-s to „summary”, czyli podaj jedną sumę, a nie wypisuj każdego podfolderu. -h to znowu „dla ludzi”.
Najważniejsza komenda tej lekcji, którą będziesz wpisywać przez całą karierę:
du -h --max-depth=1 /srv/dane | sort -h4.0K /srv/dane/logi
16K /srv/dane/lost+found
21M /srv/dane/www
86M /srv/dane/dom
106M /srv/dane--max-depth=1, w skrócie -d1, znaczy: zejdź tylko jedno piętro w dół, nie wypisuj każdego pokoju w każdym podfolderze. sort -h z lekcji o rurach układa rozmiary typu K, M, G w dobrej kolejności. Największy na dole, tuż nad kursorem, gdzie od razu go widzisz.
I teraz metoda: wchodzisz w największy folder i powtarzasz. Potem w największy w nim. I tak dalej, aż znajdziesz winowajcę. Jak szukanie, kto zjadł ciasto: idziesz po okruszkach.
Dwie flagi, które warto dorzucić przy szukaniu od korzenia:
sudo du -xh -d1 / 2>/dev/null | sort -h | tail -5-x znaczy: nie wychodź poza ten system plików. Bez tego du zacznie mierzyć podłączony dysk sieciowy albo /proc i będzie mierzył do jutra. sudo, bo do niektórych pokojów zwykły użytkownik nie ma klucza. A 2>/dev/null wycisza drugi głośnik, gdyby i tak coś narzekało.
ncdu: du z kierownicą
Chodzenie po okruszkach jest fajne, ale jest narzędzie, które robi to za ciebie i jeszcze rysuje paski.
sudo apt install ncdu # [Ubuntu]
sudo dnf install epel-release # [RHEL] na Rocky/Alma, na samym RHEL EPEL włącza się inaczej
sudo dnf install ncdu # [RHEL] pakiet pochodzi z EPEL
sudo ncdu -x /Strzałkami chodzisz po liście, Enterem wchodzisz do folderu, strzałką w lewo wracasz. n sortuje po nazwie, s po rozmiarze, g przełącza paski i procenty, ? pokazuje pomoc, q wychodzi.
Jest też klawisz d, który usuwa zaznaczony plik albo folder. Po potwierdzeniu, ale jednak. O trzeciej w nocy jeden klawisz za daleko i usuwasz bazę danych zamiast logu. Dlatego na serwerze produkcyjnym odpalaj ncdu -r, czyli tryb tylko do odczytu. Klawisz d wtedy nie działa. Jak dawanie dziecku nożyczek z zaokrąglonymi końcami: mniej frajdy, więcej palców.
lsblk i blkid: spis dysków i dowody osobiste
df mówi o systemach plików, które są podłączone. A co z dyskami, które są w komputerze, ale jeszcze nic na nich nie ma? Do tego jest lsblk, „list block devices”. To plan budynku od strony konstrukcji: jakie są dyski, jakie mają partycje, co gdzie jest podłączone.
lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 500M 0 loop /srv/daneU mnie, w środowisku testowym, dysk to plik podpięty jako loop0. Na typowej maszynie wirtualnej zobaczysz drzewko: dysk sda albo vda, a pod nim partycje sda1, sda2. Na laptopie z dyskiem NVMe: nvme0n1, a partycje nvme0n1p1, nvme0n1p2. To dodatkowe „p” przed numerem partycji jest tam dlatego, że nazwa dysku sama kończy się cyfrą. Inaczej nvme0n11 mogłoby znaczyć dwie różne rzeczy.
Przydatne warianty: lsblk -f dokłada typ systemu plików, etykietę, UUID i wolne miejsce, a lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS pozwala wybrać kolumny samemu.
A blkid to dowód osobisty systemu plików:
sudo blkid /dev/loop0/dev/loop0: LABEL="DANE" UUID="e578e9a2-ed52-4d7e-ab9b-ebbf0df9785e" BLOCK_SIZE="4096" TYPE="ext4"Najważniejsze tu jest UUID, czyli unikalny numer nadawany przy zakładaniu systemu plików. Nazwy sda, sdb potrafią się zamienić miejscami, kiedy dołożysz dysk albo zmienisz kolejność kabli. UUID się nie zmienia. To jak różnica między „pan z trzeciego okienka” a numerem PESEL. Za trzy lekcje, przy montowaniu na stałe, UUID będzie naszym najlepszym przyjacielem.
Zagadka: dysk pełny, a du pokazuje mniej
Wracamy do trzeciej w nocy. Odtwarzamy wszystko na naszym dysku testowym /srv/dane.
Najpierw aplikacja produkuje ogromny log:
dd if=/dev/zero of=/srv/dane/logi/app.log bs=1M count=350 status=none
df -h /srv/daneFilesystem Size Used Avail Use% Mounted on
/dev/loop0 452M 371M 47M 89% /srv/daneTeraz ktoś trzyma ten plik otwarty. W prawdziwym życiu to usługa, która do niego pisze. U mnie zrobi to tail -f, czyli dokładnie to, co zostawia się w zapomnianym oknie tmuxa:
tail -f /srv/dane/logi/app.log > /dev/null &I nasz admin o trzeciej w nocy robi to, co każdy by zrobił:
rm /srv/dane/logi/app.log
df -h /srv/dane
du -sh /srv/daneFilesystem Size Used Avail Use% Mounted on
/dev/loop0 452M 371M 47M 89% /srv/dane
21M /srv/daneProszę. Zarządca z księgą mówi: 371 megabajtów zajęte. Człowiek z miarką mówi: 21. Oba mają rację.
Wyobraź sobie hotel. Gość mieszka w pokoju 14. Recepcja skreśla go z listy gości, czyli usuwa nazwę pliku z folderu. Ale gość dalej ma klucz i siedzi w pokoju. Sprzątaczka nie wejdzie, dopóki gość nie odda klucza. Pokój jest zajęty, a na liście go nie ma.
Człowiek z miarką, czyli du, chodzi według listy, więc pokoju 14 nie widzi. Zarządca, czyli df, patrzy na liczniki zajętości, więc widzi.
Jak znaleźć gościa z kluczem? lsof, „list open files”:
sudo lsof +aL1 /srv/daneCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
tail 6366 root 3r REG 7,0 367001600 0 14 /srv/dane/logi/app.log (deleted)+L1 znaczy: pokaż otwarte pliki, które mają mniej niż jedną nazwę, czyli zero. Kolumna NLINK to właśnie liczba nazw. a każe połączyć ten warunek ze ścieżką, czyli „tylko z tego dysku”. Sama komenda sudo lsof +L1 pokaże takie pliki z całego systemu. Na końcu linijki dumny dopisek (deleted). Usunięty, ale żyje. Plik zombie. Prawie jak proces zombie, tylko tym razem rachunek wisi u gościa, nie u kelnera.
Jeśli nie masz lsof, doinstaluj: sudo apt install lsof [Ubuntu] albo sudo dnf install lsof [RHEL]. Albo zapytaj jądro bez pośredników:
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null/proc/6366/fd/3 -> /srv/dane/logi/app.log (deleted)Rozwiązania są trzy, od najlepszego.
Pierwsze: zrestartuj usługę, która trzyma plik. sudo systemctl restart nazwa-uslugi. Usługa zamyka stary plik, otwiera nowy, miejsce wraca.
Drugie: jeśli restart w środku dnia nie wchodzi w grę, możesz przyciąć plik przez jego uchwyt w /proc, bez zabijania procesu:
: > /proc/6366/fd/3
df -h /srv/daneFilesystem Size Used Avail Use% Mounted on
/dev/loop0 452M 21M 397M 5% /srv/daneMiejsce wróciło, a tail dalej żyje i nawet nie wie, co się stało. Dwukropek to w Bashu komenda, która nic nie robi, a strzałka czyści plik do zera. Nic plus strzałka równa się pusty plik. Matematyka Basha.
Trzecie, czyli lekcja na przyszłość: logu, do którego ktoś pisze, nie usuwaj przez rm. Wyczyść go: : > plik albo truncate -s 0 plik. Gość zostaje w pokoju, ale pokój jest posprzątany.
Jak to działa pod spodem
df wywołuje funkcję systemową statfs. Jądro odpowiada liczbami, które system plików trzyma w swoim nagłówku: tyle bloków razem, tyle wolnych, tyle i-węzłów. Dlatego df jest natychmiastowy, nawet na dysku z milionem plików.
du przechodzi całe drzewo folderów i dla każdego pliku pyta jądro, ile bloków ten plik zajmuje. Potem dodaje. Dlatego na dużym dysku potrafi mielić minutami.
Teraz najciekawsze. Nazwa pliku i sam plik to w Linuksie dwie różne rzeczy. Plik to i-węzeł z danymi. Nazwa to tylko wpis w folderze, który wskazuje na ten i-węzeł. Takich nazw może być kilka, to dowiązania twarde, które już znasz. rm w rzeczywistości wywołuje funkcję unlink: usuwa jedną nazwę i zmniejsza licznik nazw w i-węźle. Jądro zwalnia miejsce dopiero wtedy, gdy spełnione są oba warunki: licznik nazw spadł do zera i żaden proces nie ma pliku otwartego. W naszej zagadce pierwszy warunek był spełniony, drugi nie.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
df | -h | rozmiary dla ludzi (K, M, G) |
df | -T | pokazuje typ systemu plików |
df | -i | pokazuje i-węzły zamiast miejsca |
df | -x TYP | pomija systemy plików danego typu, np. tmpfs |
du | -s | tylko suma dla podanego folderu |
du | -h | rozmiary dla ludzi |
du | -d N, --max-depth=N | schodzi najwyżej N poziomów w dół |
du | -x | nie wychodzi poza jeden system plików |
du | --inodes | liczy pliki (i-węzły) zamiast bajtów |
du | --apparent-size | deklarowany rozmiar zamiast zajętych bloków |
ncdu | -x | nie wychodzi poza jeden system plików |
ncdu | -r | tylko do odczytu, klawisz d nie usuwa |
lsblk | -f | dokłada system plików, etykietę i UUID |
lsblk | -o KOLUMNY | wybrane kolumny |
blkid | URZĄDZENIE | typ, UUID i etykieta systemu plików |
lsof | +L1 | otwarte pliki, które zostały usunięte |
lsof | +aL1 PUNKT | to samo, ale tylko na jednym systemie plików |
⚠ Typowy błąd
Szukasz, co zajmuje miejsce w folderze domowym, i wpisujesz:
cd /srv/dane/dom
du -sh *5.0M raport.pdfPięć megabajtów. A du -sh . mówi 86. Gdzie reszta?
Gwiazdka z lekcji o wzorcach nazw nie łapie plików i folderów zaczynających się od kropki. A to właśnie ukryte foldery typu .cache, .local czy .npm najczęściej puchną w tajemnicy:
du -sh .[!.]* *81M .cache
5.0M raport.pdfWzorzec .[!.]* znaczy „kropka, potem cokolwiek, co nie jest kropką”, żeby nie złapać .., czyli folderu piętro wyżej. Albo prościej: du -h -d1 . | sort -h, które ukrytych folderów nie pomija w ogóle. Wniosek: przy szukaniu miejsca zawsze du -d1 na folderze, a nie gwiazdka.
✎ Zadanie
- Sprawdź
df -hTidf -ina swojej maszynie. Czy któryś system plików ma ponad 80% zajęte, w miejscu albo w i-węzłach? - Znajdź pięć największych folderów w
/var:sudo du -xh -d1 /var | sort -h | tail -5, a potem wejdź w największy i powtórz. - Odtwórz zagadkę z dzisiejszej lekcji w swoim folderze domowym: zrób plik 100 MB przez
head -c 100M /dev/zero > ~/duzy.bin, otwórz go w tle przeztail -f ~/duzy.bin > /dev/null &, usuń go i porównajdf -h ~przed i po. Znajdź go przezlsof +L1, odzyskaj miejsce bez zabijania procesu, a na koniec posprzątaj przezkill %1.
Pokaż rozwiązanie
Punkty pierwszy i drugi to po prostu df -hT, df -i i sudo du -xh -d1 /var | sort -h | tail -5. Na świeżej maszynie zwykle wygrywa /var/lib z pakietami albo /var/cache z pobranymi paczkami. Jeśli wygrał /var/log, ktoś tu dużo gada.
Najciekawszy jest punkt trzeci. Mój dysk jest duży, więc żeby zobaczyć sto megabajtów różnicy, użyłem df -m, czyli wyników w megabajtach:
df -m ~
head -c 100M /dev/zero > ~/duzy.bin
df -m ~
tail -f ~/duzy.bin > /dev/null &
rm ~/duzy.bin
df -m ~
lsof +L1 | grep duzy
: > /proc/21262/fd/3
df -m ~
kill %1Filesystem 1M-blocks Used Available Use% Mounted on
/dev/sda2 50254 17879 29798 38% /
/dev/sda2 50254 17979 29698 38% /
/dev/sda2 50254 17979 29698 38% /
tail 21262 eryk 3r REG 8,2 104857600 0 647175 /home/eryk/duzy.bin (deleted)
tail: /home/eryk/duzy.bin: file truncated
/dev/sda2 50254 17879 29797 38% /Po utworzeniu pliku przybyło sto megabajtów. Po rm nic się nie zmieniło, bo tail dalej trzymał klucz do pokoju. lsof pokazał numer procesu i numer uchwytu, czyli to 3r. Przycięcie przez /proc oddało miejsce, a tail nawet grzecznie zgłosił: „file truncated”, czyli „ktoś mi wyczyścił plik”. Jak gość hotelowy, który zauważył, że zniknął mu minibar.
Numer procesu u Ciebie będzie inny. Weź go z kolumny PID.
Co dalej
Dziś cały czas mówiliśmy „system plików”, „ext4”, „i-węzły”. W następnej lekcji zaglądamy do środka: co to właściwie jest system plików, czym różni się ext4 od XFS i Btrfs, po co mu dziennik i jak go założyć i naprawić. Na pliku, więc żaden prawdziwy dysk nie ucierpi.