代表的な面接トピック

Linux 面接:df と du でディスク使用量が異なるのはなぜか?

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

質問

容量 200 GiB の ext4 /var ファイルシステムがあります。df では 196 GiB が使用中と報告され、非特権サービスが利用可能な空き容量は 0 ですが、sudo du -x では 118 GiB のみが報告され、inode 使用率は 21% です。書き込みが失敗しており、ホストを再起動せず、未検証のデータを削除することなく、15 分以内に空き容量を回復しなければなりません。df と du の数値が一致しない理由、不足している 78 GiB を特定する方法、安全に領域を解放する方法、そしてインシデントの解決を証明する方法を説明してください。

課題と前提コンテキスト

02:00 に、あるアプリケーションが /var 配下への書き込み中に ENOSPC を受け取り始めました。ホストは次のように報告しています。

text
$ df -B1 /var
Filesystem          1B-blocks         Used  Available Use% Mounted on
/dev/nvme0n1p3    214748364800 210453397504          0 100% /var

$ sudo du -x -B1 -s /var
126701535232  /var

$ df -i /var
Filesystem          Inodes   IUsed    IFree IUse% Mounted on
/dev/nvme0n1p3    20000000 4200000 15800000   21% /var

四捨五入した値は、合計 200 GiB、df による使用量が 196 GiB、du から到達可能な容量が 118 GiB であり、78 GiB の使用領域のギャップが生じています。inode の出力から、直接の原因としての inode 枯渇は除外されます。サービスは非特権であり、ホストは再起動できず、所有者と用途が判明するまでいかなるファイルも削除してはなりません。

これは Linux 本番環境のトラブルシューティングに関する質問です。最も優れた回答は、最も大きなパス名のファイルを削除することから始めるものではありません。まず両方のツールが同じファイルシステム、名前空間、時間、単位、アクセススコープを観察していることを証明し、次に可視ディレクトリツリーの外部に存在し得る割り当てについて説明し、影響範囲を理解した上で復旧アクションを選択します。

面接官が評価するポイント

1 つ目の評価基準は、正しい計測モデルです。df はマウントされたファイルシステムに対して全体的なブロック統計を問い合わせます。du は名前付きファイルとディレクトリをトラバースし、到達可能なエントリによって表されるブロックを見積もります。両者は関連していますが、異なる対象を測定しています。リンクが解除されたファイルによって保持されている大きな割り当てや、オーバーマウントされたディレクトリの下に隠れている大きな割り当ては、現在のトラバースで名前を指定できなくてもファイルシステムの合計に残ります。

2 つ目の評価基準は、スコープ制御です。df /var を制限のない du /var と比較したり、コンテナ内で片方のコマンドを実行したり、権限エラーを無視したり、急激な書き込み中に取得したサンプルを比較したりすると、見かけ上の不一致が生じます。候補者は、原因を断定する前に、マウントターゲット、マウント名前空間、ファイルシステム境界、バイト単位、権限、および観察時間枠を揃える必要があります。

3 つ目の評価基準は、安全なインシデント対応です。lsof +aL1 /var はそのファイルシステム上でリンクカウントが 1 未満の開かれたファイルを特定できますが、その出力は証拠であり、プロセスを終了したり記述子を切り詰めたり(truncate)する許可ではありません。優れた回答は、サービスの所有者を特定し、ファイル記述子とデバイスを確認し、アプリケーションがサポートするログ再オープンやグレースフルな再起動を優先し、ディスクの回収とアプリケーションの正常性の両方を検証します。

4 つ目の評価基準は、似たように見える原因の切り分けです。ext4 の予約ブロックは非特権ライターが利用可能な領域を減らし、Size - UsedAvail を上回る原因になりますが、78 GiB が df で使用中としてカウントされ、同一ファイルシステムの du トラバースには存在しない理由を説明するものではありません。inode の枯渇でも ENOSPC が返されることがありますが、問題文の 21% という inode 使用率はその可能性を否定します。コピーオンライトのスナップショット、圧縮、クォータには、ext4 コマンドを盲目的にコピーするのではなく、ファイルシステム固有のツールが必要です。

