代表的な面接トピック

Linux面接:OOM Killをどのように診断しますか?

一般難しい
Offer.cc 編集チーム公開日 更新日

質問

64 GiBのLinuxノードにMemAvailableがまだ18 GiB残っているにもかかわらず、メモリ制限が4 GiBのコンテナがOOMKilledおよび終了コード137で繰り返し終了しています。cgroup v2では、memory.events内のoom_killが増加し、memory.currentがmemory.maxに近づいています。トリガーを証明し、cgroup OOMとグローバルOOMを識別し、メモリ増加の発生源を特定し、インシデントを安全に封じ込め、再発を防止するにはどうしますか?

プロンプトと適用される場面

64 GiBのLinuxノードにまだ18 GiBのMemAvailableがあるにもかかわらず、1つのコンテナが6時間にわたって繰り返し終了しています。 ランタイムはOOMKilledと終了コード137を記録しています。ワークロードのcgroup v2 memory.maxは4 GiBです。障害の直前に、 memory.currentはその制限に近づき、oom_kill内のmemory.eventsが増加します。同じ期間に、 memory.stat内のanonは1.2 GiBから3.5 GiBへと増加する一方、fileは約280 MiBです。トリガーを証明し、 cgroup OOMとグローバルOOMを区別し、増加の発生源を特定し、インシデントを安全に封じ込め、 再発を防止する方法を説明してください。

64 GiB、18 GiB、4 GiB、6時間のウィンドウ、および使用量の数値は面接用の前提条件であり、キャパシティのベンチマークではありません。 Linuxはcgroup v2を使用しており、ランタイムステータスとcgroupファイルは同じ障害ウィンドウを参照していると想定します。この 質問は、OSのメモリ回収(reclaim)、cgroupの課金(accounting)、OOMの証拠、および プロセス処理をテストするため、generalに属します。コンテナプラットフォームは設定環境を提供しているにすぎません。

2026年に公開されたLinuxおよびOSに関する公開面接資料には、OOMシナリオが直接含まれており、 受験者に対してカーネルログ、リソース制限、プロセスの挙動から推論することを求めています。完全な回答は、 「メモリを追加する」や「137を確認した」にとどまらず、証拠チェーン、安全な封じ込め、原因の特定、および修正が機能することを示す 再現可能なテストにまで及ぶ必要があります。

面接官が評価しているポイント

第1の評価ポイントは、候補者が証拠を正しく解釈しているかどうかです。Bashの終了ステータスの慣例では、137は 128プラスシグナル9である可能性があるため、プロセスがSIGKILLによって終了したことを示します。管理者、タイムアウトコントローラ、 またはOOM killerのいずれもがそのシグナルを送信する可能性があります。ランタイムのOOMKilled理由、同じウィンドウ内での memory.events:oom_killの増加、およびカーネルログが、原因をOOMへと絞り込む要素となります。

第2の評価ポイントは、候補者がリソースドメインを識別できるかどうかです。memory.maxはcgroupのハードリミットです。 使用量がこれに達し、回収によって課金量を減らせない場合、カーネルはそのcgroup内部でOOMを起動することがあります。ノードには まだ18 GiBの空きがある場合があります。一方、グローバルOOMはノード全体の割り当て圧迫から発生し、異なる対象(victim)プールと 異なる復旧アクションを伴います。

第3の評価ポイントは、メモリ会計(accounting)の厳密さです。1つのプロセスのRSSだけを見ていると、子孫プロセス、ページキャッシュ、 tmpfs、共有メモリ、ソケットバッファ、同じcgroupに課金されるカーネルデータを見落とします。優れた回答は、 VSZ、RSS、cgroup課金額を同等として扱うのではなく、memory.currentmemory.peakmemory.stat、プロセスごとの測定値、 およびアプリケーションプロファイルを整合させます。

最後に、面接官はインシデント対応の判断力を評価しています。制限を一時的に引き上げることでサービスが復旧する場合もあれば、 継続的なリークをノード全体の障害へと拡大させてしまう場合もあります。回答では、負荷遮断(load shedding)、再起動、スケーリング、制限引き上げの タイミングをいつにするか、通常ピークとリークをどのように区別するか、負荷・ソーク(浸透)・障害テストによってポリシーをどのように検証するかを説明する必要があります。

回答前に明確にすべき質問

  • 記録は同じコンテナインスタンスおよび障害ウィンドウを参照していますか? 古いOOMKilledステータス、現在の

