概要
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 -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