最後の評価基準は、インシデントの完了確認です。回答では、前後の証拠セットを確立し、解放されたバイト範囲を説明し、ライターが意図したパス名を再オープンしたことを確認し、削除済みで開かれたままのファイルの再発をチェックし、ログローテーションやマウント変更の監視を改善する必要があります。df のパーセンテージが下がっただけでは、アプリケーションが正常であることやデータが保持されたことは証明されません。

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

  • 両方のコマンドは同じマウント名前空間で実行されていますか? 一方がホスト上で実行され、もう一方がコンテナやサービスの名前空間で実行されている場合、/var は異なるマウントに解決される可能性があります。調査は影響を受けるプロセスの名前空間に入るか、意図的にホスト上にとどまる必要があります。
  • 具体的にどのパスが失敗しており、どのファイルシステムにそれが含まれていますか? findmnt -T は任意のパスをそのマウントに対応付けます。ネストされたマウントやバインドマウントがあると、dfdu を比較すべき対象が変わります。
  • サンプルは同じ単位と権限で同時に取得されましたか? 急増するログはコマンドの実行間隔の間に変化する可能性があり、人間が読みやすい形式の四捨五入は小さな差異を隠し、読み取り不可能なディレクトリは非特権の du のカウント不足を招きます。
  • どのファイルシステムおよびストレージ層が関与していますか? ext4、XFS、Btrfs、ZFS、overlay ファイルシステム、LVM スナップショット、クラウドボリュームスナップショットは、それぞれ異なるアカウンティングおよび復旧制御を提供します。
  • 不足しているのはデータブロック、inode、またはクォータですか? df -i、クォータツール、およびアプリケーションエラーによって切り分けます。大きなファイルを 1 つ削除しても inode の枯渇は解決しませんし、グローバルブロックに空きがあってもユーザーやプロジェクトのクォータを上書きすることはできません。
  • 運用上どのアクションが許可されていますか? ログ再オープンシグナル、グレースフルな再起動、フェイルオーバー、または短いメンテナンスウィンドウでは、可用性リスクが異なります。緊急の記述子切り詰めには、明確な責任者の特定とロールバック計画が必要です。
  • どれだけの空き容量をいつまでに回復する必要がありますか? 目標によって、書き込みの抑制、ボリュームの拡張、トラフィックのフェイルオーバー、または既知の割り当ての優先解放のいずれを行うかが決まります。未知のデータを削除する正当な理由にはなりません。

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

「まず破壊的なアクションを凍結し、findmntdf -B1sudo du -x -B1 を使用して、同じパス、ファイルシステム、マウント名前空間、時間、権限、バイト単位で比較します。inode 使用率は 21% なので、78 GiB のブロック差分を定量化し、lsof +aL1 /var でプロセスが依然として開いたままにしているリンク解除済みファイルを確認します。それが差分の原因であれば、PID、記述子、デバイス、サービスの所有者を確認した上で、アプリケーションのログ再オープンパスまたはグレースフルな再起動を利用し、領域が戻ることを確認します。そうでなければ、オーバーマウントされたディレクトリ、権限エラー、ファイルシステム固有のメタデータやスナップショットを調査します。ext4 の予約ブロックは非特権ユーザーの空き容量がゼロである理由を説明しますが、78 GiB の使用済み領域のギャップを説明するものではありません。最後に測定を再実行し、アプリケーションの書き込み、ログ、再発の有無を確認して完了とします。」

ステップバイステップの詳細解説

まず比較を再現可能にすることから始めます。失敗しているパス、現在の名前空間、マウントソース、ファイルシステムの種類、ブロック統計、inode 統計、および du の合計を時間的に近接して取得します。

bash
readlink -f /var
findmnt -T /var -o SOURCE,FSTYPE,OPTIONS,TARGET
df -B1 /var
df -i /var
sudo du -x -B1 -s /var