cgroupカウンタ、別の終了は因果チェーンを形成できません。コンテナID、開始時刻、終了時刻、パスを一致させます。

  • ホストは実際にcgroup v2を使用していますか? v1とv2では公開されるファイルとセマンティクスが異なります。パスが

コンテナ自身のcgroupなのか、複数の子孫ワークロードを含む親cgroupなのかを確認します。

  • oom_killが変化したのか、それともoomのみですか? oomは制限に達し割り当てが失敗しそうになったことを示し、

oom_killはプロセスが実際にOOM killerによって終了させられたことを記録します。memory.events.localで 階層的なカウントとクロスチェックします。

  • ノードもグローバルなメモリ圧迫下にありましたか? カーネルログ、MemAvailable、スワップ、PSI、エビクション(立ち退き)イベント、

隣接するワークロードを検査します。cgroup OOMとノードの圧迫は近い時間帯に発生することがあります。

  • 4 GiBの制限はコンテナ、Pod、または親cgroupのどれに属していますか? 子の制限に達する前に、

親の制限が子を拘束することがあります。階層を遡り、有効な境界をすべて検査します。

  • どのようなワークロードが増加と相関していますか? リクエストの同時実行数、キューの深さ、バッチサイズ、キャッシュキー、接続数、

入力サイズ、デプロイバージョンは、時間とともに増加する保持メモリと、処理後に縮小するワーキングセットを切り分けるのに役立ちます。

  • いくつのプロセスがcgroupを共有していますか? サイドカー、ワーカー、フォークされた子プロセスはすべて課金額に寄与します。

メインプロセスのみをプロファイリングすると、増加の本体を見落とす可能性があります。

  • 復旧とデータ整合性の要件は何ですか? マルチプロセスワークロードの1つのメンバーを強制終了すると、

不整合な状態が残る可能性があります。再起動、負荷遮断、トラフィックシフト、グループ終了は、べき等性とRTOに依存します。

30秒の回答フレームワーク

「私は137をSIGKILLの手がかりとして扱い、OOMの確定結論とはしません。コンテナIDと障害時刻を一致させ、 ランタイムのOOMKilled理由、対象cgroupのmemory.events.localmemory.currentmemory.max、 およびカーネルログを確認します。ノードには空きメモリがありますが、cgroupが制限に達しoom_killが増加しているため、 証拠はcgroup OOMを示しています。負荷を遮断またはシフトして証拠を保全し、ノードのヘッドルームを確認した上でのみ制限を一時的に引き上げます。 次にmemory.statを使用して匿名、ファイル、共有、カーネルメモリを内訳し、 プロセスごとのプロファイルとワークロードメトリクスを相関させて、リーク、無制限キャッシュ、同時実行急増、または 制限値不足を区別します。修正後は、代表的なピーク、長時間のソーク、制御された制限テストを実施し、 memory.high、圧力、ピーク、OOMイベントについてアラートを設定します。」

ステップごとの詳細解説

ステップ1:終了イベントを1つのタイムラインにまとめる。

コンテナID、PID、開始時刻と終了時刻、デプロイバージョン、再起動回数、cgroupパスを記録します。終了コード137は一般的に シェルやコンテナステータスでのSIGKILLに対応しますが、OOMを証明するものではありません。kill -9、プラットフォームのタイムアウト、または ノードエージェントも同じ結果をもたらす可能性があります。reason: OOMKilled、cgroupイベントの差分、および同じ秒数前後の カーネルメッセージを関連付けます。

親の階層カウンタに子孫のイベントが混ざらないよう、memory.events.localを優先します。この終了時にoom_killが 7から8に変化し、memory.currentが4 GiBに近づいており、カーネルログがメモリcgroup OOMを報告している場合、 それは整合性のあるcgroup OOMの証拠チェーンです。カウンタの変化やOOMランタイム理由がなく137のみが存在する場合は、 手動シグナル、ヘルスチェックのタイムアウト、systemd-oomd、エビクション、ランタイムによる終了を代わりに調査します。

ステップ2:OOMリソースドメインを特定する。

memory.maxは、cgroupとその子孫のアカウンティング対象メモリを制限します。これに達し、直接回収(direct reclaim)で 割り当てを満たせない場合、カーネルはそのcgroup内からのみ対象プロセスを選択できます。したがって、ホストの18 GiBの MemAvailableはコンテナを保護しません。親cgroupを上位に辿り、memory.maxおよび イベントカウンタの上限に達した境界を見つけます。

