Determina la posizione di utilizzo di Inode


15

Di recente ho installato Munin su un server Web di sviluppo per tenere traccia dell'utilizzo del sistema. Ho notato che l'utilizzo dell'inode del sistema sta aumentando di circa il 7-8% al giorno anche se l'utilizzo del disco è aumentato a malapena. Immagino che qualcosa stia scrivendo un sacco di piccoli file ma non riesco a trovare cosa / dove.

So come trovare l'utilizzo dello spazio su disco ma non riesco a trovare un modo per riassumere l'utilizzo dell'inode.

C'è un buon modo per determinare l'utilizzo dell'inode per directory in modo che io possa individuare l'origine dell'utilizzo?

Risposte:


15

Non aspettarti che funzioni rapidamente ...

cd in una directory in cui sospetti che potrebbe esserci una sottodirectory con molti inode. Se questo script richiede molto tempo, probabilmente hai trovato dove cercare il file system. / var è un buon inizio ...

Altrimenti, se passi alla directory principale in quel filesystem ed esegui questo e aspetti che finisca, troverai la directory con tutti gli inode.

find . -type d | 
while 
  read line  
do 
  echo "$( find "$line" -maxdepth 1 | wc -l) $line"  
done | 
sort -rn | less

Non sono preoccupato per il costo dell'ordinamento. Ho eseguito un test e l'ordinamento dell'output non ordinato rispetto a 350.000 directory ha richiesto 8 secondi. La scoperta iniziale è durata. Il vero costo sta aprendo tutte queste directory nel ciclo while. (il loop stesso richiede 22 secondi). (I dati del test sono stati eseguiti su una sottodirectory con 350.000 directory, una delle quali aveva un milione di file, il resto aveva tra 1 e 15 directory).

Diverse persone hanno sottolineato che ls non è eccezionale perché ordina l'output. Avevo provato l'eco, ma anche questo non è eccezionale. Qualcun altro aveva sottolineato che stat fornisce queste informazioni (numero di voci della directory) ma che non è portabile. Si scopre che find -maxdepth è molto veloce nell'aprire le directory e conta i file .file, quindi ... eccolo ... punti per tutti!


2
@mike G: Hai ragione al 100% sul fatto che questo non è il modo più veloce per fare questo genere di cose. Nella mia mente, il modo corretto di ottimizzare questo è reindirizzare a stderr quando si avvia e termina la parte "conta voci di directory" dello script. In questo modo, quando colpisci una directory con un milione di voci, dirà "elaborazione spool directory / postfix / maildrop" e quindi non dirai istantaneamente "finito" e boom - guarda in spool / postfix / maildrop e vedrai un sacco di File.
chris,

Inoltre, non ero preoccupato per il costo dell'ordinamento in quanto si tratta di un compito una tantum o almeno abbastanza raro.
Dave Forgac,

7

Se il problema è una directory con troppi file, ecco una soluzione semplice:

# Let's find which partition is out of inodes:
$ df -hi
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/sda3               2.4M    2.4M       0  100% /
...

# Okay, now we know the mount point with no free inodes,
# let's find a directory with too many files:
$ find / -xdev -size +100k -type d

L'idea alla base del find è che la dimensione di una directory è proporzionale alla quantità di file direttamente all'interno di quella directory. Quindi, qui cerchiamo le directory con tonnellate di file al suo interno.

Se non vuoi indovinare un numero e preferisci elencare tutte le directory sospette ordinate per "dimensione", anche questo è facile:

# Remove the "sort" command if you want incremental output
find / -xdev -size +10k -type d -printf '%s %p\n' | sort -n

6

Grrr, commentare richiede 50 rep. Quindi questa risposta è in realtà un commento sulla risposta di Chris.

Dal momento che l'interrogante probabilmente non si preoccupa di tutte le directory, solo di quelle peggiori, l'uso dell'ordinamento è probabilmente un'operazione eccessivamente costosa.

