web-dev-qa-db-ja.com

誰が私のinotifyリソースを消費していますか?

最近Fedora 15にアップグレードした後、次のようなエラーが発生して多くのツールが失敗することがわかりました。

tail: inotify resources exhausted
tail: inotify cannot be used, reverting to polling

Inotifyの問題を報告しているのはtailだけではありません。カーネルに問い合わせて、どのプロセスがinotifyリソースを消費しているかを調べる方法はありますか?現在のinotify関連のsysctl設定は次のようになります。

fs.inotify.max_user_instances = 128
fs.inotify.max_user_watches = 8192
fs.inotify.max_queued_events = 16384
51
larsks

プロセスがinotify_init()を介してinotifyインスタンスを作成する場合、/ procファイルシステムのfiledescriptorを表す結果のファイルは(存在しない)「anon_inode:inotify」ファイルへのシンボリックリンクであるようです。

$ cd /proc/5317/fd
$ ls -l
total 0
lrwx------ 1 puzel users 64 Jun 24 10:36 0 -> /dev/pts/25
lrwx------ 1 puzel users 64 Jun 24 10:36 1 -> /dev/pts/25
lrwx------ 1 puzel users 64 Jun 24 10:36 2 -> /dev/pts/25
lr-x------ 1 puzel users 64 Jun 24 10:36 3 -> anon_inode:inotify
lr-x------ 1 puzel users 64 Jun 24 10:36 4 -> anon_inode:inotify

概念を誤解していない限り、次のコマンドを実行すると、プロセスのリスト(/ procでの表現)が、使用するinotifyインスタンスの数でソートされて表示されます。

$ for foo in /proc/*/fd/*; do readlink -f $foo; done | grep inotify | sort | uniq -c | sort -nr

犯人を見つける

以下のコメントを介して@markkcowanはこれについて言及しました:

$ find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
40
Petr Uzel

インスタンスではなく、おそらくinotifywatchesが不足しています。誰が多くの時計を作成しているのかを知るには:

  1. ウォッチの追加のトレースを有効にする:
$ echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enable`
  1. tracing_onがsから1であるかどうかを確認します。
$ cat /sys/kernel/debug/tracing/tracing_on
0
$ echo 1 > /sys/kernel/debug/tracing/tracing_on
  1. 多くのウォッチを作成している疑いのあるinotifyインスタンス(Petr Uzelの回答で説明されているように決定)でプロセスを再起動します。そして
  2. Ftraceのセットアップ
$ cat /sys/kernel/debug/tracing/current_tracer
nop

$ cat /sys/kernel/debug/tracing/set_ftrace_filter
#### all functions enabled ####

$ echo function              > /sys/kernel/debug/tracing/current_tracer
$ echo SyS_inotify_add_watch > /sys/kernel/debug/tracing/set_ftrace_filter
  1. ファイル/sys/kernel/debug/tracing/traceを読んで、作成されたウォッチの数と作成されたプロセスを確認します。

完了したら、必ずecho 0を有効化ファイル(およびそれを有効化する必要がある場合はtracing_onファイル)に入れて、トレースをオフにしてください。これにより、トレースを続行してもパフォーマンスに影響が出ないようになります。

注: Linuxカーネルの古いバージョンでは、/sysエンドポイントはtracing_enabledと呼ばれていましたが、現在はtracing_onと呼ばれています。カーネルの古いエディションを使用している場合は、/sys/kernel/debug/tracing/tracing_on/sys/kernel/debug/tracing/tracing_enabledに変更してください。

27
Jonathan Kamens

@Jonathan Kamensが言ったように、おそらくウォッチャーが不足しています。私は 既製のスクリプトinotify-consumersを持っています。

$ time inotify-consumers  | head

   INOTIFY
   WATCHER
    COUNT     PID     CMD
----------------------------------------
    6688    27262  /home/dvlpr/apps/WebStorm-2018.3.4/WebStorm-183.5429.34/bin/fsnotifier64
     411    27581  node /home/dvlpr/dev/kiwi-frontend/node_modules/.bin/webpack --config config/webpack.dev.js
      79     1541  /usr/lib/gnome-settings-daemon/gsd-xsettings
      30     1664  /usr/lib/gvfs/gvfsd-trash --spawner :1.22 /org/gtk/gvfs/exec_spaw/0
      14     1630  /usr/bin/gnome-software --gapplication-service

real    0m0.099s
user    0m0.042s
sys 0m0.062s

ここでは、開発マシンでデフォルトの8Kウォッチャーの制限が小さすぎる理由をすばやく確認できます。何千ものフォルダーがあるnode_modulesフォルダーに遭遇すると、WebStormインスタンスだけがこれをすぐに最大化するためです。問題を保証するためにwebpackウォッチャーを追加します...

スクリプトの内容(またはGitHub上のファイル)をコピーして、$PATHのように/usr/local/binのどこかに配置します。参考までに、スクリプトの主な内容はこれだけです

find /proc/*/fd \
    -lname anon_inode:inotify \
    -printf '%hinfo/%f\n' 2>/dev/null \
    \
    | xargs grep -c '^inotify'  \
    | sort -n -t: -k2 -r 