グローバルOOMの証拠は異なります。ノードのメモリとスワップが枯渇に近づき、メモリPSIが上昇し、カーネルログに グローバルなメモリ状態と被害プロセスが含まれ、他のワークロードも影響を受けます。ノードの圧迫下では、プラットフォームが最初にPodを エビクションする場合があります。cgroup OOMの修復はワークロードの使用量とその制限に焦点を当てますが、グローバルOOMではノードの オーバーコミット、リクエストとリザーブ、システムデーモン、ワークロードの配置の修正も必要になります。

ステップ3:4 GiBがどこに使われたかを説明する。

まずmemory.currentmemory.peakを確認し、次にmemory.statで課金の内訳を調べます。ここでは、anonが 6時間で1.2 GiBから3.5 GiBに増加し、fileは約280 MiBとなっています。これにより、ヒープ、匿名マッピング、および それらを所有するプロセスが優先調査対象となりますが、まだリークの証明にはなりません。カーネルドキュメントによれば、cgroupの課金は ページキャッシュ、tmpfsと共有メモリ、カーネル構造体、ソケットバッファも対象とし、親には子孫が含まれます。

合計値を、各プロセスのRSS、PSS、匿名マッピング、プロセス数とスレッド数、ランタイムヒープメトリクス、 ビジネス測定値と並べて1つのタイムラインに配置します。生存オブジェクトとアプリケーションヒープが同時に増加している場合は、保持された 参照または無制限のキャッシュを調査します。ヒープが安定しているのにRSSが高いままの場合は、アロケータの断片化、ネイティブ ライブラリ、またはmmapを調査します。fileshmemsock、またはslabの増加は、tmpfsファイル、キャッシュ、接続バックログ、 またはカーネルオブジェクトを示唆します。

VSZは仮想アドレス空間であり、物理常駐メモリやcgroupの課金額ではありません。また、単一のスナップショットでは不十分です。 健全なバッチワークロードでもピークに達した後に回収されて減少することがありますが、リークは一般に同等の負荷でサイクルを繰り返すうちにベースラインを押し上げます。 同じスループット下での傾きと、解放後の定常状態を比較します。

ステップ4:各仮説を反証可能にする。

候補リストを絞り込み、それぞれについて予測を立てます。同時実行のピークであれば、メモリは処理中の リクエストに追従し、完了時に減少するはずです。無制限キャッシュの場合、エントリ数とanonが一緒に増加し、容量が 制限された時点で安定します。コンシューマのバックログでは、キューの深さ、バッチオブジェクト、メモリが連動します。制限値が 小さすぎる場合、安定した代表的ワーキングセットが、長期的な上昇傾向を示すことなく繰り返し境界に近づきます。

ランタイムに適したヒーププロファイル、割り当てプロファイル、またはオブジェクトヒストグラムを使用しますが、収集の オーバーヘッドを考慮に入れます。低リスクの本番メトリクスやトラフィックシフトされたレプリカから始めます。ダンプを取得する前に、ディスク容量、 プライバシー、一時停止のコストを確認します。バージョン比較やトラフィックのリプレイでは、一度に1つの要素のみを変更します。制限の引き上げ、 キャッシュの無効化、同時実行数の削減を同時に行うと、真因が隠れてしまいます。

ステップ5:封じ込めと恒久的な修正を分離する。

まずユーザーとノードを保護します。インスタンスへの同時実行数を制限する、メモリを大量消費するバッチを一時停止する、トラフィックをシフトする、または 既知の安全なバージョンにロールバックします。リプレイが安全でマルチプロセスの状態の一貫性が保たれる場合は、再起動によってメモリを 速やかに解放します。1つのワーカーを終了させると共有状態が破損する場合は、ワークロードをユニットとして終了させることを検討します。cgroup v2では、 memory.oom.group=1によりOOM killerに対してcgroupを不可分なものとして扱うよう指示できますが、その復旧セマンティクスはテストする必要があります。

ノードのヘッドルーム、隣接ワークロードの保護、予想されるピークを測定した上でのみ、memory.maxを一時的に引き上げます。 有効期限、モニタリング、ロールバックの閾値を設定します。継続的な増加に対しては、制限を大きくしても次のOOMを遅らせるだけです。 恒久的な修正としては、キャッシュの上限設定、参照の解放、バッチと同時実行数の制限、子プロセスのライフサイクル修復、あるいは 測定されたワーキングセットとバースト許容量に基づいたリクエストと制限のサイズ変更などが挙げられます。

