Déterminer l'emplacement de l'utilisation des inodes


15

J'ai récemment installé Munin sur un serveur Web de développement pour garder une trace de l'utilisation du système. Je n'ai pas remarqué que l'utilisation des inodes du système augmente d'environ 7 à 8% par jour, même si l'utilisation du disque a à peine augmenté. Je suppose que quelque chose écrit une tonne de petits fichiers mais je ne trouve pas quoi / où.

Je sais comment trouver l'utilisation de l'espace disque mais je n'arrive pas à trouver un moyen de résumer l'utilisation des inodes.

Existe-t-il un bon moyen de déterminer l'utilisation des inodes par répertoire afin que je puisse localiser la source de l'utilisation?

Réponses:


15

Ne vous attendez pas à ce que cela fonctionne rapidement ...

cd dans un répertoire où vous pensez qu'il pourrait y avoir un sous-répertoire avec beaucoup d'inodes. Si ce script prend énormément de temps, vous avez probablement trouvé où chercher dans le système de fichiers. / var est un bon début ...

Sinon, si vous passez au répertoire supérieur de ce système de fichiers et l'exécutez et attendez qu'il se termine, vous trouverez le répertoire avec tous les inodes.

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

Je ne m'inquiète pas du coût du tri. J'ai effectué un test et le tri de la sortie non triée de celle-ci sur 350 000 répertoires a pris 8 secondes. La découverte initiale a pris. Le véritable coût est d'ouvrir tous ces répertoires dans la boucle while. (la boucle elle-même prend 22 secondes). (Les données de test ont été exécutées sur un sous-répertoire avec 350 000 répertoires, dont l'un avait un million de fichiers, le reste avait entre 1 et 15 répertoires).

