Determinar la ubicación del uso de inodo


15

Recientemente instalé Munin en un servidor web de desarrollo para realizar un seguimiento del uso del sistema. Noté que el uso de inodo del sistema está aumentando en aproximadamente un 7-8% por día, aunque el uso del disco apenas ha aumentado en absoluto. Supongo que algo está escribiendo una tonelada de pequeños archivos, pero no puedo encontrar qué / dónde.

Sé cómo encontrar el uso de espacio en disco, pero parece que no puedo encontrar una manera de resumir el uso de inodo.

¿Hay una buena manera de determinar el uso de inodo por directorio para poder localizar la fuente del uso?

Respuestas:


15

No esperes que esto se ejecute rápidamente ...

cd a un directorio donde sospeche que puede haber un subdirectorio con muchos inodes. Si este script toma una gran cantidad de tiempo, es probable que haya encontrado en qué parte del sistema de archivos buscar. / var es un buen comienzo ...

De lo contrario, si cambia al directorio superior en ese sistema de archivos y ejecuta esto y espera a que termine, encontrará el directorio con todos los inodos.

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

No me preocupa el costo de la clasificación. Realicé una prueba y clasifiqué la salida sin clasificar de eso en 350,000 directorios y tardé 8 segundos. El hallazgo inicial tomó. El costo real es abrir todos estos directorios en el ciclo while. (el bucle en sí toma 22 segundos). (Los datos de prueba se ejecutaron en un subdirectorio con 350,000 directorios, uno de los cuales tenía un millón de archivos, el resto tenía entre 1 y 15 directorios).

Varias personas habían señalado que ls no es bueno en eso porque ordena la salida. Intenté echo, pero eso tampoco es genial. Alguien más había señalado que stat proporciona esta información (número de entradas de directorio) pero que no es portátil. Resulta que find -maxdepth es realmente rápido al abrir directorios y cuenta .files, así que ... aquí está ... ¡puntos para todos!


2
@ Mike G: Estás 100% en lo correcto al decir que esta no es la forma más rápida de hacer este tipo de cosas. En mi opinión, la forma correcta de optimizar esto es redirigir a stderr cuando se inicia y finaliza la parte "script de entradas de directorio". De esa manera, cuando llegue a un directorio con un millón de entradas, dirá "procesando directorio spool / postfix / maildrop" y luego no dirá instantáneamente "terminado" y boom - mire en spool / postfix / maildrop y verá muchos archivos.
chris

Tampoco me preocupaba el costo de la clasificación, ya que esta es una tarea única o al menos bastante infrecuente.
Dave Forgac

7

Si el problema es un directorio con demasiados archivos, aquí hay una solución simple:

# 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

La idea detrás de la findlínea es que el tamaño de un directorio es proporcional a la cantidad de archivos directamente dentro de ese directorio. Entonces, aquí buscamos directorios con toneladas de archivos dentro.

Si no desea adivinar un número y prefiere enumerar todos los directorios sospechosos ordenados por "tamaño", también es fácil:

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

6

Grrr, comentar requiere 50 rep. Entonces, esta respuesta es en realidad un comentario sobre la respuesta de Chris.

Dado que el interrogador probablemente no se preocupa por todos los directorios, solo por los peores, entonces usar sort es probablemente una exageración muy 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

Esto no es tan completo como su versión, pero lo que hace es imprimir líneas si son más grandes que el máximo anterior, lo que reduce en gran medida la cantidad de ruido impreso y ahorra el gasto del tipo.

La desventaja de esto es que si tiene 2 directorios muy grandes, y el primero tiene 1 inodo más que el segundo, nunca verá el segundo.

Una solución más completa sería escribir un script perl más inteligente que haga un seguimiento de los 10 valores principales vistos e imprima esos al final. Pero eso es demasiado largo para una respuesta rápida por defecto del servidor.

Además, algunas secuencias de comandos perl medianamente más inteligentes le permitirán omitir el ciclo while: en la mayoría de las plataformas, esto ordena los resultados, y eso también puede ser muy costoso para directorios grandes. El tipo ls no es necesario aquí, ya que lo único que nos importa es el conteo.


1
Es cierto sobre el ls: en situaciones como esta, me preocupa más que quede claro lo que estoy haciendo y no tanto sobre el rendimiento. Estoy bastante seguro de que puedes usar echo $ line / * | wc -w en lugar de ls $ line | wc -l y evita el problema de clasificación ls.
chris