find . -type d | 
while 
  read line  
do 
  echo "$(ls "$line" | wc -l) $line"  
done | 
perl -a -ne'next unless $F[0]>=$max; print; $max=$F[0]'  | less

Questo non è completo come la tua versione, ma ciò che fa sono le linee di stampa se sono più grandi del massimo precedente, riducendo notevolmente la quantità di rumore stampato e risparmiando le spese dell'ordinamento.

Il rovescio della medaglia di questo è se hai 2 directory molto grandi, e il primo ha 1 inode in più rispetto al 2 °, non vedrai mai il 2 °.

Una soluzione più completa sarebbe quella di scrivere uno script perl più intelligente che tenga traccia dei primi 10 valori visti e li stampi alla fine. Ma è troppo lungo per una rapida risposta al server.

Inoltre, alcuni script perl quasi più intelligenti ti permetterebbero di saltare il ciclo while - sulla maggior parte delle piattaforme, ordina i risultati e questo può anche essere molto costoso per le grandi directory. Il tipo di ls non è necessario qui, poiché tutto ciò che ci interessa è il conteggio.


1
Vero riguardo a ls - in situazioni come questa mi preoccupo di più del fatto che sia chiaro cosa sto facendo e non tanto della performance. Sono abbastanza sicuro che puoi usare echo $ line / * | wc -w al posto di ls $ line | wc -l ed eviti il ​​problema dell'ordinamento di ls.
chris,

Ho appena eseguito un test su una directory con un milione di file e ho impiegato 22 secondi e echo * ha impiegato 12 secondi. (Per la cronaca, echo * in shell non raggiungerà il limite arg perché l'eco nel 99% delle shell in uso attivo è un built-in)
chris

ls -f non ordinerà i risultati. L'ordinamento dei risultati della directory comporta un problema comune con NFS e directory di grandi dimensioni. Se il tempo necessario per leggere e ordinare la directory (sul server) supera il timeout NFS, la directory e le sottodirectory sono inutilizzabili.
mpez0,

5

Puoi usare questo piccolo frammento:

find | cut -d/ -f2 | uniq -c | sort -n

Stamperà quanti file e directory ci sono in ciascuna delle directory nella cartella corrente, con i più grandi trasgressori in fondo. Ti aiuterà a trovare le directory che contengono molti file. ( maggiori informazioni )


Questo ha funzionato brillantemente.
ptman,

3

Questa non è una risposta diretta alla tua domanda, ma la ricerca di file modificati di recente con dimensioni ridotte utilizzando find potrebbe restringere la ricerca:

find / -mmin -10 -size -20k

3
find /path ! -type d | sed 's,/[^/]*$,,' | uniq -c | sort -rn

ls non troveranno i file i cui nomi iniziano con un punto. L'uso di find evita questo. Questo trova ogni file nella struttura di directory, cancella il nome di base dalla fine di ogni percorso e conta il numero di volte in cui ciascun percorso di directory appare nell'output risultante. Potrebbe essere necessario inserire il "!" tra virgolette se la tua shell se ne lamenta.

Gli Inodi possono anche essere utilizzati da file che sono stati eliminati ma che vengono tenuti aperti da un processo in esecuzione. Se questo pacchetto Munin include programmi in esecuzione costante, un'altra cosa da verificare è se tiene aperto un numero insolito di file.


Gli inode potrebbero anche essere presi da directory molto profonde, che non troveranno. Ci sono un certo numero di strani casi limite in questo, ma la situazione più comune è una directory piena di file con nomi normali.
chris,

3

Lo farei con forza brutale: eseguo tripwire sull'intero dispositivo per una linea di base, quindi eseguo un controllo un po 'di tempo dopo e la directory offensiva sporgerà come un pollice dolorante.


Ciò richiederebbe probabilmente un miliardo di anni. Una cosa più veloce da fare è eseguire lsof | grep DIR e cerca in ognuna di quelle directory molti nuovi file.
chris,

2
Ok, che ne dici di questo: trova / | ordina> /tmp/find1.txt; trova / | ordina> /tmp/find2.txt; diff /tmp/find1.txt /tmp/find2.txt
Geoff Fritz

2

(non poter commentare è davvero invecchiare - questo è per egorgry)

egorgry - ls -i stampa l'inode NUMBER per una voce, non l'inode COUNT.

Provalo con un file nella tua directory - vedrai (probabilmente) un numero altrettanto elevato, ma non è il conteggio degli inode, è solo l'inode # a cui punta la tua directory.


lol. Ti ho votato per uno. Grazie per la spiegazione. l'utilizzo dell'inode è sempre stato fonte di confusione.
egorgry

grazie Ora ho paura di convertirlo in un commento sul tuo nodo, nel caso in cui perdo il karma quando cancello questa risposta :)
Mike G.

