概要
too many open filesエラーが出たときにlsofコマンドで確認するときの手順を説明する機会があったのでまとめる。
環境
node -v v16.16.0 docker -v Docker version 20.10.17, build 100c701
用意するもの
Dockerfile file.txt index.js
Dockerfile
FROM node:16.16.0-bullseye-slim COPY . . RUN apt update && apt install -y lsof procps linux-perf CMD ["node","index.js"]
サンプルプログラム index.js
const fs = require('fs'); const openFileAndDontClose = () => { fs.open('file.txt', 'r', (err, fd) => { console.log(`fd: ${fd}`) console.log(`err: ${err}`) }); } setInterval(openFileAndDontClose, 1000)
1秒ごとにfile.txtを開き続ける。
検証
docker buildx build -t test . docker run --name test test docker exec -it test /bin/bash
nodeプロセスのPIDを確認
ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 4100 3340 pts/0 Ss 13:41 0:00 /bin/bash root 10 0.1 0.4 352596 33644 pts/0 Sl+ 13:41 0:00 node index.js root 21 0.0 0.0 4100 3520 pts/1 Ss 13:41 0:00 /bin/bash root 34 0.0 0.0 6700 3012 pts/1 R+ 13:42 0:00 ps aux
ulimit -Sn 1048576 ulimit -Hn 1048576
ファイルディスクリプタの確認
/proc/PID/fd
ls -l /proc/1/fd total 0 lrwx------ 1 root root 64 Jul 16 14:00 0 -> /dev/null l-wx------ 1 root root 64 Jul 16 14:00 1 -> 'pipe:[56376]' lr-x------ 1 root root 64 Jul 16 14:00 10 -> 'pipe:[55593]' l-wx------ 1 root root 64 Jul 16 14:00 11 -> 'pipe:[55593]' lrwx------ 1 root root 64 Jul 16 14:00 12 -> 'anon_inode:[eventfd]' lrwx------ 1 root root 64 Jul 16 14:00 13 -> 'anon_inode:[eventpoll]' lr-x------ 1 root root 64 Jul 16 14:00 14 -> 'pipe:[50868]' l-wx------ 1 root root 64 Jul 16 14:00 15 -> 'pipe:[50868]' lrwx------ 1 root root 64 Jul 16 14:00 16 -> 'anon_inode:[eventfd]' lr-x------ 1 root root 64 Jul 16 14:00 17 -> /file.txt lr-x------ 1 root root 64 Jul 16 14:00 18 -> /dev/null lr-x------ 1 root root 64 Jul 16 14:00 19 -> /file.txt l-wx------ 1 root root 64 Jul 16 14:00 2 -> 'pipe:[56377]' lr-x------ 1 root root 64 Jul 16 14:02 20 -> /file.txt lr-x------ 1 root root 64 Jul 16 14:02 21 -> /file.txt lr-x------ 1 root root 64 Jul 16 14:02 22 -> /file.txt lr-x------ 1 root root 64 Jul 16 14:02 23 -> /file.txt ...
23 -> /file.txt
fdの番号 -> 開いている対象
lsofコマンド
lsof -p PID # もしくは # lsof -c プロセス名
lsof -p 1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 1 root cwd DIR 0,142 4096 270825 / node 1 root rtd DIR 0,142 4096 270825 / node 1 root txt REG 0,142 81180048 2890255 /usr/local/bin/node node 1 root mem REG 254,1 2890255 /usr/local/bin/node (path dev=0,142) node 1 root mem REG 254,1 2885690 /lib/x86_64-linux-gnu/libc-2.31.so (path dev=0,142) node 1 root mem REG 254,1 2885735 /lib/x86_64-linux-gnu/libpthread-2.31.so (path dev=0,142) node 1 root mem REG 254,1 2885704 /lib/x86_64-linux-gnu/libgcc_s.so.1 (path dev=0,142) node 1 root mem REG 254,1 2885711 /lib/x86_64-linux-gnu/libm-2.31.so (path dev=0,142) node 1 root mem REG 254,1 2886466 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28 (path dev=0,142) node 1 root mem REG 254,1 2885698 /lib/x86_64-linux-gnu/libdl-2.31.so (path dev=0,142) node 1 root mem REG 254,1 2885678 /lib/x86_64-linux-gnu/ld-2.31.so (path dev=0,142) node 1 root 0u CHR 1,3 0t0 5 /dev/null node 1 root 1w FIFO 0,11 0t0 56376 pipe node 1 root 2w FIFO 0,11 0t0 56377 pipe node 1 root 3u a_inode 0,12 0 12340 [eventpoll] node 1 root 4r FIFO 0,11 0t0 50866 pipe node 1 root 5w FIFO 0,11 0t0 50866 pipe node 1 root 6r FIFO 0,11 0t0 50867 pipe node 1 root 7w FIFO 0,11 0t0 50867 pipe node 1 root 8u a_inode 0,12 0 12340 [eventfd] node 1 root 9u a_inode 0,12 0 12340 [eventpoll] node 1 root 10r FIFO 0,11 0t0 55593 pipe node 1 root 11w FIFO 0,11 0t0 55593 pipe node 1 root 12u a_inode 0,12 0 12340 [eventfd] node 1 root 13u a_inode 0,12 0 12340 [eventpoll] node 1 root 14r FIFO 0,11 0t0 50868 pipe node 1 root 15w FIFO 0,11 0t0 50868 pipe node 1 root 16u a_inode 0,12 0 12340 [eventfd] node 1 root 17r REG 0,142 0 3807111 /file.txt node 1 root 18r CHR 1,3 0t0 5 /dev/null node 1 root 19r REG 0,142 0 3807111 /file.txt node 1 root 20r REG 0,142 0 3807111 /file.txt node 1 root 21r REG 0,142 0 3807111 /file.txt ...
lsof -p 1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ... node 1 root 20r REG 0,142 0 3807111 /file.txt
- FDは20番をreadで開いているという意味
- TYPEのREGはregular file
- NAMEは開いている対象
今回のプログラムはfile.txtを開き続けるので、file.txtが多く表示される。
総数だけ見たいときは下記
lsof -p 1 | wc -l
ulimitを変更する
docker run --name test --ulimit nofile=100:100 test
ulimit -Sn 100 ulimit -Hn 100
上限を30にすると29まで使った次に、too many open files
のエラーが出ることを確認
docker run --name test --ulimit nofile=30:30 test fd: 17 err: null fd: 19 err: null fd: 20 err: null fd: 21 err: null fd: 22 err: null fd: 23 err: null fd: 24 err: null fd: 25 err: null fd: 26 err: null fd: 27 err: null fd: 28 err: null fd: 29 err: null fd: undefined err: Error: EMFILE: too many open files, open 'file.txt'
まとめ
too many open files
のエラーが出たとき、どのプロセスが、なんのファイルを開こうとしているのか確認する手順は下記
ps aux
でプロセス番号確認lsof -p PID
でファイルの上表を表示- NAME欄で多く表示されているものを確認する
対応として、
- プログラムで不具合はないか確認
- ulimitで開くことのできるファイルの上限を上げる
参考情報
man lsof ... FD is followed by one of these characters, describing the mode under which the file is open: r for read access; w for write access; u for read and write access; space if mode unknown and no lock character follows; `-' if mode unknown and lock character follows. ...
man lsof ... TYPE is the type of the node associated with the file - e.g., GDIR, GREG, VDIR, VREG, etc... ... or ``REG'' for a regular file; ...
https://nodejs.org/dist/latest-v16.x/docs/api/fs.html#fsopenpath-flags-mode-callback