-xdu/var を含むファイルシステム内に留めるため、ネストされたファイルシステムが合計に加算されることはありません。-B1 はブロック単位の曖昧さを排除します。du を十分な権限で実行し、その診断出力を読みます。権限エラーを確認せずに抑制すると、アクセス問題がストレージの誤った仮説に変わってしまいます。同じマウント名前空間からコマンドをキャプチャします。コンテナ化されたライターの場合、同一のパス文字列が同一のオブジェクトを指していると仮定せず、ホストのビューと意図的な nsenter のビューを比較します。

観察されたギャップは診断用の数値であり、検索対象のファイルではありません。

text
same_filesystem_gap = df_used_blocks - du_reachable_allocated_blocks
                    = 196 GiB - 118 GiB
                    = 78 GiB

ファイルシステムのメタデータ、割り当ての粒度、並行書き込み、コピーオンライトの共有、圧縮、ツールのセマンティクスは完全には一致しないため、この式は近似値です。それでも有用です。78 GiB の安定したギャップは、出力の四捨五入として片付けるには大きすぎます。別の通常の大きなパス名を探す前に、可視で到達可能なツリーの外部にブロックを割り当てている原因を探します。

最も価値の高いチェックは、最後のディレクトリンクが削除された開かれたファイルです。

bash
sudo lsof +aL1 /var

# After selecting a candidate from lsof, verify the live descriptor.
sudo readlink /proc/2481/fd/7
sudo stat -Lc 'device=%d inode=%i size=%s blocks=%b block_size=%B' /proc/2481/fd/7

Linux の unlink は名前を削除します。それが最後のリンクであってもプロセスが依然としてそのファイルを開いている場合、ファイルとそのブロックは参照している最後の記述子が閉じるまで残ります。du はディレクトリツリーを通じてその inode に到達できませんが、df は依然として割り当てられたブロックをカウントします。よくあるインシデントパターンは、ローテーションによってファイルが誤って削除または名前変更された後も、ロガーがそのファイルに書き込みを続けるケースです。

見かけ上の SIZE/OFF の値を、すべてのスパースファイルや特殊ファイルに対する正確な回収予測として扱わないでください。記述子がターゲットデバイス上の通常ファイルであること、リンクカウントがゼロであること、どのプロセスがそれを所有しているか、まだ増加中であるか、アプリケーションに文書化された再オープンシグナルがあるかを確認します。1 つの記述子で 78 GiB の大半を占めていない場合は、複数の大きな記述子を関連付けて確認します。

推奨される復旧順序は、アプリケーション固有の再オープン、グレースフルなリロード、グレースフルな再起動またはフェイルオーバー、その後の緊急介入です。サポートされているログ再オープンアクションは、ホスト全体を終了することなく、古い記述子を閉じて現在のパス名を開きます。制御されたサービス再起動もこれを解放しますが、レプリカ、レディネス、処理中の作業を考慮する必要があります。最初に SIGKILL を送信すると、アプリケーションのクリーンアップパスが破棄されます。すでにリンクが解除されているファイルに対してさらに名前を削除しても何も起きません。

/proc/PID/fd/FD 経由の書き込みはプロセスを停止することなく領域を回収できますが、これは最後の手段です。ライターが古いオフセットを保持し、次の書き込みでスパースホールを作成したり、アプリケーションフォーマットを破損したり、内部の不変条件を破ったりする可能性があります。サービスの所有者が記述子と書き込みセマンティクスを確認し、トラフィックが封じ込められ、証拠が保存され、復旧計画が存在する場合にのみ使用してください。一般的なランブックにおいて、記述子の切り詰めをデフォルトの修正方法として提示すべきではありません。

