Determinar o local de uso do inode

15

Eu instalei recentemente o Munin em um servidor web de desenvolvimento para acompanhar o uso do sistema. Eu notei que o uso de inodes do sistema está aumentando em cerca de 7-8% por dia, mesmo que o uso do disco quase não tenha aumentado. Eu estou supondo que algo está escrevendo uma tonelada de pequenos arquivos, mas não consigo encontrar o que / onde.

Eu sei como encontrar o uso do espaço em disco, mas não consigo encontrar uma maneira de resumir o uso do inode.

Existe uma boa maneira de determinar o uso do inode por diretório, para que eu possa localizar a fonte do uso?

Dave Forgac
fonte

Respostas:

15

Não espere que isso ocorra rapidamente ...

cd para um diretório em que você suspeita que possa haver um subdiretório com muitos inodes. Se esse script demorar muito, você provavelmente encontrou onde procurar no sistema de arquivos. / var é um bom começo ...

Caso contrário, se você mudar para o diretório superior desse sistema de arquivos e executar e aguardar a conclusão, você encontrará o diretório com todos os inodes.

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

Não estou preocupado com o custo da classificação. Eu executei um teste e a triagem da saída não classificada daquela contra 350.000 diretórios levou 8 segundos. A descoberta inicial levou. O custo real está abrindo todos esses diretórios no loop while. (o loop em si leva 22 segundos). (Os dados de teste foram executados em um subdiretório com 350.000 diretórios, um dos quais com um milhão de arquivos e o restante entre 1 e 15 diretórios).

Várias pessoas apontaram que ls não é bom nisso, porque classifica a saída. Eu tentei eco, mas isso também não é ótimo. Alguém apontou que o stat fornece essas informações (número de entradas do diretório), mas não é portátil. Acontece que find -maxdepth é realmente rápido ao abrir diretórios e conta .files, então ... aqui está ... pontos para todos!

chris
fonte
2
@ Mike G: Você está 100% correto sobre não ser a maneira mais rápida de fazer esse tipo de coisa. Na minha opinião, a maneira correta de otimizar isso é redirecionar para o stderr ao iniciar e concluir a parte "contar entradas do diretório" do script. Dessa forma, quando você atinge um diretório com um milhão de entradas, ele diz "processando o diretório spool / postfix / maildrop" e não diz instantaneamente "concluído" e boom - procure em spool / postfix / maildrop e você verá muitas arquivos.
chris
Eu também não estava preocupado com o custo da classificação, pois essa é uma tarefa única ou, pelo menos, pouco frequente.
Dave Forgac
7

Se o problema for um diretório com muitos arquivos, aqui está uma solução simples:

# 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

A idéia por trás da findlinha é que o tamanho de um diretório é proporcional à quantidade de arquivos diretamente dentro desse diretório. Então, aqui procuramos diretórios com toneladas de arquivos dentro dele.

Se você não quer adivinhar um número e prefere listar todos os diretórios suspeitos ordenados por "tamanho", também é fácil:

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

Grrr, comentar requer 50 repetições. Portanto, esta resposta é na verdade um comentário sobre a resposta de chris.

Como o questionador provavelmente não se importa com todos os diretórios, apenas com os piores, o uso da classificação provavelmente é um exagero muito caro.

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

Isso não é tão completo quanto a sua versão, mas o que isso faz é imprimir linhas se elas forem maiores que o máximo anterior, reduzindo bastante a quantidade de ruído impressa e economizando as despesas da classificação.

A desvantagem disso é que, se você tiver 2 diretórios muito grandes, e o primeiro tiver 1 inode a mais que o 2º, você nunca verá o 2º.

Uma solução mais completa seria escrever um script perl mais inteligente que monitore os 10 principais valores vistos e os imprima no final. Mas isso é muito longo para uma resposta rápida à falha do servidor.

Além disso, alguns scripts perl mais inteligentes permitem que você pule o loop while - na maioria das plataformas, ls classifica os resultados e isso também pode ser muito caro para diretórios grandes. O tipo ls não é necessário aqui, pois tudo o que importa é a contagem.