2

Aggiornare

Un liner che restituisce il conteggio degli inode di ogni figlio di una determinata directory con le voci più grandi in fondo.

find . -mindepth 1 -printf "%p/%i\n" \
  | awk -F/ '{print $2"/"$NF}' | sort -u \
  | cut -d/ -f1 | uniq -c | sort -n

Risposta originale

#!/bin/bash
# Show inode distribution for given directory

dirs=$(find $1 -mindepth 1 -maxdepth 1 -type d)

for dir in $dirs
do
    inode_count=$(find $dir -printf "%i\n" 2> /dev/null | sort -u | wc -l)
    echo "$inode_count $dir"
done

Eseguilo in questo modo (dato che lo script sopra si trova in un file eseguibile nella directory di lavoro)

./indist / | sort -n

1

l'utilizzo dell'inode è approssimativamente uno per file o directory, giusto? Così

find [path] -print | wc -l

per contare approssimativamente il numero di inode utilizzati in [percorso].


1

Ho provato a scrivere una pipeline di shell efficiente, ma è diventata ingombrante e lenta o imprecisa, ad es.

find . -depth -printf '%h\n' | uniq -c | awk '$1>1000'

elencherà le directory foglia (e alcune altre) con più di 1000 file al loro interno. Quindi, ecco uno script Perl per farlo in modo efficiente sia nel tempo che nella RAM. L'output è simile

«Files-in-subtree» «files-direttamente-nella-directory» «nome-directory»

così puoi massaggiarlo e filtrarlo facilmente usando strumenti normali, ad es. sort (1) o awk (1) come sopra.

#! /usr/bin/perl -w
# Written by Kjetil Torgrim Homme <kjetil.homme@redpill-linpro.com>

use strict;
use File::Find;

my %counted;
my %total;

sub count {
    ++$counted{$File::Find::dir};
}

sub exeunt {
    my $dir = $File::Find::dir;

    # Don't report leaf directories with no files
    return unless $counted{$dir}; 

    my $parent = $dir;
    $parent =~ s!/[^/]*$!!;

    $total{$dir} += $counted{$dir};
    $total{$parent} += $total{$dir} if $parent ne $dir;
    printf("%8d %8d %s\n", $total{$dir}, $counted{$dir}, $dir);
    delete $counted{$dir};
    delete $total{$dir};
}

die "Usage: $0 [DIRECTORY...]\n" if (@ARGV && $ARGV[0] =~ /^-/);
push(@ARGV, ".") unless @ARGV;

finddepth({ wanted => \&count, postprocess => \&exeunt}, @ARGV);

-1
[gregm@zorak2 /]$ ls -i /home
131191 gregm

la mia casa sul mio laptop sta usando 131191 inode.


3
ls -i stampa l'inode NUMBER per una voce, non l'inode COUNT. Provalo con un file nella tua directory - vedrai (probabilmente) un numero altrettanto elevato, ma non è il conteggio degli inode, è solo l'inode # a cui punta la tua directory.
egorgry
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.