lsof でギャップを説明できない場合は、マウントトポロジを調査します。空でないディレクトリの上にファイルシステムがマウントされると、基になるディレクトリエントリが隠蔽されますが、そのブロックは親ファイルシステム上で割り当てられたままになります。findmnt はネストされたパス、バインドパス、オーバーマウントされたパスを明らかにします。承認されたメンテナンス作業中に、/var/var の外側にある空のターゲットに非再帰的にバインドマウントすることで、本番環境の子マウントをアンマウントせずに、背後にある親のビューを公開できます。

bash
sudo mkdir -p /mnt/var-underlay
sudo mount --bind /var /mnt/var-underlay
sudo du -x -B1 -s /mnt/var-underlay
sudo umount /mnt/var-underlay

まず実際のマウントツリーに対してこのアプローチを検証してください。名前空間の伝播やプラットフォームのポリシーによって効果が変わる可能性があります。単に調査するためだけにビジー状態の本番ファイルシステムをアンマウントしないでください。隠れたファイルが見つかった場合は、移動または削除する前に、所有者と保持要件を特定してください。

次に、利用可能性のアカウンティングを使用済み領域のギャップから切り離します。ext4 では、特権プロセス用の予約ブロックにより、未加工の空きブロックがアプリケーションから利用できなくなる場合があります。変更するのではなく調査します。

bash
source=$(findmnt -n -o SOURCE -T /var)
sudo tune2fs -l "$source" | grep -E 'Block count|Reserved block count|Block size'
sudo du --inodes -x -d1 /var | sort -n

問題文では合計と使用量の差が 4 GiB ありますが、利用可能容量は 0 と表示されており、これは非特権サービスに対して一部の空きブロックが利用不可になっていることと一致します。これは直接的な書き込み失敗の理由を説明しますが、なぜ dfdu で到達可能な容量よりも 78 GiB 多く使用中とマークしているかは説明しません。インシデント中に予約を減らすと、特権デーモン向けに確保されたヘッドルームがなくなり、断片化のリスクが高まる可能性があります。これには反射的な tune2fs -m 0 ではなく、キャパシティに関する判断が必要です。

inode の枯渇は、もう 1 つの独立した ENOSPC の原因です。ここでは df -i が 21% を示しているため、引き金ではありません。もし 100% であれば、du --inodes -x を使用してファイル数の多いツリーを特定し、保持ポリシーでカバーされているデータのみを削除します。ブロックの回収と inode の回収は別の目標です。

ファイルシステム固有のアカウンティングは最後に行います。GNU du は、コピーオンライトの共有、圧縮、バックアップブロック、ネットワークファイルシステムによって、その見積もりがデバイスの消費量と乖離する可能性があることを文書化しています。Btrfs または ZFS では、そのファイルシステムのツールを使用して、サブボリューム、スナップショット、クォータ、排他バイト数と参照バイト数を調査します。オーバーレイストレージでは、それらを所有する名前空間とランタイムからレイヤーを調査します。XFS または Btrfs のソースに対して ext4 ツールを実行しないでください。

開かれたファイル、隠れたデータ、アクセスエラー、並行増加、予約された利用可能容量、ファイルシステムの機能を確認しても証拠を説明できない場合は、カーネルログとファイルシステムの正常性を調査します。修復ツールはオンラインの診断ショートカットではありません。証拠を保持し、ファイルシステムの文書化された手順を使用し、アンマウントが必要なチェックをスケジュールしてください。

同じ証拠セットを使用してインシデントを完了します。df -B1df -i、および特権付き du -x -B1 を再実行し、解放されたバイト数がクローズまたは削除された割り当てと予想されるオーバーヘッド内で一致することを確認します。アプリケーションによる新規書き込みを検証し、サービスが意図した名前付きファイルに書き込んでいることを確認し、レイテンシ、エラー、レプリカ、データの整合性をチェックします。その後、ブロックと inode の空き容量、削除済みで開かれたままのファイルの増加、ログローテーションの失敗、マウントトポロジの変更、予期しないスナップショットの増加に対してアラートを設定します。

質の高い模範解答

