ある月曜の朝、Jenkins画面を開いたら、妙に読み込みが遅かったです。

「あれ、今日はなんか重たいな」と思いながらReplayボタンを押すと、ビルドが走り始めたものの、しばらくして fatal: write error: Bad file descriptor というエラーで止まってしまいました。Gitのクローンに失敗しています。普段なら数秒で終わるはずの処理が、転送速度が77KB/s → 51KB/sとじわじわ下がっていき、最終的にタイムアウト。

504 Gateway Time-outも頻発します。ALB経由でJenkinsにアクセスしているのですが、60秒待ってもレスポンスが返ってきません。

「これは何かがおかしい」

そう思ったときから、約3時間の調査が始まりました。


最初の仮説:ネットワークか、一時的な問題か?

エラーログを見ると、Gitのクローン処理でI/Oエラーが出ています。最初に思い浮かんだのは「ネットワークの一時的な問題では?」という仮説でした。転送速度が徐々に低下していることから、接続が不安定になっているのかもしれません。

しかし、よく考えると違います。

エラーログをもう一度見直すと、pipeline-groovy-lib プラグインがShared Libraryを取得中に失敗しています。これはController側で実行される処理です。Jenkins全体が重いということは、Controller側のリソースが圧迫されている可能性が高いです。

「なるほど、Controller側の問題かもしれない」

ここで方向転換しました。CloudWatchのメトリクスを確認することにしました。


CloudWatchメトリクスを読み解く

まずはEC2インスタンスのメトリクスから確認しました。

CPU使用率のグラフ - 通常0-5%で推移し、たまに30%前後のスパイクがある程度

CPU、ネットワークには目立った異常はありません。

次にディスクのメトリクスを見ます。

ディスク読み書きレイテンシのグラフ - 書き込みは0.7秒程度、読み込みはほぼ0秒で安定

一瞬、「01/24のスパイクが怪しい」と思いましたが、よく見ると現在は落ち着いています。これだけでは説明がつきません。

「ディスクI/Oの問題なのは間違いないが、EBSのメトリクスでは決定的な証拠が見つからない...」


EBSか、EFSか ― 見えない容疑者

ただ、ここで一つ疑問が浮かびました。

「待てよ、このJenkins、EBSじゃなくてEFS使ってるんだった」

念のためEBSのメトリクスも確認してみましたが、特に問題はなさそうでした。一方、EFSのメトリクスを開いてみると――

EFSスループット利用率のグラフ - 00:00〜03:00頃に100%に達するスパイクが頻発している

EFSタイプ別IOPSのグラフ - メタデータ操作(緑)が支配的で、データ操作(青・オレンジ)は比較的少ない

EFSストレージバイト数のグラフ - 合計(オレンジ線)が00:00頃から増加傾向

「これだ」

EFSがスループット制限に達しています。しかも、データの読み書きではなく、メタデータ操作でIOPSを食いつぶしています。そして、ストレージ容量も増加し続けている。

正直ここで詰まりました。

容量自体は17GB程度で、特別大きいわけではありません。にもかかわらず、スループットが100%に達するスパイクが頻発するのはなぜか。メタデータIOPSとは具体的に何を指しているのか。


メタデータIOPSとは何か

調べてみると、EFSにおける「メタデータ操作」とは以下のようなものを指すようです。

つまり、小さいファイルを大量に作成・削除・確認する処理がメタデータIOPSを消費します。

Jenkins環境でこれに該当するものは何でしょうか。

これらはいずれも、多数の小さいファイルで構成されています。特にGitリポジトリは、.git/objects 配下に膨大な数のファイルを持ちます。

「なるほど、容量の問題じゃなくて、ファイル数の問題か」


容疑者を絞り込む

EFSのストレージ容量が増加傾向にあることから、「何かが蓄積されている」と考えました。

まずは /mnt/efs/jenkins の各ディレクトリのサイズを確認してみました。

