エラーの概要
このエラーは通常、トラフィックが急増した際に発生します。Nginx サーバーが接続を拒否したり、Java のマイクロサービスが突然クラッシュしたりし、ログには Too many open files という簡潔なメッセージが表示されます。これは、プロセスがオペレーティングシステムで許可されている以上のリソースを開こうとしたために発生します。Linux では、ネットワークソケット、データベース接続、パイプを含むほぼすべての I/O リソースを「ファイル」として扱います。これらはそれぞれ「ファイル記述子 (File Descriptor, FD)」を消費します。
Too many open files
発生原因
Linux は、バグのある単一のプロセスがシステムメモリをすべて使い果たすのを防ぐために、安全制限を設けています。デフォルトでは、多くのディストリビューションで 1 プロセスあたりわずか 1,024 ファイルに制限されています。これは現代のウェブサーバーやデータベースにとっては少なすぎます。これらの制限には 2 つのレイヤーがあります。
- ソフトリミット: カーネルによって適用される現在の制限値です。プロセスは自身のソフトリミットをハードリミットの値まで引き上げることができます。
- ハードリミット: 物理的な上限として機能します。root 権限を持つユーザーのみがハードリミットを引き上げることができます。
ステップ 1: 現在の制限値を確認する
まず、システムで現在許可されている値を確認します。アプリケーションを実行している特定のユーザー(例: www-data や mysql)として以下のコマンドを実行してください。
# ソフトリミットを表示
ulimit -Sn
# ハードリミットを表示
ulimit -Hn
すべてのプロセスを通じてオペレーティングシステム全体で処理できる最大ファイル数を確認するには、以下を実行します。
cat /proc/sys/fs/file-max
ステップ 2: ユーザーごとの制限値を引き上げる(恒久設定)
アプリケーションを手動または cron で実行している場合は、/etc/security/limits.conf ファイルを編集する必要があります。この変更は恒久的で、システムの再起動後も有効です。
sudo nano /etc/security/limits.conf
ファイルの末尾に以下の行を追加します。ほとんどのプロダクション環境では 65535 を推奨します。変更を特定のユーザーに限定したい場合は、* を特定のユーザー名に置き換えてください。
* soft nofile 65535
* hard nofile 65535
重要: これらの変更をユーザーセッションに反映させるには、一度ログアウトしてから再度ログインする必要があります。
ステップ 3: Systemd サービスの修正
limits.conf のような標準設定ファイルは、systemd で管理されているサービス(Nginx、MySQL、Node.js アプリなど)には適用されないことがよくあります。これらの場合は、サービス固有の設定で制限を指定する必要があります。
特定のサービス用の上書きファイルを作成します。
sudo systemctl edit myservice.service
エディタで以下の行を挿入します。
[Service]
LimitNOFILE=65535
ファイルを保存して終了します。次に、systemd に設定の再読み込みとアプリケーションの再起動を指示します。
sudo systemctl daemon-reload
sudo systemctl restart myservice.service
ステップ 4: システム全体の制限値を引き上げる
数百万のリクエストを処理する高並列サーバーでは、カーネルのグローバル制限に達することがあります。/proc/sys/fs/file-max の値が低い場合は、/etc/sysctl.conf を編集して引き上げることができます。
sudo nano /etc/sysctl.conf
200万個のファイルオープンを許可するために、以下のパラメータを追加または更新します。
fs.file-max = 2097152
再起動せずに新しいカーネル設定を即座に適用します。
sudo sysctl -p
修正の確認方法
設定変更が機能したか確認せずに放置しないでください。アプリケーションのプロセス ID (PID) を特定し、実際の実行時の制限値を検査します。
# PID を特定する (例: Nginx の場合)
pgrep nginx
# 特定の PID の制限値を確認する
cat /proc/<PID>/limits | grep "Max open files"
出力結果に新しい制限値(例: 65535)が反映されているはずです。
トラブルシューティングのヒント
ファイル記述子のリークを特定する
制限値を高くしたにもかかわらず数日後にエラーが再発する場合、コード内でリークが発生している可能性があります。これは、アプリがデータベース接続やファイルを開いたものの、閉じ忘れている場合に起こります。lsof を使用して、プロセスが何を保持しているか調査します。
# PID が現在開いているファイル数を正確にカウントする
sudo lsof -p <PID> | wc -l
# 保持されている特定のファイルやソケットを確認する
sudo lsof -p <PID>
PAM の要件
limits.conf の変更が反映されない場合は、システムが実際に制限モジュールを読み込んでいるか確認してください。/etc/pam.d/common-session を確認し、以下の行がコメントアウトされていないことを確認します。
session required pam_limits.so