通常のサービスにoom_score_adj=-1000を割り当てることでインシデントを「解決」してはなりません。これによりそのサービスはOOMの選択対象から除外され、 カーネルはより重要または多数のプロセスを強制終了せざるを得なくなる可能性があります。エンドツーエンドの障害ポリシーレビューを経た後、 不可欠なシステムタスクにのみ予約してください。スワップも回収とレイテンシの挙動を変化させます。短いバーストを吸収することはあっても、 無制限の増加を修復することはありません。

ステップ6:キャパシティを検証し、早期のシグナルを作成する。

本番相当のcgroup課金と制限のもとで、代表的なトラフィックをリプレイします。定常負荷、ピーク、 大規模入力、バックログ復旧、マルチプロセスの挙動をカバーします。ピークには短時間の負荷テスト、 増加の傾きと解放後のベースラインには長時間のソークテスト、アラート、終了、再起動、 データ整合性の動作確認には制御された低めの制限テストを使用します。成功の基準は、目標スループットとレイテンシでの明確なヘッドルーム、減少するmemory.current、 新たなoom_killの非発生、およびノードの圧迫や隣接ワークロードの悪化がないことです。

ハードリミットの前の制御境界としてmemory.highを使用します。これを超えるとcgroupがスロットルされ、OOMを 直接呼び出すことなく直接回収が促進されるため、アラートを出したり封じ込めを自動化したりする時間が確保されます。 memory.current/memory.max比率、memory.peakhighmaxoomoom_killイベント、メモリPSI、 再起動率、アプリケーションヒープ、増加の傾きを監視します。制限値はピークおよびソークの測定値から設定し、 バージョン、同時実行数、または入力分布が変化したときに見直す必要があります。

質の高い回答例

「まず、OOMがこの終了を引き起こしたかどうかを証明します。終了コード137はプロセスがSIGKILLを受信したことを示しますが、 手動による終了やタイムアウトでも同様の結果になります。コンテナID、終了時刻、cgroupパスを一致させ、ランタイムの OOMKilled理由、memory.events.localの差分、およびカーネルログを調査します。この終了時にoom_killが増加し、 memory.currentが4 GiBに達し、ログがメモリcgroupを特定している場合、cgroup OOMが裏付けられます。

これは、ノードに18 GiBの空きが存在し得る理由も説明しています。memory.maxはcgroupのハード境界であり、 回収に失敗すると、カーネルはホストを枯渇させることなくそのリソースドメイン内で対象を選択します。それでも、親cgroup、ノードPSI、 スワップ、グローバルOOMログを検査して、同時発生しているノード圧迫やプラットフォームによるエビクションを除外します。

封じ込めとしては、メモリ消費の大きいトラフィックを遮断またはシフトし、障害前のメトリクスと低オーバーヘッドのプロファイルを保全します。 再起動が安全であれば、既知のバージョンをリストアします。ノードのヘッドルームと隣接ワークロードを確認した上でのみ、 有効期限とロールバック条件を設定して制限を一時的に引き上げます。メモリの追加は恒久的な修正ではありません。

診断のためには、memory.currentmemory.peakmemory.statを使用してcgroupの合計を照合し、 プロセスごとのRSS、PSS、匿名マッピング、ランタイムヒープを検査します。ここではanonが1.2 GiBから3.5 GiBに増加し、ファイルメモリは 約280 MiBであったため、ヒープ、匿名mmap、子プロセス、アロケータを優先しますが、プロファイルによって発生源を証明します。 メモリを同時実行数、キューの深さ、キャッシュエントリ、バッチサイズ、バージョンと相関させます。同等負荷でベースラインが上昇している場合はリークを示唆し、 処理後に減少する安定したワーキングセットであれば、通常のピークまたは制限値不足を示唆します。

修正後は、同じcgroup制限下でピーク負荷と長時間のソークテストを実行し、メモリが減少し、 イベントカウンタの増加が止まり、レイテンシとスループットが目標を満たしていることを検証します。次にハード境界の手前にmemory.highとアラートを 設定し、ピーク、圧力、OOMイベント、増加の傾きを監視し、測定されたワーキングセットから同時実行数、キャッシュ、キャパシティをサイズ決定します。 マルチプロセスサービスの場合は、OOMでグループ全体を終了すべきかを決定する前に、部分終了後のデータ整合性もテストします。」

よくある間違い

  • 137だけでOOMと断定する → SIGKILLはオペレータやタイムアウトコントローラからも送信され得る → **ランタイム理由、