「78 GiB の差異が生じるのは妥当です。dfdu ではアカウンティングのスコープが異なるためです。df はファイルシステム全体のブロック統計を取得しますが、du は到達可能な名前付きエントリをトラバースします。まず、/varfindmnt でマッピングし、影響を受けるサービスのマウント名前空間で両方のツールを実行し、バイト単位、du -x、root 権限、ほぼ同時のサンプリングを使用して、これが実際のギャップであることを証明します。inode は 21% に過ぎないため、その可能性は一旦除外します。

最初の仮説は、削除済みで開かれたままのファイルです。lsof +aL1 /var を実行し、大きな結果それぞれの PID、ファイル記述子、デバイス、inode、所有者、増加傾向を確認します。ロガーが依然として不足している領域の大部分を保持している場合、サポートされている再オープンシグナルまたは制御されたグレースフルな再起動を使用します。サービスの所有者が影響範囲を確認するまで、プロセスの強制終了や /proc の切り詰めは避けます。記述子が閉じた後、df で期待される範囲が回収されたこと、およびアプリケーションが新しい名前付きログに書き込んでいることを確認します。

それでギャップが説明できない場合は、findmnt の出力を調査して別のマウントによってファイルが隠されているディレクトリがないか確認し、du の権限エラーを確認し、ファイルシステム固有のツールを使用してスナップショット、コピーオンライト割り当て、クォータ、メタデータを調査します。このボリュームは ext4 であるため、予約ブロックを調査します。予約ブロックは、合計から使用量を引いた値が 4 GiB あるにもかかわらずサービスが利用可能なバイト数が 0 である理由を説明できますが、78 GiB の使用済み領域のギャップを説明するものではありません。

最後に、元の測定を再実行し、失敗した書き込みパスをテストし、サービスの正常性とデータの整合性を確認し、どの割り当てが解放されたかを正確に記録して完了とします。再発防止策としては、ログローテーションやマウントのライフサイクルの修正、ブロックと inode の両方に関するアラート設定、ファイルシステムが緊急用予約領域に達する前のリンク解除・オープン状態の増加監視などが挙げられます。」

よくある間違い

  • 最も大きな可視ファイルをすぐに削除する → 必要なデータである可能性があり、不可視の割り当てを説明できない場合があります → データを変更する前に計測スコープを揃え、所有者を特定してください。
  • df /vardu / を比較する → ネストされたファイルシステムや異なるルートによって引き算が無効になります → 失敗しているパスをマッピングし、同一ファイルシステム上で du -x を使用してください。
  • du の権限エラーを無視する → 到達不能な名前付きファイルがあると合計が不自然に低くなります → 十分な権限で実行し、すべての診断メッセージを確認してください。
  • 異なるマウント名前空間でコマンドを実行する → 同じパス名が異なるファイルシステムに解決される可能性があります → 影響を受けるプロセスの名前空間で両方の測定値を収集してください。
  • 78 GiB のギャップを「予約ブロック」と呼ぶ → ext4 の予約は非特権ユーザーの空き容量に影響しますが、ギャップは使用済みブロックと到達可能な割り当てを比較したものです → それぞれの差を個別に計算してください。
  • すでに削除済みで開かれたままのファイルに対して rm を使用する → 最後の名前はすでに失われており、生存している記述子が inode を割り当てたままにしています → 所有プロセスに記述子をクローズまたは安全に再オープンさせてください。
  • 最初の修正として kill -9 を送信する → 突然の終了により作業が失われ、クリーンアップがバイパスされる可能性があります → サポートされている再オープン、リロード、グレースフルな再起動、またはフェイルオーバーのパスを優先してください。
  • 習慣的に /proc/PID/fd/FD を切り詰める(truncate) → 保持されたオフセットやファイル形式の前提により、新たな破損が発生する可能性があります → 切り詰めは、検証済みのセマンティクスを持つ承認された緊急事態のために取っておいてください。
  • 隠れたファイルを確認するためにビジー状態の子マウントをアンマウントする → 依存しているサービスが即座に失敗する可能性があります → トポロジを調査し、承認された代替ビューまたはメンテナンスウィンドウを使用してください。
  • inode 使用率をバイトギャップの一部として扱う → inode の枯渇は独立した ENOSPC のメカニズムです → df -i を確認し、大量のファイル数を個別に診断してください。
  • すべてのファイルシステムに ext4 のコマンドを適用する → スナップショットと割り当てのセマンティクスは XFS、Btrfs、ZFS、および overlay 層で異なります → FSTYPE を特定し、サポートされているツールを使用してください。
  • df が低下した後に作業を止める → ライターはまだ異常な状態であるか、誤ったターゲットにログを記録している可能性があります → アプリケーションの書き込み、名前付きファイル、データの整合性、および再発の兆候を検証してください。

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