制限を増やす方法について疑問がある場合は、永続化する方法を次に示します。

echo fs.inotify.max_user_watches=524288 | Sudo tee -a /etc/sysctl.conf
Sudo sysctl -p
12
oligofren

どのプロセスがinotify watches(インスタンスではない)を消費するかを追跡するには、カーネルで動的ftrace機能が有効になっている場合は、それを使用できます。

必要なカーネルオプションはCONFIG_DYNAMIC_FTRACE

Debugfsファイルシステムがまだマウントされていない場合は、最初にマウントします。

mount -t debugfs nodev /sys/kernel/debug

このdebugfsディレクトリのtracingサブディレクトリの下に移動します

cd /sys/kernel/debug/tracing

関数呼び出しのトレースを有効にする

echo function > current_tracer

フィルターのみSyS_inotify_add_watchシステムコール

echo SyS_inotify_add_watch > set_ftrace_filter

トレースリングバッファが空でなければクリアする

echo > trace

まだ有効になっていない場合は、トレースを有効にします

echo 1 > tracing_on

疑わしいプロセスを再起動します(私の場合、クラッシュプラン、バックアップアプリケーションでした)

Inotify_watchが使い果たされるのを見る

wc -l trace
cat trace

できた

6
silvergun
find /proc/*/fd/* -type l -lname 'anon_inode:inotify' 2>/dev/null | cut -f 1-4 -d'/' |  sort | uniq -c  | sort -nr
3
Paul

上記のスクリプトを変更して、それらが消費しているプロセスのリストを表示しましたinotifyリソース:

ps -p `find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print | sed s/'^\/proc\/'/''/ | sed s/'\/fd.*$'/''/`

私のダブルsedを置き換える方法があると思います。


cut -f 3 -d '/'   

または

sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1'  

そして、あなただけのpidを取得します。
また、追加した場合

2> /dev/null  

findでは、findによってスローされた厄介なエラー行をすべて削除します。だからこれはうまくいくでしょう:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
1
Arkadij Kuzhel

このスクリプトを一連のサーバーに対して実行する必要があったため、これを実現するためにAnsibleプレイブックを作成しました。それはいくつかの 他の答えの概念 を単一のプレイブックに適合させ、PIDによるinotifyウォッチャーの使用を示すレポートを生成するために必要なコマンドを実行します。

$ ansible-playbook \
    -i systems-inventory/cluster2.lab1 \
    playbooks/show_inotify_watcher_cnt.yml \
    -l ocp-master-02a.lab1*

注:このプレイブックの使用例は、以下のプレイブックの終わりに向けてのコメントです。

$ cat show_inotify_watcher_cnt.yml
###########################################################
# References
###########################################################
# - https://stackoverflow.com/questions/40230184/how-to-do-multiline-Shell-script-in-ansible
###########################################################


- hosts: compute infra masters
  tasks:
    - Shell:
        cmd: |
            cat <<EOF > /tmp/inotify_cnt.sh
            #!/bin/bash

            ## Get the procs sorted by the number of inotify watchers
            ##
            ## From \`man find\`: 
            ##    %h     Leading directories of file's name (all but the last element).  
            ##           If the file name contains no slashes (since it is in the current directory) 
            ##           the %h specifier expands to \`.'.
            ##    %f     File's name with any leading directories removed (only the last element).
            lines=\$(
                find /proc/*/fd \
                -lname anon_inode:inotify \
                -printf '%hinfo/%f\n' 2>/dev/null \
                \
                | xargs grep -c '^inotify'  \
                | sort -n -t: -k2 -r \
                )

            printf "\n%10s\n" "INOTIFY"
            printf "%10s\n" "WATCHER"
            printf "%10s  %5s     %s\n" " COUNT " "PID" "CMD"
            printf -- "----------------------------------------\n"
            for line in \$lines; do
                watcher_count=\$(echo \$line | sed -e 's/.*://')
                pid=\$(echo \$line | sed -e 's/\/proc\/\([0-9]*\)\/.*/\1/')
                cmdline=\$(ps --columns 120 -o command -h -p \$pid) 
                printf "%8d  %7d  %s\n" "\$watcher_count" "\$pid" "\$cmdline"
            done
            EOF
      become: yes

    - file: 
        dest: /tmp/inotify_cnt.sh
        mode: a+x

    - Shell: /tmp/inotify_cnt.sh
      become: yes
      register: output

    - debug:
        var: output.stdout_lines

    - Shell: |
        sysctl fs.inotify
      become: yes
      register: output

    - debug:
        var: output.stdout_lines


