"Too many open files" の対処 - ファイルディスクリプタ枯渇
この記事で解決できること
Too many open filesエラーの原因(プロセス上限 vs システム上限)が分かるulimitとlsofで 現在の FD 使用状況 をすぐ確認できるlimits.conf/sysctl.conf/ systemd オーバーライドで 恒久的に上限を上げる 方法が分かる
結論(最短 3 ステップ)
ulimit -nまたは/proc/<pid>/limitsで どの上限に当たっているか を確認lsof -p <pid> | wc -lで プロセスが実際に開いている FD 数 を確認- 原因に応じて
/etc/security/limits.conf(PAM 経由プロセス)またはLimitNOFILE=(systemd サービス)で恒久設定
前提(対象環境)
- OS:Ubuntu / Debian / RHEL 系
- 権限:
sudoが使える前提 - systemd ベースのサービス管理前提
「Too many open files」とはどういうエラーか
ファイルディスクリプタ(FD)は、Linux がファイル・ソケット・パイプなどを管理するための 整数の識別子。プロセスはファイルを開くたびに FD を 1 つ消費する。
Linux には 2 種類の FD 上限がある。
- プロセス上限(EMFILE): 1 プロセスが同時に開ける FD の数。
ulimit -nで確認できる - システム上限(ENFILE): OS 全体で開ける FD の総数。
/proc/sys/fs/file-maxで確認できる
多くの場合、エラーは プロセス上限への到達(デフォルト 1024)が原因。高トラフィックな Web サーバ・データベース・Node.js アプリは 1024 では不足することが多い。
典型症状
- Nginx / Apache / MySQL / Node.js が負荷時に突然エラーを吐く
- ログに
Too many open files (EMFILE)やaccept4: Too many open filesが出る ulimit -nの値(デフォルト 1024)付近で接続数が詰まる
1. FD 使用状況を確認する
1-1. 現在のシェル・プロセスの上限を確認
$ ulimit -n # ソフト上限(実効値) $ ulimit -Hn # ハード上限(プロセスが自分で引き上げられる上限)
1024 1048576
ソフト上限(1024)が実効値。プロセス自身がハード上限まで引き上げることができる。
1-2. 特定プロセスの上限と使用数を確認
$ cat /proc/<pid>/limits | grep 'open files'
Limit Soft Limit Hard Limit Units Max open files 65536 65536 files
実際に開いている FD 数を確認する:
$ ls /proc/<pid>/fd | wc -l # または $ sudo lsof -p <pid> | wc -l
1-3. システム全体の FD 使用状況
$ cat /proc/sys/fs/file-nr
13472 0 524288
3 列の意味:現在の使用数 / 解放予約済(常に 0) / システム上限。
システム上限値の確認:
$ sysctl fs.file-max fs.file-max = 524288
プロセス上限(ulimit -n)がシステム上限(fs.file-max)より先に詰まるのが通常。システム上限に達することは稀。
2. 原因プロセスを特定する
2-1. FD 使用数の多いプロセスをランキング
$ sudo lsof 2>/dev/null | awk '{print $1, $2}' | sort | uniq -c | sort -rn | head -204821 nginx 1234 3102 mysqld 5678 1204 node 9012
2-2. 特定プロセスが何を開いているか確認
$ sudo lsof -p <pid>
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 1234 www 0r REG 8,1 1234 /var/log/nginx/access.log nginx 1234 www 3u IPv4 56789 0t0 TCP *:80 (LISTEN)
ソケットが大量にある場合、コネクションリーク(close し忘れ)が原因である可能性が高い。
2-3. ソケット状態の確認
$ ss -s
Total: 12034 TCP: 11820 (estab 8000, closed 200, orphaned 20, timewait 200)
estab(確立済み)が異常に多い場合、アプリ側の接続管理を見直す。
3. 一時的な上限引き上げ(応急処置)
現在のシェルセッションのみに有効な一時設定。ログアウト後は元に戻る。
$ ulimit -n 65536
既に起動しているプロセスの上限を変えたい場合は prlimit を使う:
$ sudo prlimit --pid <pid> --nofile=65536:65536
ulimit の変更はそのシェルセッションのみ有効。恒久設定は次セクションを参照。
4. 恒久的な設定変更
4-1. PAM 経由プロセス(ログインセッション・一般デーモン)
/etc/security/limits.conf または /etc/security/limits.d/*.conf に追記する。
$ sudo vi /etc/security/limits.d/99-fd-limits.conf
# * は全ユーザー対象。特定ユーザーの場合はユーザー名を指定 * soft nofile 65536 * hard nofile 65536 # 特定ユーザーのみ変える場合 www-data soft nofile 65536 www-data hard nofile 65536
設定を反映するには 再ログイン(またはサービス再起動)が必要。確認:
$ su - www-data -s /bin/bash -c 'ulimit -n' 65536
Ubuntu 18.04 以降は /etc/systemd/system.conf の DefaultLimitNOFILE= でシステムワイドなデフォルトも変更できる。ただし全サービスに影響するため、サービス単位の設定(4-3)を優先する。
4-2. システム全体の上限(fs.file-max)を上げる
通常はプロセス上限の対処で十分。システム上限に達した場合のみ変更する。
$ sudo sysctl -w fs.file-max=1048576 # 即時反映(再起動で消える)
永続化するには /etc/sysctl.d/99-fd.conf を作成する:
$ sudo vi /etc/sysctl.d/99-fd.conf
fs.file-max = 1048576
$ sudo sysctl --system # 全 sysctl.d/*.conf を再読み込み
4-3. systemd 管理サービスの場合
systemd は PAM を経由しないため limits.conf は無効。サービス単位でオーバーライドを作る。
$ sudo systemctl edit nginx # エディタが開く
以下を追記して保存する:
[Service]
LimitNOFILE=65536
または手動でファイルを作成する:
$ sudo mkdir -p /etc/systemd/system/nginx.service.d/ $ sudo vi /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
$ sudo systemctl daemon-reload $ sudo systemctl restart nginx
設定が反映されているか確認する:
$ cat /proc/$(pgrep -o nginx)/limits | grep 'open files' Max open files 65536 65536 files
LimitNOFILE=infinity はカーネル 5.15 以降で使用可能。それより古い環境では数値で指定する(65536 または 1048576)。
5. やってはいけないこと
ulimit -n unlimitedを恒久設定する: プロセスがバグで FD をリークした場合にシステム全体が詰まる。適切な上限(65536〜1048576)を設定する- 原因を調べずに上限だけ上げる: FD リーク(接続を閉じ忘れる実装バグ)が原因の場合、上限を上げても一時しのぎにしかならない
limits.confを書いて systemd サービスに適用されると思い込む: systemd 管理下のサービスは PAM を経由しないためlimits.confは無効fs.file-maxを不必要に大きくする: カーネルが FD ごとにメモリを確保するため、無制限に上げるとメモリを圧迫する