Mike G.
fonte
1
É verdade sobre o ls - em situações como essa, eu me preocupo mais com a clareza do que estou fazendo e não com o desempenho. Tenho certeza de que você pode usar echo $ line / * | wc -w no lugar de ls $ line | wc -l e você evita o problema de classificação ls.
chris
Acabei de executar um teste em um diretório com um milhão de arquivos e levou 22 segundos e o eco * levou 12 segundos. (Para que conste, o eco * no shell não atingirá o limite de arg porque o eco em 99% das conchas em uso ativo é um
10249 chris
ls -f não classifica os resultados. A classificação dos resultados do diretório leva a um problema comum com NFS e diretórios grandes. Se o tempo para ler e classificar o diretório (no servidor) exceder o tempo limite do NFS, o diretório e os subdiretórios serão inutilizáveis.
mpez0
5

Você pode usar este pequeno trecho:

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

Ele imprimirá quantos arquivos e diretórios existem em cada um dos diretórios da pasta atual, com os maiores infratores na parte inferior. Isso o ajudará a encontrar diretórios com muitos arquivos. ( mais informações )

Rory
fonte
Isso funcionou brilhantemente.
ptman
3

Esta não é uma resposta direta à sua pergunta, mas pesquisar arquivos modificados recentemente com um tamanho pequeno usando find pode restringir sua pesquisa:

find / -mmin -10 -size -20k
Kyle Brandt
fonte
3
find /path ! -type d | sed 's,/[^/]*$,,' | uniq -c | sort -rn

ls não vai encontrar arquivos cujos nomes começam com um ponto. Usar find evita isso. Ele localiza todos os arquivos na árvore de diretórios, exclui o nome da base do final de cada caminho e conta o número de vezes que cada caminho de diretório aparece na saída resultante. Você pode ter que colocar o "!" entre aspas, se o seu shell reclamar.

Os inodes também podem ser usados ​​por arquivos que foram excluídos, mas estão sendo mantidos abertos por um processo em execução. Se este pacote Munin incluir programas em execução constante, outra coisa a verificar é se está mantendo um número incomum de arquivos.

Kenster
fonte
Os inodes também podem ser usados ​​por diretórios realmente profundos, que isso não será encontrado. Existem vários casos estranhos nisso, mas a situação mais comum é um diretório cheio de arquivos com nomes normais.
chris
3

Eu faria uma força bruta: execute um fio de trip em todo o dispositivo para obter uma linha de base, depois execute uma verificação algum tempo depois e o diretório incorreto se destacará como um polegar dolorido.

Geoff Fritz
fonte
Isso provavelmente levaria um bilhão de anos. Uma coisa mais rápida a fazer é executar lsof | grep DIR e procure em cada um desses diretórios muitos arquivos novos.
chris
2
Ok, e quanto a isso: find / | tipo> /tmp/find1.txt; encontrar / | tipo> /tmp/find2.txt; diff /tmp/find1.txt /tmp/find2.txt
Geoff Fritz
2

(não poder comentar está realmente ficando velho - isso é para ficar com fome)

egorgry - ls -i imprime o inode NUMBER para uma entrada, não o inode COUNT.

Experimente com um arquivo no seu diretório - você (provavelmente) verá um número igualmente alto, mas não é a contagem de inodes, é apenas o # inode para o qual a entrada do diretório aponta.

Mike G.
fonte
ri muito. Eu votei em você um. Obrigada pelo esclarecimento. O uso do inode sempre foi confuso.
egorgry
graças Agora eu estou com medo de converter isso em um comentário no seu nó, no caso de eu perder o karma quando eu apagar esta resposta :)
Mike G.
2

Atualizar

Um liner que retorna a contagem de inodes de cada filho de um determinado diretório com as maiores entradas na parte inferior.

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

Resposta original

#!/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

Execute-o assim (considerando que o script acima reside em um arquivo executável no seu diretório de trabalho)

./indist / | sort -n
raphinesse
fonte
1

o uso do inode é de aproximadamente um por arquivo ou diretório, certo? Então faz

find [path] -print | wc -l

para contar aproximadamente quantos inodes são usados ​​em [path].

pjz
fonte
1

Tentei escrever um pipeline de shell eficiente, mas ele se tornou pesado e lento ou impreciso, por exemplo,

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

listará diretórios de folha (e alguns outros) com mais de 1000 arquivos neles. Então, aqui está um script Perl para fazê-lo com eficiência, tanto em tempo quanto em RAM. Saída é como

«Arquivos na subárvore» «arquivos diretamente no diretório» «nome do diretório»

para que você possa massagear e filtrá-lo facilmente usando ferramentas normais, por exemplo, sort (1) ou awk (1) como acima.

#! /usr/bin/perl -w
# Written by Kjetil Torgrim Homme <[email protected]>

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);

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

minha casa no meu laptop está usando 131191 inodes.

faminto
fonte
3
ls -i imprime o inode NUMBER para uma entrada, não o inode COUNT. Experimente com um arquivo no seu diretório - você (provavelmente) verá um número igualmente alto, mas não é a contagem de inodes, é apenas o # inode para o qual a entrada do diretório aponta.
21820 egorgry