##########
# USAGE
##########
## $ ansible-playbook -i systems-inventory/cluster2.lab1 playbooks/show_inotify_watcher_cnt.yml -l ocp-master-02a.lab1*
##   
##   PLAY [compute infra masters] *****************************************************************************************************************************************************
##   
##   TASK [Gathering Facts] ***********************************************************************************************************************************************************
##   ok: [ocp-master-02a.lab1.mydomclec.local]
##   
##   TASK [Shell] *********************************************************************************************************************************************************************
##   changed: [ocp-master-02a.lab1.mydomclec.local]
##   
##   TASK [file] **********************************************************************************************************************************************************************
##   ok: [ocp-master-02a.lab1.mydomclec.local]
##   
##   TASK [Shell] *********************************************************************************************************************************************************************
##   changed: [ocp-master-02a.lab1.mydomclec.local]
##   
##   TASK [debug] *********************************************************************************************************************************************************************
##   ok: [ocp-master-02a.lab1.mydomclec.local] => {
##       "output.stdout_lines": [
##           "",
##           "   INOTIFY",
##           "   WATCHER",
##           "    COUNT     PID     CMD",
##           "----------------------------------------",
##           "     957     6553  /usr/bin/hyperkube kubelet --v=2 --address=0.0.0.0 --allow-privileged=true --anonymous-auth=true --authentication-token-",
##           "      11      856  /usr/lib/systemd/systemd-udevd",
##           "      11     1457  /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid",
##           "      10     1471  /usr/sbin/rpc.gssd",
##           "       5        1  /usr/lib/systemd/systemd --switched-root --system --deserialize 22",
##           "       5        1  /usr/lib/systemd/systemd --switched-root --system --deserialize 22",
##           "       5     1508  /usr/sbin/NetworkManager --no-daemon",
##           "       4        1  /usr/lib/systemd/systemd --switched-root --system --deserialize 22",
##           "       4        1  /usr/lib/systemd/systemd --switched-root --system --deserialize 22",
##           "       4     1508  /usr/sbin/NetworkManager --no-daemon",
##           "       4     1378  /usr/lib/polkit-1/polkitd --no-debug",
##           "       3     4211  tail --follow=name /var/log/openvswitch/ovs-vswitchd.log /var/log/openvswitch/ovsdb-server.log",
##           "       3     1970  /usr/sbin/crond -n",
##           "       3     1378  /usr/lib/polkit-1/polkitd --no-debug",
##           "       2     1893  /usr/sbin/rsyslogd -n",
##           "       2     1389  /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation",
##           "       1     9012  virt-handler -v 3 --port 8443 --hostname-override ocp-master-02a.lab1.mydomclec.local --pod-ip-address 172.20.0.201",
##           "       1     9012  virt-handler -v 3 --port 8443 --hostname-override ocp-master-02a.lab1.mydomclec.local --pod-ip-address 172.20.0.201",
##           "       1     9012  virt-handler -v 3 --port 8443 --hostname-override ocp-master-02a.lab1.mydomclec.local --pod-ip-address 172.20.0.201",
##           "       1     9012  virt-handler -v 3 --port 8443 --hostname-override ocp-master-02a.lab1.mydomclec.local --pod-ip-address 172.20.0.201",
##           "       1     6553  /usr/bin/hyperkube kubelet --v=2 --address=0.0.0.0 --allow-privileged=true --anonymous-auth=true --authentication-token-",
##           "       1     6553  /usr/bin/hyperkube kubelet --v=2 --address=0.0.0.0 --allow-privileged=true --anonymous-auth=true --authentication-token-",
##           "       1        1  /usr/lib/systemd/systemd --switched-root --system --deserialize 22",
##           "       1     1388  /usr/sbin/sssd -i --logger=files",
##           "       1     1359  /usr/bin/abrt-watch-log -F BUG: WARNING: at WARNING: CPU: INFO: possible recursive locking detected ernel BUG at list_de",
##           "       1     1347  /usr/sbin/abrtd -d -s",
##           "       0     6553  /usr/bin/hyperkube kubelet --v=2 --address=0.0.0.0 --allow-privileged=true --anonymous-auth=true --authentication-token-"
##       ]
##   }
##   
##   TASK [Shell] *********************************************************************************************************************************************************************
##   changed: [ocp-master-02a.lab1.mydomclec.local]
##   
##   TASK [debug] *********************************************************************************************************************************************************************
##   ok: [ocp-master-02a.lab1.mydomclec.local] => {
##       "output.stdout_lines": [
##           "fs.inotify.max_queued_events = 16384",
##           "fs.inotify.max_user_instances = 128",
##           "fs.inotify.max_user_watches = 65536"
##       ]
##   }
##   
##   PLAY RECAP ***********************************************************************************************************************************************************************
##   ocp-master-02a.lab1.mydomclec.local : ok=7    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
##   
0
slm