Acabo de ejecutar una prueba en un directorio con un millón de archivos y l tardó 22 segundos y echo * tardó 12 segundos. (Para el registro, echo * en el shell no alcanzará el límite arg porque el eco en el 99% de los shells en uso activo está incorporado)
Chris

ls -f no ordenará los resultados. La clasificación de los resultados del directorio conduce a un problema común con NFS y directorios grandes. Si el tiempo para leer y ordenar el directorio (en el servidor) excede el tiempo de espera de NFS, el directorio y los subdirectorios no se pueden usar.
mpez0

5

Puedes usar este pequeño fragmento:

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

Imprimirá cuántos archivos y directorios hay en cada uno de los directorios en la carpeta actual, con los delincuentes más grandes en la parte inferior. Le ayudará a encontrar directorios que tengan muchos archivos. ( más información )


Esto funcionó brillantemente.
ptman

3

Esta no es una respuesta directa a su pregunta, pero la búsqueda de archivos modificados recientemente con un tamaño pequeño mediante la búsqueda podría reducir su búsqueda:

find / -mmin -10 -size -20k

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

ls no van a encontrar los archivos cuyos nombres comienzan con un punto. Usar find evita esto. Esto encuentra todos los archivos en el árbol de directorios, borra el nombre base del final de cada ruta y cuenta el número de veces que cada ruta de directorio aparece en la salida resultante. Puede que tenga que poner el "!" entre comillas si su shell se queja de ello.

Los inodos también pueden ser utilizados por archivos que han sido eliminados pero que se mantienen abiertos mediante un proceso en ejecución. Si este paquete de Munin incluye algún programa que se ejecute constantemente, otra cosa que debe verificar es si mantiene abierto un número inusual de archivos.


Los inodes también podrían ser tomados por directorios realmente profundos, que esto no encontrará. Hay varios casos extremos extraños en esto, pero la situación más común es un directorio lleno de archivos con nombres normales.
chris

3

Haría fuerza bruta en este caso: ejecute tripwire en todo el dispositivo para una línea de base, luego ejecute una verificación algún tiempo después y el directorio ofensivo sobresaldrá como un pulgar dolorido.


Eso probablemente llevaría mil millones de años. Una cosa más rápida es ejecutar lsof | grep DIR y busque en cada uno de esos directorios muchos archivos nuevos.
chris

2
Ok, ¿qué tal esto: find / | ordenar> /tmp/find1.txt; encontrar / | ordenar> /tmp/find2.txt; diff /tmp/find1.txt /tmp/find2.txt
Geoff Fritz

2

(no poder comentar realmente está envejeciendo, esto es para egorgry)

egorgry - ls -i imprime el NÚMERO de inodo para una entrada, no el CODO de inodo.

Pruébelo con un archivo en su directorio: (probablemente) verá un número igualmente alto, pero no es el recuento de inodos, es solo el inodo # al que apunta la entrada de su directorio.


jajaja Te voté uno. Gracias por la explicación. El uso de inodo siempre ha sido confuso.
egorgry

gracias Ahora tengo miedo de convertir esto en un comentario en su nodo, en caso de que pierda el karma cuando elimine esta respuesta :)
Mike G.

2

Actualizar

Una línea que devuelve el recuento de inodos de cada elemento secundario del directorio dado con las entradas más grandes en la parte inferior.

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

Respuesta 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

Ejecútelo así (dado que el script anterior reside en un archivo ejecutable en su directorio de trabajo)

./indist / | sort -n

1

El uso de inodo es aproximadamente uno por archivo o directorio, ¿verdad? También

find [path] -print | wc -l

para contar aproximadamente cuántos inodos se utilizan en [ruta].


1

Traté de escribir una tubería de shell eficiente, pero se volvió difícil de manejar y lenta o inexacta, por ejemplo,

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

enumerará directorios de hoja (y algunos otros) con más de 1000 archivos en ellos. Entonces, aquí hay un script de Perl para hacerlo de manera eficiente tanto en tiempo como en RAM. La salida es como

«Archivos-en-subárbol» «archivos-directamente-en-directorio» «nombre-directorio»

para que pueda masajearlo y filtrarlo fácilmente con herramientas normales, por ejemplo, ordenar (1) o awk (1) como se indicó anteriormente

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

mi casa en mi computadora portátil está usando inodos 131191.


3
ls -i imprime el NÚMERO de inodo para una entrada, no el CODO de inodo. Pruébelo con un archivo en su directorio: (probablemente) verá un número igualmente alto, pero no es el recuento de inodos, es solo el inodo # al que apunta la entrada de su directorio.
egorgry
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.