フォローアップ 1:lsof が利用できない、またはプロセスが見えない場合はどうしますか?

ライターと同じ PID およびマウント名前空間で /proc/PID/fd を調査し、(deleted) で終わるシンボリックリンクを探し、デバイス、inode、リンクカウント、およびプロセスの所有権を確認します。権限、PID 名前空間、またはセキュリティポリシーによって隠されている場合、ホストの lsof はコンテナ化されたプロセスを見逃す可能性があります。/proc の切り詰めを次の自動的なステップにしないでください。復旧の決定はサービス固有のままです。

フォローアップ 2:逆に du が df より大きい場合はどうしますか?

まず du がネストされたファイルシステムに入り込んでいないか確認します。du -x はその要因を排除します。次に、ハードリンクの引数セマンティクス、見かけのサイズ(apparent-size)オプション、並行削除、コピーオンライトや圧縮のアカウンティングを確認します。du は選択されたディレクトリ階層を見積もり、df は 1 つのファイルシステムを報告するため、不一致の方向によって考えられるスコープエラーが変わります。

フォローアップ 3:Btrfs や ZFS では何が変わりますか?

パス名が削除された後もスナップショットやコピーオンライトの共有によってブロックが保持される可能性があるため、可視のファイルサイズだけでは不十分です。ファイルシステム自身の容量、サブボリュームまたはデータセット、スナップショット、クォータのビューを使用し、参照されている割り当てと排他的な割り当てを区別し、明示的な保持ポリシーに基づいてのみスナップショットを削除します。ext4 の予約ブロックの論理や tune2fs は適用できません。

フォローアップ 4:即座に復旧するために ext4 の予約パーセンテージを 0 に設定してもよいですか?

キャパシティと信頼性のレビューを行った後でのみ可能です。予約は特権プロセスを継続させ、断片化を減らすことができます。非特権ユーザーの空き容量を回復させることはできますが、使用済みブロックを削除するわけではなく、削除済みで開かれたままの割り当てを説明するものでもありません。まず既知の容量を解放または拡張し、その後、ボリュームの役割と運用ポリシーに従って予約を調整してください。

フォローアップ 5:削除済みで開かれたままの挙動を安全に再現するにはどうすればよいですか?

使い捨てのファイルシステムまたはテストホストを使用します。プロセスで大きなテストファイルを開き、記述子を開いたままそのパス名のリンクを解除し、dfdu を比較し、lsof +L1 で観察した後、記述子を閉じてブロックが戻ることを確認します。本番ボリューム上で実験を行ったり、実際のサービスの記述子を再利用したりしないでください。

フォローアップ 6:長期的なアラートは何を測定すべきですか?

ファイルシステムおよび名前空間ごとにセグメント化された、ブロックと inode の両方について枯渇までの時間(time-to-exhaustion)と最小ヘッドルームに対してアラートを設定します。削除済みで開かれたままの通常ファイル、ログローテーションの失敗、スナップショットの増加、マウントトポロジの変更について、ゲージや定期的なインベントリを追加します。アラートは、スコープの整合から始まり、破壊的な復旧の前に所有者の承認を必要とするランブックにリンクする必要があります。

公開情報ソース

関連する質問