Jenkinsの一般的なパフォーマンスボトルネックとその修正方法

遅いJenkinsインスタンスに悩んでいませんか?この包括的なガイドでは、メモリリーク、ディスク容量の問題、過剰なログ出力など、Jenkinsの一般的なパフォーマンスボトルネックについて詳しく解説します。症状の特定、根本原因の理解、JVMチューニング、インテリジェントなビルド履歴管理、ログ最適化、効率的なパイプラインコーディングなどの実践的な解決策を学びます。CI/CDパイプラインをスムーズに稼働させ、ビルドの高速化、UIの応答性向上、全体的なソフトウェアデリバリープロセスの効率化を実現するための必須の監視ツールとベストプラクティスを紹介します。

Jenkinsの一般的なパフォーマンスボトルネックとその修正方法

遅いJenkinsインスタンスには、通常単一の原因はありません。UIが重く感じられ、ビルドがキューで待機し、エージェントが切断され、ログを開くのに永遠に時間がかかり、「Jenkinsがまたダウンした」と言う人が出ます。その苦情の背後には、多くの場合、コントローラのヒープ圧力、低速なディスク、過負荷のエージェント、プラグインの問題、パイプラインの不適切な動作、ソース管理やアーティファクトシステムへのネットワーク遅延など、いくつかの一般的なボトルネックのうちの1つが潜んでいます。

Jenkinsのパフォーマンスを修正する最速の方法は、コントローラの問題とビルドの問題を分離することです。ビルドが実行されていない場合でもJenkinsのUI、キュー、ジョブページが遅い場合は、コントローラから始めてください。UIは問題ないがビルドに時間がかかりすぎる場合は、エージェント、ワークスペース、キャッシュ、外部システムから始めてください。

コントローラのメモリとガベージコレクション

JenkinsコントローラはJavaプロセスです。ジョブ設定、プラグイン、ビルドメタデータ、キュー状態、Webリクエストに十分なヒープが必要です。ヒープが小さすぎると、コントローラはガベージコレクションに多くの時間を費やします。プラグインがメモリをリークしたり、メモリに大量のデータを保存したりする場合、ヒープを増やしても次のインシデントを遅らせるだけかもしれません。

症状には、UIの遅さ、長時間の一時停止、OutOfMemoryError、頻繁なエージェント切断、または利用可能なエグゼキュータと一致しないビルドキューの遅延が含まれます。

まずプロセスとログを確認します:

ps -o pid,rss,vsz,etime,cmd -C java
journalctl -u jenkins --since "2 hours ago" | grep -Ei 'OutOfMemory|GC overhead|heap|killed'

中程度のコントローラの場合、2〜4 GBのヒープで十分かもしれません。忙しいインストールではさらに必要になる場合があります。マシンのRAMの大部分をヒープに盲目的に設定しないでください。OSはファイルシステムキャッシュ、プロセスオーバーヘッド、監視エージェントにもメモリを必要とします。

典型的なサービスオプションは次のようになります:

JENKINS_JAVA_OPTS="-Xms1g -Xmx4g -XX:+UseG1GC"

JVMオプションを変更した後、メンテナンスウィンドウ中にJenkinsを再起動し、通常の負荷での動作を監視します。メモリが数日間着実に上昇し、決して安定しない場合は、ヒープダンプを取得し、最近更新またはインストールされたプラグインを確認します。プラグインは最新の状態に保ちますが、ロールバック計画なしに大規模なプラグインセットを更新しないでください。

ディスク容量とディスクI/O

Jenkinsは常にディスクを使用します。JENKINS_HOMEには、ジョブ設定、ビルドレコード、フィンガープリント、プラグインデータ、シークレット、ログ、そして時には多すぎるアーティファクトが保存されます。エージェントは、ワークスペース、依存関係キャッシュ、Dockerレイヤー、テストレポート、一時ファイルにディスクを使用します。

ディスクがいっぱいになるのは明らかです。ディスクが遅いと、何も壊れているようには見えず、すべてが待機しているだけなので、より厄介です。

容量とレイテンシの両方を確認します:

df -h
du -sh /var/lib/jenkins/* 2>/dev/null | sort -h | tail
iostat -xz 1

ビルド中に%utilとawait時間が高い場合、ディスクがボトルネックです。一般的な修正方法は、ワークスペースをより高速なストレージに移動する、古いDockerレイヤーを削除する、アーティファクトの保持期間を短縮する、レポートやパッケージのみが必要な場合にジョブがディレクトリ全体をアーカイブするのを停止することです。

ジョブにビルド破棄ポリシーを設定します:

options {
  buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10'))
}

JENKINS_HOMEの手動クリーンアップには注意してください。Jenkinsの実行中にランダムなXMLファイルやプラグインディレクトリを削除しないでください。Jenkinsの保持設定、プラグイン固有のクリーンアップツール、およびバックアップを使用してください。

コントローラでの作業過多

最も有害な設定の1つは、コントローラでビルドを実行することです。コントローラは、コンパイル、テストの実行、Dockerイメージのビルド、大規模なチェックアウトを行うべきではありません。

ほとんどのインストールでは、コントローラのエグゼキュータを0に設定します。ビルドはエージェントに配置します。すでにコントローラビルドを実行している場合は、徐々に移行し、ローカルツールパス、資格情報バインディング、コントローラにのみ存在するファイルなどの隠れた前提条件に注意してください。

また、コントローラに負荷のかかるGroovyコードがないかパイプラインを確認します。shなどのパイプラインステップはエージェントで実行されますが、Jenkinsfile内のGroovyロジックはコントローラで実行される可能性があります。巨大なファイルをGroovy変数に読み込んだり、巨大なマップを構築したり、パイプラインスクリプトで大規模なJSON処理を行ったりしないでください。大量のデータ処理には、エージェント上でシェル、Python、jq、またはビルドツールを使用してください。

エージェントの過負荷またはミスマッチ

特定のラベルのキュー時間が長い場合、汎用エグゼキュータを追加しても役に立ちません。linux && docker && large-memoryを必要とするジョブには、その正確な容量が必要です。

キューの理由とラベルの使用状況を確認します。次に、エージェントOSを確認します:

uptime
free -h
mpstat 1
iostat -xz 1
docker system df

エージェントがスワップしている場合は、エグゼキュータを減らすかメモリを増やします。CPUがピークに達し、ビジー期間中にビルド時間が増加する場合は、同時実行数を減らすかエージェントを追加します。I/O待機が高い場合は、キャッシュとワークスペースをより高速なストレージに移動するか、そのノードでの同時ジョブ数を減らします。

Kubernetesエージェントの場合、リソース要求はJenkinsのエグゼキュータ数と同じくらい重要です。CPUやメモリの要求が少なすぎるポッドは、すでにビジーなノードにスケジュールされ、Jenkinsは準備完了のエージェントを認識する一方でビルドが遅くなる可能性があります。使い捨てポッドの場合、1ポッドあたり1エグゼキュータの方が、同じコンテナを共有する複数のエグゼキュータよりも通常は理解しやすいです。

プラグインの問題

プラグインはJenkinsの強みの1つであり、パフォーマンス問題の一般的な原因でもあります。プラグインは、ページレンダリングコストを追加したり、ジョブの読み込みを遅くしたり、ビルド履歴を過剰に保持したり、通常のUI操作中に外部呼び出しを行ったりする可能性があります。

パフォーマンスが突然変化した場合は、最近何が変更されたかを尋ねます:

  • Jenkinsコアのアップグレード。
  • プラグインのアップグレード。
  • 新しいプラグインのインストール。
  • 新しいグローバル設定。
  • 新しいパイプラインライブラリバージョン。

「Jenkinsの管理」のヘルス情報、ログ、プラグインの変更ログを使用します。プラグインの更新後にUIが遅くなった場合は、ステージングコントローラがある場合はそこでロールバックをテストします。大規模なアップグレードの前に、JENKINS_HOMEとプラグインバージョンのバックアップを保持します。

「念のため」プラグインを保持しないでください。インストールされた各プラグインはメンテナンスの表面積を増やします。ジョブの依存関係を確認した後、未使用のプラグインを削除します。

SCMとアーティファクトリポジトリの遅延

多くの「Jenkinsが遅い」という報告は、実際にはGit、パッケージレジストリ、コンテナレジストリ、またはアーティファクトリポジトリの問題です。

ビルドログで繰り返される遅いステップを確認します:

git fetch
mvn dependency:resolve
npm ci
docker pull
docker push
archiveArtifacts

すべてのジョブが依存関係のダウンロードを待機している場合は、近くにプロキシまたはキャッシュを追加します。git fetchが遅い場合は、リポジトリサイズ、ブランチ検出、シャロークローン設定、エージェントからGitサーバーへのネットワークパスを確認します。エフェメラルエージェントでのDockerプルが遅い場合は、レジストリミラーまたはBuildKitレジストリキャッシュを使用します。

診断を正直に保ちます:Jenkinsは作業をスケジュールしますが、遠くのパッケージレジストリを高速化することはできません。

ログとビルド履歴の肥大化

大きなコンソールログはページレンダリングを遅くし、ストレージを消費します。通常のビルド中にすべてのテストフィクスチャ、すべてのHTTP応答、または完全なデバッグログを出力するジョブは、最終的にJenkinsの使用を苦痛にします。

まずジョブを修正します。通常のログの詳細度を減らし、詳細なログは必要な場合にのみ圧縮アーティファクトとしてアーカイブします。コンソール出力は、進捗状況と失敗のコンテキストに焦点を当てます。

次に保持期間を設定します:

options {
  buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '50'))
}

コンプライアンスが厳しい環境では、長期アーティファクトとログを、保持、検索、ライフサイクルポリシー用に設計された外部ストレージシステムに移動します。

実践的なインシデント対応手順

Jenkinsが今すぐ遅い場合、次の順序を使用します:

  1. コントローラUIが遅いかどうかを確認します。
  2. コントローラのCPU、メモリ、GC症状、ディスクを確認します。
  3. キューの理由と待機中のラベルを確認します。
  4. 最もビジーなエージェントのCPU、メモリ、ディスク、ワークスペースの増加を確認します。
  5. 最近のプラグイン、ジョブ、共有ライブラリの変更を比較します。
  6. 1つの遅いビルドログを読み、繰り返される高コストなステップを特定します。

この手順により、ランダムなチューニングを防ぎます。ヒープを増やしても飽和したDockerエージェントは修正できません。エグゼキュータを追加してもディスクがいっぱいになる問題は修正できません。ワークスペースを削除しても、コントローラの一時停止を引き起こすプラグインは修正できません。

Jenkinsを保守可能に保つ

健全なJenkinsインストールには退屈な習慣があります:コントローラエグゼキュータはゼロに設定、エージェントはワークロードに合わせてサイジング、ビルド保持は設定済み、依存関係キャッシュは意図的、プラグイン更新は追跡、基本的なメトリクスはPrometheus、Grafana、CloudWatch、またはチームがすでに使用している監視システムにエクスポートされています。

最善の修正は、多くの場合、小さく具体的です。Dockerビルドを専用エージェントに移動します。ノイズの多いジョブのログ出力を削減します。Mavenプロキシを追加します。スワップしているノードのエグゼキュータを減らします。未使用のプラグインを削除します。何年もすべてのビルドを保持しているジョブに保持期間を設定します。

Jenkinsのパフォーマンスは、1つのブラックボックスとして扱うのをやめ、キューからコントローラ、エージェント、ファイルシステム、ネットワーク依存関係、そしてビルドログへと作業を追跡し始めると改善します。

例:ビルドは遅いがJenkinsは正常

開発者がプルリクエストチェックに25分かかると報告したとします。Jenkins UIは応答性が高いです。キューは短いです。エージェントはオンラインです。遅いログは次のことを示しています:

git fetch: 20秒
npm ci: 9分
単体テスト: 4分
docker build: 10分
アーティファクトのアーカイブ: 1分

これは主にJenkinsコントローラの問題ではありません。考えられる修正は、パッケージキャッシング、Dockerfileレイヤーの順序付け、BuildKitキャッシュ、そしておそらくテストの分割です。コントローラのヒープを増やしても何も変わりません。

例:すべてが待機しているがエージェントはアイドル状態

エージェントがアイドル状態に見えるのにジョブがキューに入れられている場合、キューの理由を読みます。ジョブがlinux && dockerを必要とする一方で、アイドル状態のエージェントはlinuxしか持っていないことがわかるかもしれません。または、ジョブがdisableConcurrentBuilds、ロック可能なリソース、または一致するエージェントのプロビジョニングに失敗しているクラウドプラグインによってブロックされている可能性があります。

その種のボトルネックは設定であり、単なる容量の問題ではありません。一致しないエージェントを2つ追加しても役に立ちません。

例:コントローラが毎日午後に遅くなる

UIが毎日同じ時間に低下する場合、スケジュールされたジョブを探します:ブランチインデックス、バックアップ、大規模なアーティファクトクリーンアップ、脆弱性スキャン、または夜間パイプラインが早く開始されすぎている場合。その時間帯のコントローラCPU、ヒープ、ディスクI/Oを確認します。また、0 2 * * *のようなcron式により、多くのジョブがまったく同じ分に開始されていないか確認します。

Jenkinsのスケジュールでは、可能な場合はハッシュ化されたタイミングを優先します:

H 2 * * *

これにより、すべてを正時に開始するのではなく、ジョブが分散されます。

適切な監視が答えるべきこと

最低限、監視はサーバーにログインしなくても次の質問に答える必要があります:

  • コントローラプロセスは生きていて応答性があるか?
  • ヒープはどのくらい使用され、ガベージコレクションはどのくらいの頻度で実行されているか?
  • ジョブはラベルごとにキューでどのくらい待機しているか?
  • どのエージェントがオフラインか、または繰り返し再接続しているか?
  • コントローラとエージェントのディスクはほぼ満杯か?
  • 同じジョブのビルドが時間の経過とともに遅くなっているか?

完璧なダッシュボードは必要ありません。ディスク、ヒープ、キュー長、エージェントの可用性に関するいくつかのメトリクスとアラートだけでも、開発者が報告する前に多くの障害をキャッチできます。