プロンプトと対象となるロール
LinuxのAPIサービスが安定したトラフィック下で数時間稼働した後、新しい接続の拒絶を開始します。アップストリームサービスへの呼び出しやログファイルを開く試みも失敗し始めます。以下の情報が分かっています。
- サービスプロセスのソフト
RLIMIT_NOFILEは 8,192、ハード制限は 65,536 である。 - 起動直後は約 400 個のオープンなファイルディスクリプタを持ち、その後毎分約 25 個のペースで増加する。
- 障害発生直前、
/proc/<pid>/fdには約 8,192 個のエントリが含まれ、ログにはEMFILE: Too many open filesが記録されている。 - プロセスを再起動するとサービスは直ちに復旧するが、増加傾向は再発する。
- ホストの
/proc/sys/fs/file-nrは/proc/sys/fs/file-maxを大幅に下回ったままである。
ファイルディスクリプタ、プロセスのファイルディスクリプタテーブル、およびオープンファイル記述(open file description)の関係を説明してください。通常ファイル、ソケット、パイプ、epoll がすべてディスクリプタを消費する理由を説明し、EMFILE と ENFILE を区別してください。次に、本番環境で安全な診断手順を示し、これがFDリークなのか、それともキャパシティを超える正当な並行性によるものなのかを判断し、緩和策と恒久的な修正案を提示し、障害が解決されたことをどのように証明するかを説明してください。
この質問は、バックエンド、SRE、インフラストラクチャ、システム、および一般的なソフトウェアエンジニアリングの面接に適しています。8,192、65,536、400、毎分25個という値は面接用の仮定値であり、普遍的な推奨値ではありません。実際の回答では、サービスマネージャー、コンテナランタイム、プロセスのパーミッション、およびアプリケーションの並行処理モデルも考慮する必要があります。
面接官が見ているポイント
第1に、候補者がファイルディスクリプタとはプロセス内の小さな整数インデックスであり、パス名やinode、カーネルオブジェクトそのものではないことを説明できるか?優れた回答は「プロセスのFDテーブル → オープンファイル記述 → ファイル、ソケット、パイプ、またはその他のオブジェクト」という連鎖を描き、dup で作成されたディスクリプタや fork を介して継承されたディスクリプタが同じオープンファイル記述を参照できることを理解しています。
第2に、制限の適用スコープを区別できるか?EMFILE は現在のプロセスが RLIMIT_NOFILE に達したことを意味し、ENFILE はシステム全体のオープンファイル制限に達したことを意味します。fs.file-max 単体でも、無関係なシェルでの ulimit -n でも、失敗しているプロセスの有効な制限を証明することはできません。
第3に、診断がカウント、構成内訳、およびトレンドをカバーしているか?1回限りの lsof スナップショットは一時点を示すに過ぎません。リークを立証するには、安定したトラフィック下で総数とオブジェクトタイプをサンプリングし、増加をコネクションプール、リクエストのライフサイクル、ログローテーション、子プロセス、および障害パスと関連付ける必要があります。
第4に、一時的なキャパシティ拡張と根本原因の修正を区別できるか?制限を引き上げることはインシデント対応の猶予時間を生み出したり、正当なキャパシティプランニングの一環になったりしますが、所有権が失われたリソースをクローズするわけではありません。一定の正の傾き(増加ペース)がある場合、引き上げた大きな制限もいずれ消費し尽くします。
最後に、検証ループが存在するか?「エラーが止まった」だけでは不十分です。優れた回答では、目標のピークトラフィックおよび障害注入下で、FD使用率と傾き、オブジェクトタイプ、リクエストエラー、テールレイテンシ、プールの状態、および繰り返されるライフサイクルイベントを検証します。
回答前に確認すべき質問
- 正確な errno は何か? 人間が読めるメッセージからスコープを推測するのではなく、アプリケーションまたはシステムコールのエラーから
EMFILEまたはENFILEを確認する。 - どの PID が失敗しているか? スーパーバイザー、ワーカー、サイドカー、短命な子プロセスは、異なる制限を継承し、異なるリソースを保持している可能性がある。
- 何がサービスを起動しているか? インタラクティブシェル、systemd、コンテナランタイム、プロセス管理ツールによって、設定されるソフト/ハード制限が異なる場合がある。シェルのコマンドで実行中のサービスを遡及的に変更することはできない。
- FDの増加に伴って何が変動しているか? 同時接続数、アップストリームリクエスト数、キューの深さ、ログローテーション、リロード、子プロセス数、エラー率と突き合わせる。
- どのオブジェクトタイプが増加しているか? ソケット、通常ファイル、パイプ、
anon_inode:[eventpoll]、inotifyオブジェクト、削除済みファイルは、それぞれ異なる所有権パスを示唆する。 - 正当なキャパシティモデルはどのようなものか? 高並行性のプロキシは、正当に多くのソケットを必要とする場合がある。カウントが大きいこと自体が自動的にリークを意味するわけではない。設定された並行性と一致し、負荷が下がったときに落ち着く必要がある。
/procを安全に調査できるか? 他のユーザーのディスクリプタを読み取ることは、パーミッションやptraceルールによって制限される場合がある。本番環境の調査では、必要最小限の権限を使用し、長時間にわたる高オーバーヘッドのトレーシングを避けるべきである。
30秒の簡潔な回答
「ファイルディスクリプタは、プロセスのFDテーブルにおける非負の整数インデックスです。テーブルエントリはシステム全体のオープンファイル記述(open file description)を参照し、これがファイルオフセットやステータスフラグを保持して、通常ファイル、ソケット、パイプ、その他のカーネルI/Oオブジェクトを指します。EMFILE はそのプロセスが RLIMIT_NOFILE に達したことを意味し、ENFILE はホストがシステム全体のオープンファイル制限に達したことを意味します。
失敗しているPIDとerrnoを確認し、/proc/<pid>/limits を読み取り、/proc/<pid>/fd をカウントして分類し、時間経過に伴うカテゴリをサンプリングして /proc/sys/fs/file-nr を確認します。安定したトラフィック下で一定の正の傾きを持ち、再起動でリセットされ、特定のオブジェクトタイプに集中している場合はリークを示します。並行性に追従し、定義されたプラトー(頭打ち)に達し、その後減少するカウントはキャパシティの圧迫を示します。レート制限、インスタンスのローリング再起動、キャパシティ検証後の実際のサービス制限引き上げで緩和できますが、恒久的な修正はリソースの所有権管理、失敗パスでのクリーンアップ、制限付きプール、適切な継承です。ピーク負荷時のFD使用率と傾き、オブジェクトの解放、関連エラーゼロによって修正を証明します。」
ステップごとの詳細解説
ステップ 1: 3層の参照モデルを構築する
成功すると、open() は非負の整数を返します。Linuxは通常、そのプロセスで現在使用されていない最も小さいディスクリプタ番号を選択します。慣例により、0、1、2 は標準入力、標準出力、標準エラー出力です。それ以降の番号も、単にそのプロセス内でのインデックスに過ぎません。
中核となる関係は以下のとおりです。
process FD-table entry → open file description → underlying object
各レイヤーは異なる状態を保持します。
| レイヤー | 保持するもの | 重要な特性 |
|---|---|---|
| プロセスFDテーブルエントリ | ディスクリプタ番号、および close-on-exec などのディスクリプタフラグ | 番号はそのプロセスのFDテーブルコンテキスト内でのみ意味を持つ |
| オープンファイル記述 (Open file description) | 現在のファイルオフセットとオープンファイルステータスフラグ | 複数のディスクリプタが共有できるシステム全体のオブジェクト |
| 基礎となるオブジェクト | inode、ソケット、パイプ、デバイス、または匿名のカーネルオブジェクト | 実際のI/O動作を定義する |
同一パスに対して独立して open() を2回呼び出すと、通常は別々のオープンファイル記述が作成されます。dup() によって返されたディスクリプタは同じオープンファイル記述を参照するため、両方のディスクリプタはオフセットとファイルステータスフラグを共有します。fork() の後、対応する親プロセスと子プロセスのディスクリプタも同じオープンファイル記述を参照します。close-on-exec などのディスクリプタ固有のフラグは、その共有状態から分離されています。
これが、「FD 42」がプロセス間で共通の意味を持たない理由であり、パス名だけでグループ化すると問題が隠れてしまう理由です。同じパスを独立して何度も開くことができ、異なるディスクリプタ番号が1つのオープン状態を共有することもあります。
ステップ 2: どのリソースがディスクリプタを消費するかを理解する
Unixスタイルのインターフェースは、読み取り可能、書き込み可能、または待機可能なディスクリプタを通じて多くのI/Oリソースを公開します。
- 通常ファイルとディレクトリ
- TCP、UDP、およびUnixドメインソケット
- 匿名パイプと名前付きパイプ (FIFO)
- 端末、デバイス、および一部の擬似ファイル
epoll、eventfd、timerfd、signalfd、inotify などの匿名カーネルオブジェクト
/proc/<pid>/fd には、プロセス内でオープンされているディスクリプタごとに1つのシンボリックリンクが含まれています。通常ファイルは通常パスを示します。ソケットとパイプは一般に socket:[inode] や pipe:[inode] として表示されます。対応するinodeを持たないオブジェクトは、anon_inode:[eventpoll] または別の anon_inode タイプとして表示されることがあります。
イベントループ用に epoll ディスクリプタを1つ作成したからといって、監視対象の数千の接続がディスクリプタを消費しなくなるわけではありません。監視される各ソケットは依然として独自のFDを持ちます。逆に、少数の anon_inode:[eventpoll] エントリがあること自体はイベントループのリークを証明しません。実際にカウントが増加しているカテゴリを特定してください。
ステップ 3: EMFILE、ENFILE、および制限の上限を区別する
RLIMIT_NOFILE にはソフト制限とハード制限があります。カーネルはソフト制限を強制します。ハード制限は、非特権プロセスがソフト制限を引き上げることができる上限です。Linuxはこの値を、プロセスが開くことができる最大のディスクリプタ番号より1大きい値として定義しています。
主な境界は以下のとおりです。
| シグナル | 意味 | 最初に調査すべき証拠 |
|---|---|---|
EMFILE | このプロセスが RLIMIT_NOFILE に達した | /proc/<pid>/limits と /proc/<pid>/fd |
ENFILE | ホストがシステム全体のオープンファイル制限に達した | /proc/sys/fs/file-nr、file-max、およびカーネルログ |
/proc/sys/fs/nr_open | RLIMIT_NOFILE を引き上げるためのカーネル上限 | ハード制限の引き上げに失敗した際に確認 |
/proc/sys/fs/file-nr の第1フィールドは割り当てられたファイルハンドルの数であり、第3フィールドは file-max に対応します。これはシステム全体のオープンファイル記述のカウントであるため、すべてのプロセスのFD数の合計と一致する必要はありません。複数のディスクリプタが1つのオープンファイル記述を共有できるためです。
プロンプトでは明示的に EMFILE が報告されており、システム全体の使用量は file-max を大幅に下回っています。したがって、主な調査対象はプロセスごとの制限です。fs.file-max を変更しても、この障害には対処できません。
ステップ 4: まず低リスクで比較可能な証拠を収集する
PIDとパーミッションを確認した後、低オーバーヘッドの /proc スナップショットから開始します。
pid=12345
grep 'Max open files' /proc/"$pid"/limits
find /proc/"$pid"/fd -maxdepth 1 -type l 2>/dev/null | wc -l
find /proc/"$pid"/fd -maxdepth 1 -type l -exec readlink {} \; 2>/dev/null |
sed -E 's/socket:\[[0-9]+\]/socket:[id]/; s/pipe:\[[0-9]+\]/pipe:[id]/' |
sort | uniq -c | sort -nr | head -20
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max/proc はリアルタイムのビューです。走査中にプロセスがディスクリプタを開閉する可能性があり、短命なエントリは消えることがあります。これらのコマンドをアトミックな監査ではなく、傾向診断として扱ってください。タイムスタンプを付けて一定間隔で合計数とカテゴリをサンプリングし、アプリケーションのメトリクスと突き合わせます。
ソケットが増加の主因である場合は、接続の方向、宛先、TCPの状態を調査します。通常ファイルが増加している場合は、ログ、一時ファイル、または設定のリロードを中心にパスをグループ化します。パイプが増加している場合は、子プロセスとIPCのライフサイクルを調査します。anon_inode オブジェクトが増加している場合は、イベントループ、ウォッチャー、またはタイマーの登録と破棄の箇所を特定します。
lsof -p <pid>、ss -tanp、または範囲を限定したシステムコールのトレーシングなどのツールは、/proc とアプリケーションメトリクスによって調査範囲を絞り込んだ後で詳細を追加できます。長時間のトレーシングは本番環境のオーバーヘッドを増加させる可能性があり、十分な権限がなければ不完全になる場合があります。
ステップ 5: カウント、構成、傾き、回復性を用いて問題を分類する
現在のカウントが高いことだけではリークとは断定できません。次の4つの質問を投げかけます。
- カウントは期待される並行性と一致しているか? バジェットには、リスニングソケット、受け入れられた接続、アウトバウンドプール、ファイル、パイプ、イベントオブジェクト、および安全マージンが含まれます。
- 構成はアーキテクチャと一致しているか? アップストリームプールの上限が500であるにもかかわらず、その宛先へのディスクリプタが5,000に達している場合は、返却パスとクローズパスを調査します。
- 安定した負荷下でも傾きは正(増加傾向)のままか? リクエスト率と並行性が横ばいであるにもかかわらず、FDが毎分25個増加し続ける場合、リークの証拠は強力です。
- 負荷が下がったときに使用量はプラトーに戻るか? リクエストスコープのファイル、短命な接続、一時パイプは解放されるべきです。長寿命のプール接続は残る可能性がありますが、明示的な上限に収まるはずです。
プロンプトでは、サービスは約400から開始し、一定のペースで増加し、再起動でリセットされ、それを繰り返します。このパターンはリークの可能性を強く示唆しています。自然なコネクションプールのウォームアップを未クローズのファイルと誤認しないために、オブジェクトの分類が依然として必要です。
キャパシティ不足の場合は通常、様相が異なります。使用量は並行性に追従し、設定されたプールや接続の制限付近でプラトーに達し、負荷やタイムアウトが収まると減少します。オブジェクトの構成比率は設計と一致しており、障害は正当なピーク需要が当初のバジェットを超えた場合にのみ現れます。制限の引き上げ、プロセスの追加、接続あたりのコスト削減が恒久的なキャパシティ変更となります。
ステップ 6: リソースの所有権を一般的なリークパスから追跡する
優れた回答は、単にコマンドを列挙するのではなく、「誰がそれを作成し、誰がそれを所有し、障害発生後に誰がそれをクローズするのか?」を問います。一般的な原因は以下のとおりです。
- HTTPクライアントがレスポンスボディをクローズしないため、接続を再利用することも速やかに解放することもできない。
- データベース、キャッシュ、またはアップストリーム接続がチェックアウトされた後、タイムアウトや例外発生時に返却されない。
- ログローテーションや設定のリロードで、古いハンドルをクローズせずに新しいファイルを繰り返し開く。
- 各リクエストがタイマー、ウォッチャー、パイプ、またはイベントオブジェクトを作成するが、キャンセル処理でクリーンアップがスキップされる。
- 親プロセスと子プロセスが、標準ストリームやIPCパイプの未使用の端を開いたままにしている。
- ディスクリプタが予期せず
execを超えて存続し、別のプロセスがリソースを生存させ続ける。 - リトライロジックが、以前の試行が保留されたまま新しい接続を作成する。
コード構造の中でライフサイクルの所有権を明確にしてください。defer、finally、RAII、またはフレームワークのスコープ付きリソース管理を使用します。取得直後にクリーンアップ処理を設定します。プール、並行性、キュー、リトライに制限を設けます。キャンセル、タイムアウト、早期リターンのパスを同じクリーンアップロジックに通すようにします。
マルチスレッドプログラムにおいて、open() を呼び出してから後の操作で FD_CLOEXEC を設定すると、別のスレッドが fork と exec を実行できる隙間が生じます。サポートされている場合は、作成時にアトミックに O_CLOEXEC を設定してください。これにより継承の競合を防ぐことができますが、通常の close() 所有権管理の代わりになるわけではありません。
ステップ 7: インシデントの緩和策と恒久的修正を分離する
リスクに応じて、インシデントの緩和策には以下が含まれます。
- プロセスがログ、制御接続、設定ファイルを開く能力を失う前に、新規ワークをレート制限するか、インスタンスあたりの並行性を減らす。
- リークしているインスタンスをローリング再起動しつつ、少なくとも1つの診断サンプルを保持し、同時一斉再起動を回避する。
- メモリ、カーネルオーバーヘッド、ダウンストリームのキャパシティを確認した上で、実際のサービスプロセスのソフト制限およびハード制限を引き上げる。
- インスタンスを追加して、正当な並行性をより多くのプロセスに分散させる。
現在のターミナルで ulimit -n を変更しても、すでに実行中のサービスには影響しません。プロセスを実際に作成するサービスマネージャー、コンテナ、またはランタイムを変更して再起動し、/proc/<new-pid>/limits を確認します。設定ファイルの編集単体では、新しい制限がアクティブであることの証明にはなりません。
恒久的な修正は、確認された増加オブジェクトを対象にする必要があります。クローズパスの完結、プールと並行性の制限、ローテーションやウォッチャーのライフサイクルの修正、適切なタイムアウトの設定、意図しない継承の防止、および主要なリソースカテゴリの現在数と作成/解放カウンターの公開を行います。
ステップ 8: キャパシティバジェットと傾きによって修正を証明する
インスタンスごとのFDバジェットを作成します。
baseline FDs + peak inbound connections + peak outbound connections + pools and files + IPC/event objects + safety headroom
バジェットは実際のアーキテクチャと負荷テストに基づく必要があります。すべてのサービスに適用できる普遍的な使用率パーセンテージは存在しません。制限には、インシデント発生時の診断、ヘルスチェック、ロギング、制御接続のための余裕も残しておく必要があります。
検証では少なくとも以下の点を確認する必要があります。
- 目標ピークトラフィックおよび障害注入下で、FD総使用量が安定したプラトーまで上昇すること。
- 負荷が下がりタイムアウトが経過した後、短命なディスクリプタが期待されるベースラインに戻ること。
- 以前増加していたオブジェクトタイプに、持続的な正の傾き(増加傾向)がなくなっていること。
EMFILE、ENFILE、接続失敗、ファイルオープン失敗がゼロを維持していること。- 過度に厳格な制限によって、リクエストのp95/p99、プール待機時間、リトライ量が悪化していないこと。
- デプロイ、ログローテーション、設定リロード、子プロセスの起動を繰り返しても、階段状の増加が発生しないこと。
- モニタリングに
process_open_fds、process_max_fds、使用率、増加率、主要なオブジェクトプールが含まれていること。
「10分間の負荷テストにパスした」だけでは、緩やかなリークを見逃す可能性があります。元のインシデントを引き起こした増加量をカバーできる十分な時間実行するか、疑わしいパスを増幅して作成カウンターと解放カウンターが釣り合っていることを証明してください。
優れた回答例
「まず、errno が EMFILE であること、および失敗しているのがAPIワーカー自体であることを確認します。ファイルディスクリプタは、プロセスのFDテーブルにおける非負のインデックスです。テーブルエントリはシステム全体のオープンファイル記述を参照し、これがオフセットやファイルステータスフラグを保持して、通常ファイル、ソケット、パイプ、または匿名のカーネルオブジェクトを参照します。dup と fork により複数のディスクリプタが1つのオープンファイル記述を共有できるため、プロセスのFD数とシステム全体のファイルハンドル数は異なるメトリクスです。
EMFILE はこのプロセスが RLIMIT_NOFILE に達したことを意味し、ENFILE はホストが file-max に達したことを意味します。ここでは、ソフト制限が8,192で、インシデント前のカウントがその値に近く、file-nr は file-max を大幅に下回っているため、システム全体の制限を変更しても的を射ていません。
/proc/<pid>/limits を読み取り、/proc/<pid>/fd 内のカウントとシンボリックリンクのリンク先をサンプリングして、ソケット、パイプ、通常ファイル、anon_inode オブジェクトに分類します。各カテゴリの傾きを、インバウンド接続、アップストリームプール、ファイルローテーション、子プロセス、エラーと突き合わせます。安定したトラフィック下で約400から始まって毎分25個増加し、再起動でリセットされるという挙動は、リークを強く示唆しています。特定のアップストリームへのソケットが支配的である場合、すべての接続が正当なトラフィックを反映していると仮定するのではなく、レスポンスボディのクローズ、タイムアウト時のキャンセル、プールの返却パスを調査します。
緩和策としては、診断サンプルを保持しながらレート制限を行い、インスタンスをローリング再起動します。キャパシティ分析で許容される場合は、実際のサービスマネージャーまたはコンテナ設定で一時的に制限を引き上げますが、新しいプロセスの /proc/<pid>/limits を検証する必要があります。恒久的な修正としては、リソースの所有権とクリーンアップを構造化し、プール、リトライ、タイマー、ウォッチャーに制限を設け、close-on-exec によって意図しない継承を防止します。
目標ピーク負荷と障害パスの下で検証します。FD使用量は計画されたプラトーに達し、負荷が下がった際にはベースラインに戻る必要があります。以前増加していたカテゴリの蓄積が止まり、EMFILE はゼロのままであり、テールレイテンシやプールの待機時間が悪化していないことを確認します。これらの結果によって修正が証明されます。再起動や制限の拡大は、枯渇を先延ばしにしたことを証明するに過ぎません。」
よくある間違い
- FDをパスやinodeとして扱う → プロセステーブル内のインデックスである → FDテーブル、オープンファイル記述、基礎となるオブジェクトを説明する。
- ディスクファイルのみがFDを消費すると決めつける → ソケット、パイプ、
epoll、タイマー、ウォッチャーも消費する →/proc/<pid>/fdのリンク先を分類する。 - Too many open files エラーに対して何でも
fs.file-maxを変更する →EMFILEとENFILEはスコープが異なる → まず errno と失敗している PID を確認する。 - 現在のシェルの
ulimit -nをサービスの制限とみなす → 実行中のサービスは別の場所から起動された可能性がある →/proc/<pid>/limitsを読み取る。 - カウントが高いことだけでリークと断定する → 正当な高並行性によって高いプラトーが形成されることがある → キャパシティモデル、タイプ構成、傾き、負荷後の回復を比較する。
- 単に 8,192 から 65,536 に引き上げるだけにする → 一定のリーク傾きがあれば新しい制限も消費し尽くす → 引き上げは検証済みのキャパシティまたは一時的な猶予枠として扱う。
- 正常なリターンパスのみをチェックする → タイムアウト、キャンセル、リトライ、早期リターンでクリーンアップがスキップされがちである → すべてのリソースに対して作成者、所有者、障害パスのクリーンアップを定義する。
- 1回限りの
lsofスナップショットを使用する → 1回のスナップショットでは蓄積を証明できない → 一定間隔でサンプリングし、ワークロードやプールと相関させる。 - エラーが一時的に消えたことでよしとする → 再起動や制限の引き上げは再発を遅らせるだけの場合がある → オブジェクトタイプ、傾き、回復、繰り返されるライフサイクルイベントを検証する。
フォローアップの質問
フォローアップ 1: ulimit -n が 65,536 を示しているのに、サービスが依然として 8,192 で失敗するのはなぜですか?
ulimit は通常、現在のシェルについて報告し、それ以降に作成された子孫プロセスに影響します。systemd、コンテナランタイム、または別のプロセス管理ツールによってすでに起動されたサービスが遡及的に変更されることはありません。また、スーパーバイザーとそのワーカーで設定が異なる場合もあります。失敗しているPIDの /proc/<pid>/limits を読み取り、実際の起動設定を更新して、新しいPIDで検証してください。
フォローアップ 2: 同じファイルに対する2つのディスクリプタが、互いの読み取り位置に影響を与えることがあるのはなぜですか?
それらが dup 由来であるか、または fork 前の同一ディスクリプタに由来する場合、同じオープンファイル記述を参照しているため、オフセットとファイルステータスフラグを共有します。プログラムが open() を2回呼び出した場合、同一パスであっても通常は独立したオフセットを持つ2つの独立したオープンファイル記述が生成されます。パス名が共通していることと、オープン状態が共有されていることは同義ではありません。
フォローアップ 3: ファイルが削除された後でも、プロセスがFDを介してそのファイルを引き続き使用できるのはなぜですか?
ディスクリプタはオープンファイル記述を参照しており、I/Oのたびにパス名を再解決することはないためです。ディレクトリエントリを削除しても、既存の参照は無効化されません。基礎となるオブジェクトは、最後の参照がクローズされるまで存続できます。プロセスにクローズさせずに古いファイルを unlink するログローテーションは、ディスクリプタとディスク容量の両方を消費し続ける可能性があります。
フォローアップ 4: RLIMIT_NOFILE を非常に大きな数値に引き上げる操作が失敗することがあるのはなぜですか?
非特権プロセスは、ソフト制限をハード制限より上に引き上げたり、自身のハード制限を自由に変更したりすることはできません。また、Linuxは RLIMIT_NOFILE を /proc/sys/fs/nr_open で制限しています。サービスマネージャー、コンテナ、または権限境界によって制約が追加されることもあります。変更を行うたびに、起動エラーと新しいプロセスの有効な制限を確認してください。
フォローアップ 5: 1つの epoll インスタンスが多数の接続を監視している場合でも、プロセスがFDを枯渇させることがあるのはなぜですか?
epoll インスタンス自体が1つのFDを消費し、anon_inode:[eventpoll] として表示されます。監視対象の各ソケットは依然として個別のFDです。epoll は多数の接続に対する待機を効率化しますが、数千のソケットを1つのディスクリプタに統合したり、アプリケーションの代わりにクローズしたりするわけではありません。
フォローアップ 6: FD使用量に対してどのようにアラートを設定しますか?
インスタンスごとに、現在開いているディスクリプタ数、プロセスの最大値、使用率、および増加率を監視します。使用率は枯渇への近さを検知し、傾きは緩やかなリークを早期に検知します。コネクションプール、ファイル、ウォッチャーも独自の現在数を公開すべきです。普遍的なパーセンテージをコピーするのではなく、ピーク時のバジェット、スケーリングにかかる時間、インシデント対応のリードタイムに基づいてしきい値を設定してください。