TT Lab
はじめる
学ぶ 学習パス コース

CPU・メモリリークの見極め

ディスクとファイルディスクリプタ

TT Labで続きを見る

一言でいうと

dfに余裕があるのに書き込めないときは、inodeが尽きたか、削除されたファイルを誰かが開いたままにしています。どちらもdf -hでは見えません。

なぜ必要なのか

ディスク関連の報告で最もよく出る2つが、これです。

inodeの枯渇。ファイル1つ1つがinodeを1つずつ使います。小さなファイルが数百万個あると、容量は余っていても、inodeが先に尽きます。df -iで見ないと見えません。

削除されたのに開かれたままのファイル。ログファイルをrmしたのに、プロセスがまだそのファイルを開いていると、Linuxはリンクだけを消して、実際のブロックは返しません。dfは相変わらず満杯だと言い、duはファイルがないと言います。この不一致が、決定的な手がかりです。

# du 와 df 가 다르면 이걸 본다
ls -l /proc/*/fd 2>/dev/null | grep deleted

解決は、プロセスにファイルを開き直させることです(ログならlogrotateのcopytruncateか、デーモンへのSIGHUP)。プロセスを再起動してもかまいません。

どう動くのか

ファイルディスクリプターのリークは、じわじわと死にます。

ls /proc/<PID>/fd | wc -l              # 지금 몇 개 열었나
cat /proc/<PID>/limits | grep 'open files'   # 한도

この数字が時間とともに単調に増加していれば、閉じないコードがあります。上限に達した瞬間、Too many open filesでリクエストがすべて失敗します。上限を上げるのは時間を稼ぐことで、直すことではありません。

よくある勘違い

ulimit -nが無制限なら安全だという勘違い。コンテナのデフォルトのRLIMIT_NOFILEは事実上無制限(10億規模)ですが、古いデーモンの中には、起動時にその数字の分だけ接続テーブルを確保しようとして、メモリを使い切って死ぬものがあります。無制限が常によいとは限りません。

ディスクが遅いかを判定する2つの数字

iostat -x 1の多くの列のうち、実際に使うのは2つです。

Device  r/s   w/s  rkB/s  wkB/s  r_await  w_await  aqu-sz  %util
nvme0n1 120  380   4800  15200     0.31     0.52    1.24    38.2

%utilを信用しないでください。この値は「リクエストが1つでも進行中だった時間の割合」なので、並列処理ができるNVMeでは、100%でも余裕が残っています。回転ディスクの時代の指標です。

どのプロセスがディスクを使っているか

# 실시간으로 I/O 상위 프로세스
iotop -oPa            # -o: 실제로 I/O 하는 것만, -a: 누적

# iotop 이 없을 때 (컨테이너에서 흔하다)
cat /proc/<PID>/io    # read_bytes, write_bytes 를 두 번 읽어 차이를 낸다

/proc/<PID>/ioのread_bytesは実際のディスクから読んだ量で、rcharは読み取りのシステムコールで渡した量です。2つの差が大きければ、ページキャッシュがよく効いているという意味で、良いサインです。逆に2つが近ければ、キャッシュが効かず、毎回ディスクを叩いています。

コンテナでI/Oを制限する

CPU・メモリと違い、KubernetesにはI/Oの制限フィールドがありません。cgroup v2にはio.maxがありますが、Podのスペックとしては公開されていません。そのため、実務の対応は違います。

ioniceは、CFQ・BFQスケジューラーでしか効きません。最近のNVMeのデフォルトであるnone(マルチキュー)では何の効果もないので、cat /sys/block/nvme0n1/queue/schedulerで先に確認します。

実務で本当に大切なこと

ログローテーションの設定で、createとcopytruncateの違いが、ここに関わります。

どちらもタダではありません。アプリケーションがSIGHUPを処理できるならcreate、できないならcopytruncateを使い、失われる可能性があることを承知しておきます。