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

ログから原因を見つける

障害が起きた日のログはすでに消えていた

TT Labで続きを見る

目標

ローテーションされたログの束から実際の保管区間を測り、事故の区間がその範囲外であることを数字で明らかにし、圧縮率を実測して保存日数と容量を計算し、ローテーションのポリシーとして書きます。最後に、レート制限にかかって、そもそも記録されない行を数えます。

なぜ重要なのか

調査の初日に最もよくぶつかる壁は、難しい質問ではなく、空のディレクトリです。そのとき必要なのは、嘆きではなく、3つの数字です。何日足りなかったか、あと何日分残すべきか、その値がディスクのバジェットに合うか。3つの数字は、すべて今あるファイルから測れます。そして、「ログがない」には2つの意味があります。ローテーションで消えたか、レート制限にかかって、そもそも記録されなかったか。対策がまったく違うので、2つを見分ける必要があります。

ステップ

  1. /root/keep/gen_keep.pyを作成して実行し、/root/keep/var/log/の下に、保管状態を再現してください。
  2. /root/keep/order.jsonに、ローテーション済みファイルを古いものから並べた一覧と、そのルールを書いてください。
  3. /root/keep/coverage.jsonに、ファイルごとの行数と、最初の行・最後の行の時刻を書いてください。
  4. /root/keep/window.jsonに、事故の区間が保管範囲の中か、どれだけ足りなかったかを書いてください。
  5. /root/keep/budget.jsonに、圧縮率の実測値と、保存日数・容量の計算を書いてください。
  6. /root/keep/logrotate.confに、ローテーションのポリシーを書いてください。rotateの値は、ステップ5の計算と同じである必要があります。
  7. /root/keep/ratelimit.jsonに、レート制限にかかって捨てられる行数を数えて書いてください。
  8. /root/keep/keep_report.mdに、4つの節で報告書を残してください。

参考

顧客企業の保管状態を再現する

/root/keep/gen_keep.pyを作成して実行し、/root/keep/var/logディレクトリの下に、payments.log(720行)・payments.log.1(2000行)・payments.log.2.gz・payments.log.3.gz・old/payments.log-20260407.gz・old/payments.log-20260408.gz・burst.ndjson(360行)を作ってください。

ローテーションの名前が2種類混ざっている状態を作ります。番号が付いたものと日付が付いたもの、圧縮されたものとされていないものが、一緒にあって初めて、このラボが成立します。圧縮ファイルは、Pythonのgzip.GzipFileでmtime=0を与えて書けば、作り直しても同じファイルになります。

ローテーション済みファイルを古いものから並べる

/root/keep/order.jsonに、order(ファイルのパスを古いものから入れた配列で、/root/keep/var/log/を除いた相対パス)とrule(そのように並べたルールを1文で)を書いてください。

番号が付いた名前と日付が付いた名前は、ソートの方向が互いに逆です。一方は数字が大きいほど過去で、もう一方は名前が小さいほど過去です。今書いているファイルが最も新しいものです。ファイルを開かずに名前だけで並べてみて、次のステップで内容で確認します。

ファイルごとに実際に含まれる区間を測る

/root/keep/coverage.jsonに、ファイルごとにfile・lines・first_ts・last_tsを入れたオブジェクトを、古いものから配列で書いてください。時刻は、ログの行の最初の欄をそのまま使います。

ファイル名はうそをつくことがあります。ローテーションが失敗したり、人が手で移したファイルが混ざったりすると、名前の順序と内容の順序がずれます。そのため、内容でもう一度測ります。圧縮ファイルは、zcatで読むか、Pythonでgzip.open(path, 'rt')で開きます。

事故の区間が範囲外であることを数字で明らかにする

/root/keep/window.jsonに、oldest_retained・newest_retained・incident_start・incident_end・incident_covered(trueかfalse)・short_by_seconds(整数)・extra_rotations_needed(整数)を書いてください。extra_rotations_neededは、足りない秒を1日で割ったあと、切り上げた値です。

事故の区間は、指示文の参考の節に書かれています。足りない秒は、保管されている最も古い時刻から、事故の開始時刻を引いた値です。1日単位でローテーションしているので、何日分多く残すべきだったかは、切り上げで求めます。

圧縮率を実測して保存日数を計算する

/root/keep/budget.jsonに、sample_raw_bytes_per_day・sample_gz_bytes_per_day・compression_ratio・prod_raw_bytes_per_day・prod_gz_bytes_per_day・required_bytes_for_30_days・fits_in_budget・max_days_in_budget・rotate_valueを、指示文の計算ルールのとおりに書いてください。

圧縮率は目分量で使わず、この顧客のファイルで測ります。アクセスログとスタックトレースでは、縮む度合いが違います。delaycompressのために、直近の2日分は原本のサイズで見積もる必要があり、logrotateのrotateが現在のファイルを除いた個数であることが、この計算の落とし穴です。

計算した値でローテーションのポリシーを書く

/root/keep/logrotate.confに、/root/keep/var/log/payments.logのブロックを書いてください。daily・rotate <5단계의 rotate_value>(プレースホルダーはステップ5のrotate_valueです)・compress・delaycompress・missingok・notifempty・dateext・create 0640 root admが必要で、copytruncateは入れません。

rotateの値は、ステップ5で計算した、その数字です。保存日数と同じ値ではありません。copytruncateを外す理由は、マニュアルに書かれています。コピーと空にする処理の間の短い隙間に書かれた行が消えるからで、それは調査で最も惜しい瞬間に消えます。

そもそも記録されない行を数える

/root/keep/ratelimit.jsonに、interval_sec・burst・windows_over_limit・dropped_total・dropped_by_service(サービス名をキーにしたオブジェクト)・worst_window(window_start・service・dropped)を書いてください。1つの区間で捨てられる行はcount - burstで、負なら0です。

journaldのレート制限は、サービスごとに別々に適用され、1つの区間で上限を超えると、その区間の残りがすべて捨てられます。保管期間をどれだけ延ばしても、この損失は残ります。worst_windowは、捨てられた行が最も多い区間1つです。

何を変えるかを文章に残す

/root/keep/keep_report.mdに、## 무엇이 없었나、## 지금 보관 정책은 무엇인가、## 얼마를 남겨야 하는가、## 무엇을 바꾸기로 했나(韓国語の見出しは、順に「何がなかったか」「今の保管ポリシーは何か」「いくら残すべきか」「何を変えることにしたか」という意味です)の4つの節で書いてください。足りなかった秒・rotateの値・バジェットの中で可能な日数・レート制限で捨てられる行数を、数字で含める必要があります。

この報告書を読む人は、ディスクのバジェットを握っている人です。「ログをもっと残してください」ではなく、「1日Nバイト、30日でMバイト、バジェットに収まります」と言って初めて、決定が下されます。レート制限による損失は、保存とは別の対策が必要だという点も、一緒に書いてください。