Diverses personnes ont fait remarquer que ls n'est pas génial car il trie la sortie. J'avais essayé l'écho, mais ce n'est pas génial non plus. Quelqu'un d'autre avait fait remarquer que stat donne cette information (nombre d'entrées de répertoire) mais qu'elle n'est pas portable. Il s'avère que find -maxdepth est vraiment rapide pour ouvrir des répertoires et compte les fichiers. Donc, ici, c'est .. des points pour tout le monde!


2
@mike G: Vous avez raison à 100% de ne pas être le moyen le plus rapide de faire ce genre de choses. Dans mon esprit, la bonne façon d'optimiser ceci est de rediriger vers stderr lors du démarrage et de la fin de la partie "compter les entrées du répertoire" du script. De cette façon, lorsque vous frappez un répertoire avec un million d'entrées, il dira "traitement du répertoire spool / postfix / maildrop" puis ne dira pas instantanément "terminé" et boom - regardez dans spool / postfix / maildrop et vous verrez beaucoup de des dossiers.
chris

Je ne m'inquiétais pas non plus du coût du tri, car il s'agit d'une tâche ponctuelle ou au moins assez rare.
Dave Forgac

7

Si le problème concerne un répertoire contenant trop de fichiers, voici une solution 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

L'idée derrière la findligne est que la taille d'un répertoire est proportionnelle à la quantité de fichiers directement à l'intérieur de ce répertoire. Nous recherchons donc ici des répertoires contenant des tonnes de fichiers.

Si vous ne voulez pas deviner un nombre et que vous préférez répertorier tous les répertoires suspects classés par "taille", rien de plus simple:

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

6

Grrr, commenter nécessite 50 répétitions. Donc, cette réponse est en fait un commentaire sur la réponse de Chris.

Étant donné que le questionneur ne se soucie probablement pas de tous les répertoires, seulement des pires, alors l'utilisation du tri est probablement une exagération très coûteuse.

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

Ce n'est pas aussi complet que votre version, mais cela permet d'imprimer des lignes si elles sont plus grandes que le maximum précédent, ce qui réduit considérablement la quantité de bruit imprimé et économise les dépenses de tri.

L'inconvénient est que si vous avez 2 très gros répertoires et que le premier a 1 inode de plus que le 2e, vous ne verrez jamais le 2e.

Une solution plus complète serait d'écrire un script perl plus intelligent qui garde une trace des 10 premières valeurs vues et les imprime à la fin. Mais c'est trop long pour une réponse rapide de panne de serveur.

De plus, certains scripts perl moyennement plus intelligents vous permettraient de sauter la boucle while - sur la plupart des plates-formes, ls trie les résultats, et cela peut également être très coûteux pour les grands répertoires. Le tri ls n'est pas nécessaire ici, car nous nous soucions uniquement du nombre.


1
C'est vrai pour le ls - dans des situations comme celle-ci, je m'inquiète davantage de savoir clairement ce que je fais et pas tellement de la performance. Je suis sûr que vous pouvez utiliser echo $ line / * | wc -w à la place de ls $ line | wc -l et vous évitez le problème de tri ls.
chris

Je viens de lancer un test sur un répertoire contenant un million de fichiers et ls a pris 22 secondes et echo * 12 secondes. (Pour mémoire, l'écho * dans le shell n'atteindra pas la limite d'arg, car l'écho dans 99% des shells en utilisation active est intégré)
Chris

ls -f ne triera pas les résultats. Le tri des résultats du répertoire entraîne un problème courant avec NFS et les grands répertoires. Si le temps de lecture et de tri du répertoire (sur le serveur) dépasse le délai NFS, le répertoire et les sous-répertoires sont inutilisables.
mpez0

5

Vous pouvez utiliser ce petit extrait:

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

Il affichera le nombre de fichiers et de répertoires dans chacun des répertoires du dossier actuel, avec les plus grands délinquants en bas. Il vous aidera à trouver des répertoires contenant de nombreux fichiers. ( plus d'infos )


Cela a fonctionné avec brio.
ptman

3

Ce n'est pas une réponse directe à votre question, mais la recherche de fichiers récemment modifiés avec une petite taille en utilisant find peut affiner votre recherche:

find / -mmin -10 -size -20k

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

ls ne trouvera pas les fichiers dont les noms commencent par un point. L'utilisation de find évite cela. Cela trouve chaque fichier dans l'arborescence de répertoires, supprime le nom de base à la fin de chaque chemin et compte le nombre de fois que chaque chemin de répertoire apparaît dans la sortie résultante. Vous devrez peut-être mettre le "!" entre guillemets si votre shell s'en plaint.

Les inodes peuvent également être utilisés par des fichiers qui ont été supprimés mais qui sont maintenus ouverts par un processus en cours. Si ce paquet Munin comprend des programmes en cours d'exécution, une autre chose à vérifier est de savoir s'il contient un nombre inhabituel de fichiers.


Les inodes pourraient également être récupérés par des répertoires très profonds, que cela ne trouverait pas. Il y a un certain nombre de cas de bord étranges dans cela, mais la situation la plus courante est un répertoire plein de fichiers avec des noms normaux.
chris

3

Je forcerais celui-ci: exécutez tripwire sur l'ensemble de l'appareil pour une ligne de base, puis exécutez une vérification quelque temps plus tard et le répertoire incriminé ressortira comme un pouce endolori.


Cela prendrait probablement un milliard d'années. Une chose plus rapide à faire est d'exécuter lsof | grep DIR et recherchez dans chacun de ces répertoires de nombreux nouveaux fichiers.
chris

2
Ok, que diriez-vous de ceci: trouver / | sort> /tmp/find1.txt; trouver / | sort> /tmp/find2.txt; diff /tmp/find1.txt /tmp/find2.txt
Geoff Fritz

2

(ne pas pouvoir commenter devient vraiment vieux - c'est pour l'égorgie)

egorgry - ls -i affiche l'inode NUMBER pour une entrée, pas l'inode COUNT.

Essayez-le avec un fichier dans votre répertoire - vous verrez (probablement) un nombre tout aussi élevé, mais ce n'est pas le nombre d'inodes, c'est juste l'inode # vers lequel pointe votre entrée de répertoire.


lol. Je vous ai voté un. Merci pour l'explication. l'utilisation des inodes a toujours été source de confusion.
egorgry

merci Maintenant, j'ai peur de le convertir en commentaire sur votre nœud, au cas où je perdrais le karma quand je supprimerais cette réponse :)
Mike G.

2

Mise à jour

Un liner qui renvoie le nombre d'inodes de chaque enfant d'un répertoire donné avec les entrées les plus importantes en bas.

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

Réponse 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

Exécutez-le comme ceci (étant donné que le script ci-dessus réside dans un fichier exécutable dans votre répertoire de travail)

./indist / | sort -n

1

l'utilisation d'inode est d'environ une par fichier ou répertoire, non? Alors faites

find [path] -print | wc -l

pour compter approximativement le nombre d'inodes utilisés sous [chemin].


1

J'ai essayé d'écrire un pipeline shell efficace, mais il est devenu lourd et lent ou inexact, par exemple,

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

répertorie les répertoires feuilles (et certains autres) contenant plus de 1 000 fichiers. Voici donc un script Perl pour le faire efficacement en temps et en RAM. La sortie est comme

«Fichiers-dans-sous-arbre» ​​«fichiers-directement-dans-répertoire» «nom-répertoire»

vous pouvez donc le masser et le filtrer facilement à l'aide d'outils normaux, par exemple, tri (1) ou awk (1) comme ci-dessus.

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

ma maison sur mon ordinateur portable utilise des inodes 131191.


3
ls -i imprime l'inode NUMBER pour une entrée, pas l'inode COUNT. Essayez-le avec un fichier dans votre répertoire - vous verrez (probablement) un nombre tout aussi élevé, mais ce n'est pas le nombre d'inodes, c'est juste l'inode # vers lequel pointe votre entrée de répertoire.
egorgry
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.