du -sh /mnt/efs/jenkins/*

結果:

342M    plugins
106M    war
332M    logs
373M    caches
timeout jobs       # タイムアウト
174M    fingerprints
timeout config-history

jobs ディレクトリでタイムアウト。これは怪しいです。

しかし、ここで気づいたことがあります。

plugins:         342M
war:             106M
logs:            332M
caches:          373M
fingerprints:    174M
合計:            約1.3GB

残り約15.7GBが未発見

EFSの合計容量は17GB程度あるのに、確認できたのは1.3GB程度。どこかに大きなファイルやディレクトリが隠れています。


応急処置:プロビジョンドスループットの設定

調査が長引きそうだったので、ここで一旦判断をしました。

「調査中もJenkinsは動かし続けたい。EFSのスループットを上げておくのが最適だろう」

EFSには以下の2つのスループットモードがあります:

当時の設定はバーストモードでしたが、これをプロビジョンドスループット300 MiB/sに変更することにしました。

ただし、EFSのスループットモード変更には制限があり、すぐには設定できないことがわかりました。翌日に設定変更することにしました。

なぜプロビジョンドスループットを選んだか。

  1. 安定した性能 → メタデータIOPSのスパイクに対して予測可能な性能
  2. 調査中も安心 → findやduを実行してもスループット保証
  3. 即効性 → バーストモードのクレジット回復を待つ必要がない

この判断が正しかったかどうかは、翌日の設定変更後に確認することになります。

ただし、これはあくまで応急処置です。根本原因は解決していません。


真犯人の発見:Git一時ファイルの残留

さらに調査を続けました。

大きなファイルを直接探してみることにしました。

find /mnt/efs/jenkins -type f -size +100M 2>/dev/null -ls

すると――

125829120 Jan 27 01:50 /mnt/efs/jenkins/jobs/Seed_Jobs/jobs/0_Seed_Job_Orchestrator/builds/873/libs/.../root/.git/objects/pack/tmp_pack_S3GPJw
122683392 Jan 27 01:50 /mnt/efs/jenkins/jobs/Seed_Jobs/jobs/0_Seed_Job_Orchestrator/builds/872/libs/.../root/.git/objects/pack/tmp_pack_c4EAwd

**tmp_pack_***という一時ファイルが大量に残っていました。

各ファイル100-300MB。今日(Jan 27)のタイムスタンプが多数ありました。

「これか」


何が起きていたのか

整理すると、こういうことでした。

  1. Pipeline Shared Libraryのチェックアウト時

    • JenkinsがShared Libraryをgit cloneします
    • Gitがpackファイルを作成する際に一時ファイル(tmp_pack_*)を生成します
    • EFSのIOPS制限でタイムアウト
    • 一時ファイルが削除されずに残ります
  2. 毎回のビルドで同じ現象

    • 毎日23:12頃に実行されるビルドで発生
    • 各ビルドに200-300MBのゴミが残ります
    • これが数十ビルド分蓄積 = 約15GB
  3. 悪循環

    EFS遅い → Git失敗 → ゴミ蓄積 → さらにEFS遅い
    

なるほど、だから今日急激に症状が表面化したわけです。


対処と残タスク

原因が特定できたことで、緊急度は下がりました。そこで、まずは再発防止策として定期クリーンアップジョブの作成に着手しました。

実施した対応

定期クリーンアップジョブの作成

残タスク

原因が判明し、再発防止策を講じたことで一旦は落ち着きましたが、以下のタスクが残っています。

  1. 一時ファイルの削除

    systemctl stop jenkins
    find /mnt/efs/jenkins -name "tmp_pack_*" -type f -delete
    systemctl start jenkins
    
    • 既存の約15GBのゴミファイルをクリーンアップ
  2. 監視アラートの設定

    • EFSスループット利用率が75%を超えたらアラート
    • 早期発見のための仕組み

今回の調査を通じて、いくつか気づいたことがありました。

気づき1: 容量だけ見ていても見えない問題がある

「容量が少ないから問題ない」と思っていましたが、実際には小さいファイルの大量蓄積が性能劣化の原因でした。容量以上に、ファイル数・メタデータ操作の頻度が重要なんだと実感しました。

気づき2: バーストモードは予測しづらい負荷に弱い

EFSのバーストモードは、クレジットを使い切ると性能が低下します。今回のように「いつの間にか」蓄積される問題だと、いつクレジットが枯渇するか予測しにくいです。

気づき3: 調査中も運用を継続できる余地を残しておく

プロビジョンドスループットへの変更判断は、調査中もJenkinsを安定稼働させるための保険でした。「調査が長引くかもしれない」という前提で先手を打っておくことの大切さを感じました。


振り返り

最初は「ネットワークの一時的な問題だろう」と思っていました。

でも実際には、ネットワークでもディスク容量でもなく、メタデータIOPS、しかもGitの一時ファイルという予想外のところに原因がありました。

仮説を立てては検証し、違うと分かったら別の仮説に切り替える。メトリクスを読み解きながら、一歩ずつ真相に近づいていく。正直ここで詰まりましたというポイントもありましたが、最終的に原因を特定できたときは「ああ、そういうことか」とすっと理解できました。

表面的な症状(Gitクローンエラー)の奥に、より深い原因(EFSメタデータIOPS)があって、さらにその奥に真犯人(Git一時ファイルの蓄積)がいる。

一つ一つ丁寧に仮説を立てて検証していく。その積み重ねが大事なんだなと、改めて感じた調査でした。


今後の対応

今回の問題では、EFSの容量が増加していることには気づけましたが、何が根本原因かをすぐに特定できませんでした。

tmp_pack_*削除ジョブは既に作成済みですが、今後同様の問題を早期発見できるように、EFS使用量の可視化を進めていく予定です。

具体的には、定期的にディレクトリ別の使用量を監視し、レポートとして可視化することで、容量の異常な増加を早期に検知できる仕組みを構築します。これにより、問題が表面化する前に対処できるようになります。


関連書籍

今回のようなインフラ運用やトラブルシューティングについてさらに学びたい方におすすめの書籍です。

[📦 商品リンク: moshimo-book-sre-google]

[📦 商品リンク: moshimo-book-jenkins-jissen]

[📦 商品リンク: moshimo-book-aws-operations]