cgroupイベント、カーネルログを相関させる。**

  • ホストに空きメモリがあるためOOMを除外する → ノードに余裕があってもcgroupがハードリミットに達することがある →

まずリソースドメインを特定する。

  • 1つのプロセスのRSSだけを見る → 子孫、ファイル、共有メモリ、カーネル課金もカウントされる → **合計を

照合し、memory.statを内訳する。**

  • VSZを物理使用量として扱う → アドレス空間サイズは常駐メモリや課金メモリではない → **RSS/PSS、

マッピング、cgroupメトリクスをクロスチェックする。**

  • anonのあらゆる増加をリークと呼ぶ → 通常のワーキングセットやバッチピークも増加する → **同等の

負荷における傾きと解放後のベースラインを比較する。**

  • 即座に制限を2倍にする → リークを遅らせ、ノードの安全マージンを消費する可能性がある → **キャパシティの

証拠、有効期限、ロールバック閾値を必須とする。**

  • アプリケーションをOOMに対して無敵(immune)にする → 被害が他のタスクやノードに移る可能性がある → **エンドツーエンドの

障害ポリシーの一環としてのみoom_score_adjを変更する。**

  • 1分間の負荷テストしか実行しない → 短いテストでは緩やかなリークや断片化を見落とす → **ピークテストと

長時間のソークテストの両方を実行する。**

  • ハードリミットでのみアラートを発報する → memory.maxでの対応は通常手遅れである → **早期アクションのために

memory.high、PSI、増加の傾きを使用する。**

  • 1つのワーカーが停止した後の回復を鵜呑みにする → マルチプロセスの共有状態が不整合になる可能性がある → **グループ終了、

再起動、データ復旧のセマンティクスをテストする。**

フォローアップの質問と回答方法

フォローアップ1:終了コード137が存在しますが、oom_killは増加していませんでした。次に何を検査しますか?

まずcgroupパスとイベント時刻を確認し、親や新しいインスタンスと比較していないことを確認するために memory.events.localを読み取ります。それでもOOMの証拠がない場合は、ランタイムの終了理由、デプロイやヘルスチェックのタイムアウト、 オペレータの監査ログ、systemd-oomd、ノードエビクション、カーネルログを検査します。終了コード137は結果がSIGKILLであることを 示すのみであり、送信者を特定するものではありません。

フォローアップ2:メモリリークと制限値不足をどのように区別しますか?

同様のスループット、入力、同時実行数での繰り返しのサイクルを比較します。リークは通常、ベースラインや生存オブジェクト数を 押し上げ、低負荷時にも減少しません。制限値不足の場合は、再現可能なピーク時に失敗しやすく、その後は安定したワーキングセットに戻る 可能性が高くなります。単一のグラフに頼るのではなく、ヒープや割り当てプロファイル、キャッシュエントリ、子プロセス、バッチサイズ、 memory.statで検証します。両方の状態が共存することもあります。

フォローアップ3:memory.maxを引き上げるのが妥当なのはどのような場合ですか?

代表的なピークテストおよびソークテストで健全な動作が示され、必要なワーキングセットが現在の制限を超えている一方で、 ノードのキャパシティ、リザーブ、隣接保護にまだマージンが残っている場合に引き上げます。インシデント時の引き上げには、 有効期限、モニタリング、ロールバック閾値が必要です。使用量が際限なく増加する場合、制限の引き上げは一時的な封じ込めにすぎません。 負荷の遮断と増加源の修復も行ってください。

フォローアップ4:memory.highmemory.maxはどのように連携して機能すべきですか?

memory.highはスロットリングと直接回収の境界です。これを超えてもOOM killerが直接呼び出されるわけではないため、 観察と自動化のウィンドウを提供できます。memory.maxは最終的な分離境界であり、回収に失敗するとcgroup OOMを 呼び出す可能性があります。両方を測定されたワーキングセット、ピーク、レイテンシ許容量、ノードマージンに基づいて設定し、 highmaxoomoom_killイベントに対して個別にアラートを発報します。

フォローアップ5:マルチプロセスサービスがmemory.oom.groupを使用する理由は何ですか?

1つのワーカーを終了させると共有状態、ロック、未完了トランザクションに不整合が生じる場合、cgroupを 不可分なワークロードとして扱うことで、障害と復旧を決定論的にすることができます。有効にする前に、グループ全体の再起動時間、 タスクのべき等性、データ復旧をテストしてください。oom_score_adj=-1000を持つタスクは例外となるため、中途半端な ワークロードが残っていないことも確認してください。

公開情報ソース

関連する質問