【ITニュース解説】Jenkins Shared Library Structure for Reusable Pipelines
2026年10月23日に「Dev.to」が公開したITニュース「Jenkins Shared Library Structure for Reusable Pipelines」について初心者にもわかりやすく解説しています。
ITニュース概要
多くのJenkinsパイプラインで共通処理をコピペすると、修正が大変。Jenkins Shared Libraryは、共通処理を再利用可能な部品(ステップや関数)として一元管理する仕組みだ。vars/、src/などで構造化し、効率的なパイプライン構築と保守性向上に役立つ。ただし、間違った使い方に注意し、小さな単位で設計が肝要だ。
ITニュース解説
Jenkins Shared Libraryは、システム開発の現場で自動化を進める際に、Jenkinsというツールでパイプライン(一連の自動処理)を組む上で非常に重要な仕組みである。もし、あなたが複数のアプリケーションやサービスを開発していて、それぞれのプロジェクトで「Dockerイメージをビルドする」「デプロイする」といった、ほとんど同じ手順を何十回も記述している状況を想像してみてほしい。もしその共通の手順にセキュリティ上の修正が必要になったら、何十箇所ものファイルを一つ一つ手作業で変更し、レビューし、マージしなければならない。これは非常に手間がかかり、ミスも発生しやすい。Jenkins Shared Libraryは、このような「共通の処理の重複」という問題を解決するために生まれた。
このライブラリは、GitリポジトリとしてJenkinsに登録され、パイプラインの実行時にそのコードが読み込まれて利用可能になる仕組みだ。これにより、一度書いた共通処理を複数のJenkinsパイプラインから呼び出して使い回せるようになる。パイプラインからライブラリを使うには、Jenkinsfileの先頭で@Library('ライブラリ名@バージョン') _のように指定するだけでよい。
Shared LibraryのGitリポジトリ内には、特定の役割を持ついくつかのディレクトリが存在する。まずvars/ディレクトリには、パイプラインのカスタムステップやグローバル変数となるGroovyファイルが置かれる。例えば、buildImage.groovyというファイルにcall()メソッドを定義すると、JenkinsfileからbuildImage(...)という形でカスタムステップとして呼び出せるようになる。また、buildImage.txtというファイルを用意すれば、そのステップのヘルプテキストも表示可能だ。次にsrc/ディレクトリには、通常のGroovyクラスをパッケージ化して配置する。これはJavaプロジェクトのソースコードと同じような感覚で利用でき、純粋なロジックを記述するのに適している。そしてresources/ディレクトリには、Groovy以外のファイル、例えばシェルスクリプトのテンプレートや設定ファイルなどを保存し、libraryResourceという関数を使ってパイプラインから読み込める。test/ディレクトリは、Jenkinsにとっては特別な意味を持たないが、慣習的にこれらのライブラリコードの単体テストを記述する場所として使われる。
このライブラリのコードは、Jenkinsパイプラインエンジンの中で実行される。これは非常に重要なポイントで、Jenkinsfileのコードと同様にCPS(Continuation-Passing Style)という特殊な変換を受ける。そのため、shコマンドの実行時やユーザーからの入力待ちで一時停止しても、Jenkinsのコントローラーが再起動した後も処理を継続できるという利点がある。しかし、一般的なGroovyの書き方とは異なる動作をするケースもあるため、注意が必要だ。
しかし、この便利なShared Libraryも、使い方を誤るとかえってメンテナンスが困難になることがある。最も一般的な失敗は、ライブラリを「なんでも詰め込む場所」として扱ってしまうことだ。例えば、vars/standardPipeline.groovyという一つのファイルに、パイプラインの全ステージをハードコードし、チームごとの例外処理をブール値のパラメータで切り替えるような運用は良くない。結果として、このステップは数十個の引数を持つ巨大なものになり、どの引数の組み合わせが何を意味するのか誰も予測できなくなってしまう。
バージョンの扱いも重要だ。ライブラリを@Library('shared')のようにバージョンを指定せずにロードしたり、常にmainブランチのような最新版を指すようにしたりすると、ライブラリへの変更がすべてのパイプラインに即座に影響を与えてしまう。もしバグのあるコミットがマージされると、システム全体のビルドが停止するような大障害につながる可能性がある。
また、処理の実行場所にも注意が必要だ。大量のデータをパースしたり、大きなコレクションをループしたり、外部サービスにHTTP呼び出しを行ったりするような重い処理をGroovyコードで直接実行すると、それはJenkinsコントローラー上で実行される。コントローラーはすべてのパイプラインビルドで共有されるリソースであるため、このような重い処理が走ると、他のビルドのパフォーマンスにも悪影響を与えてしまう。重い処理は、可能な限りshやbatステップを使って、ビルドエージェント(実際にビルドが実行されるサーバー)上で実行するよう心がけるべきだ。
varsスクリプト内で、ステップ間でグローバルな状態を保持しようとすることも問題を引き起こしやすい。CPSのシリアライズ(処理状態を保存・復元する仕組み)と相性が悪く、特に一時停止を伴うステップをまたぐと、NotSerializableExceptionのようなエラーが発生する典型的な原因となる。また、ライブラリのリポジトリやリソースファイルに認証情報や秘密情報(APIキーなど)を直接記述することは厳禁である。リポジトリにアクセスできる人なら誰でもその情報を読み取れてしまうため、情報漏洩のリスクがある。Jenkinsの資格情報管理機能を使って、実行時に安全に情報をバインドすべきである。
テストの欠如も大きな問題だ。ライブラリの変更は多くのパイプラインに影響を与える可能性があるため、テストを怠ると、変更が直接本番環境のパイプラインでテストされることになり、重要なリリースが失敗する原因となる。
さらに、ライブラリの信頼スコープについても知っておく必要がある。Jenkinsのグローバル設定で登録されたライブラリは、Groovyサンドボックス外で動作し、暗黙的に信頼される。これは、そのライブラリがシステムのあらゆる操作を行えることを意味するため、悪意のあるコードが含まれると大きなセキュリティリスクにつながる。一方、フォルダレベルで設定されたライブラリはサンドボックス内で動作し、より制限された環境で実行される。ライブラリをグローバルスコープに配置する際は、そのリポジトリへの変更を誰が承認できるのかを厳しく管理することが不可欠である。
正しいアプローチとは、Jenkinsfileを「何をしたいか」という意図の宣言に徹し、Shared Libraryを「どうやって行うか」を定義した、小さく再利用可能なステップ群として設計することだ。巨大なテンプレートステップを作るのではなく、「Dockerイメージをビルドする(buildImage)」「イメージをスキャンする(scanImage)」「Helmでデプロイする(deployHelm)」といった、多くの狭い責任を持つステップを多数用意するべきである。そして、それらのステップを組み合わせて、必要に応じて「標準的なサービス構築パイプライン」といった意見のあるテンプレートをその上に構築するのは良い方法だ。
推奨されるディレクトリ構成は、jenkins-shared-lib/の直下に、vars/、src/、resources/、test/を配置する。vars/にはbuildImage.groovyのようなカスタムステップを置き、src/にはorg/example/ci/ImageRef.groovyのような純粋なロジックヘルパー(単体テストしやすい)を配置する。resources/にはorg/example/ci/trivy-args.txtのような、libraryResourceで読み込むファイルを置く。
例えば、入力値を検証し、重い処理はエージェントのshコマンドに任せ、呼び出し間で状態を持たないvars/buildImage.groovyのステップは次のようになる。まず、入力パラメータcfgにnameが含まれているか確認し、不足していればすぐにエラーを出す。その後、ImageRefのようなsrc/に定義されたヘルパークラスを使ってイメージの参照名を作成し、shコマンドでDockerイメージのビルドとプッシュを行う。このステップはイメージの参照名を戻り値として返すことで、次のステップでその値を利用できるようにする。
このステップを利用するJenkinsfileでは、必ず@Library('shared-ci@v2.4.0') _のように、固定されたタグやコミットIDでライブラリのバージョンを指定すべきである。これにより、ライブラリの意図しない変更がパイプラインに影響を与えることを防ぎ、パイプラインの安定性を保てる。ライブラリをリリースする際は、Gitのタグとセマンティックバージョニング(例: v1.0.0、v1.1.0、v2.0.0)を使い、ステップのシグネチャ変更のような破壊的変更はメジャーバージョンアップとしてリリースする。これにより、各チームは自身のスケジュールで安全にライブラリを更新できるようになる。
パフォーマンスについてもいくつか注意点がある。ライブラリを利用するすべてのビルドは、開始時にそのライブラリのGitリポジトリをチェックアウトする。小さなリポジトリであれば問題ないが、多くのビルドが走るbusyなJenkinsコントローラーでは、このチェックアウトの負荷が蓄積されることがある。ライブラリの設定には、取得したバージョンをコントローラー上でキャッシュするオプションもあるが、浮動するブランチ名と組み合わせるとキャッシュが最新のコミットに追従しない場合があるため、やはりタグの固定が推奨される。
より大きなコストは、通常、コントローラー上でのGroovyコードの実行だ。CPSモデルから導かれるルールとして、パース、フィルタリング、大量データ処理のような作業は、Groovyでループするのではなく、エージェント上のshやbatステップ(例えばjqを使ってJSONファイルを処理するなど)に任せるべきだ。また、非常に大きなマップやリストをステップの戻り値として使わない方がよい。これらはビルドの状態と共にシリアライズされるため、処理が重くなる。さらに、数十個の小さなsh呼び出しをするよりも、短いスクリプトを一つのshブロックで実行する方が効率的だ。各ステップはビルドレコードに簿記情報を追加するため、ステップ数が多いほどオーバーヘッドが増える。そして、パイプラインが必要とするライブラリだけをロードし、不必要な@Library指定は避けるべきだ。
最終的に、Shared Libraryの変更が「一つのステップ、一つの責任、一つのバージョンタグ」という原則で説明できない場合、そのライブラリの構造は、当初解決しようとした「コピペ問題」に逆戻りし始めている可能性が高い。Jenkins Shared Libraryを効果的に活用するためには、このようなベストプラクティスを理解し、適切に設計・運用することが重要である。