Jibを使ってJavaコンテナイメージをビルドする
2021年8月17日
0 分で読めますコンテナイメージを少しでも扱ったことがあれば、イメージを構築する手順をレイヤーごとに記述した、よく知られたファイル、Dockerfileをご存じでしょう。Dockerfileを使わずにOCI準拠のイメージをビルドできるツールが増えていることをご存じですか?この記事では、Dockerfileを正しく記述する手間をかけずに最適化されたイメージをビルドできる、100% JavaベースのツールJibを紹介します。
イメージの構築方法とDockerfileの基本はすでにご存じだという前提で進めます。Dockerfileを初めて使う方は、コンテナイメージのビルドについて詳しく解説した、私の開発者主導のワークフローに関する記事をご覧ください。
課題
Java開発者であれば、アプリケーションのコードベースやライブラリの依存関係に加えて、対象とするJDKやJVMも把握していることでしょう。ベースにするWebアプリケーションサーバーのバージョンや、ガベージコレクターのチューニング、データベース接続などのランタイム設定も含まれるかもしれません。一方、これまで対応する必要がなかったのは、パッケージのインストール、ファイルシステムの権限、実行時に使うUID/GIDといったOSレベルの懸念事項や、その他の設定詳細ではないでしょうか。こうした設定は従来、プラットフォームを提供する運用チームやセキュリティチームが担当していました。
コンテナランタイムやその上で動作するオーケストレーションシステムの普及に伴い、こうした低レベルの懸念事項の多くが、アプリケーションやサービスのコードリポジトリに含まれるInfrastructure as Code(IaC)の設定ファイルという形で、開発チームの担当範囲に移りつつあります。これらのファイルには、KubernetesのYAML、TerraformのHCL、CloudFormationのJSON、そしてもちろん、アプリケーションの実行環境となるコンテナイメージの構造を定義するDockerfileなど、さまざまな形式があります。
アプリケーションをイメージにパッケージ化してコンテナで実行すること自体は、通常それほど難しくありません。しかし、イメージの構成が適切で、最適化され、安全であり、組織の標準に準拠していることを確認するのは簡単ではありません。開発者には、OSレベルのスキャンツールやイメージのラベル付け標準、最新のDockerfileのベストプラクティスなど、担当すべきことが次々と増えています。HadolintやDockle、そして当社のSnyk Containerなどの静的解析・スキャンツールは、こうした作業に役立ちます。では、こうした新たな要件や定型作業の大半、あるいはすべてを自動化できるとしたらどうでしょうか?
Jibの登場:100% Javaベースのコンテナイメージビルドツール
Jibは、Dockerfileもコンテナランタイムも不要で、OCI(Docker v2)準拠のコンテナイメージをビルドできる、オープンソースの100% Javaツールです。単体のCLIツールとしても利用できますが、一般的にはMavenまたはGradleのビルドステップ用プラグインとして使います。プラグインを使えば、Javaの成果物であるmvnやgradleのビルドコマンドを実行するだけで、.jar、.warなどの成果物に加えて、コンテナでアプリケーションをすぐに実行できるOCIイメージをビルドし、必要に応じてデプロイできます。
Spring BootアプリケーションにJibを追加するには、Mavenのpom.xmlファイルにこのXMLを記述し(ドキュメント全文はこちら)、mvn packageコマンドを再実行するだけです。ここではコンテナイメージの配置先をいくつか指定できます。この例では、後で実行に使うイメージレジストリにデプロイしますが、ローカルの**.**tarファイルに出力したり、Dockerデーモンが稼働していてアクセス可能な場合は、Dockerのローカルイメージキャッシュに保存したりすることもできます(Dockerでビルドした場合と同様です)。
注:上記の出力には興味深い「警告」行がいくつかあります。これについては後ほど説明します。
コンテナランタイムがインストールされたマシンで、次のコマンドを実行してコンテナを起動します:docker run --rm -it -p 8080:8080 myimage:tag
Gradleをお使いの場合は、build.gradleファイルで同じことを行う方法について、公式ドキュメントをご覧ください。
これだけです。イメージをビルドするのにDockerfileや追加のツールは必要ありません。Javaの成果物のビルドに使っているツールだけで済みます。開発者の負担が軽くなるだけでなく、Dockerソケットを公開したり、他のビルドツールを管理したりする必要がないため、CIビルドエージェントのサポートも大幅に簡単になります。
イメージの詳細を見る
イメージのビルドは簡単になりましたが、私が初めて見たときと同じように、こんな疑問が浮かぶかもしれません……
Jibはどのようにイメージをビルドするのか?
他のイメージビルドツールと同様に、JibはJavaランタイム、アプリケーション、そのすべての依存関係を含むファイルシステムレイヤーと、コンテナエンジンがJVMの起動方法を把握するためのメタデータを作成します。
この例のレイヤー情報を、docker image historyコマンドで確認してみましょう:
最初の4つのレイヤーのCREATEDの日付がすべて「51年前」と表示されていることに注目してください。これは、同じコードベースからのビルドでまったく同じレイヤーハッシュを生成することを目指す、Jibの再現可能なビルド戦略によるものです。詳しくは、FAQをご覧ください。
レイヤーのコメントを見ると、AdoptOpenJDKのデフォルトのベースイメージ(詳細は後述)から複数のレイヤーがあり、その後に次のレイヤーが続いていることがわかります:
Maven/Gradleファイルで定義されたライブラリの依存関係
リソースファイル
コンパイル済みアプリケーションコードのクラスファイル
Java JVMの引数ファイル
オープンソースツールdiveを使って、さらに詳しく各レイヤーに含まれるファイルを見てみましょう:
依存関係
このレイヤーには、すべての.jarファイルが/app/libsフォルダーに追加されています。
リソース
ここには/app/resourcesと、リソースフォルダー内の関連ファイルがすべて含まれています。
クラス
ビルドのコンパイルフェーズで生成された.classファイルは、このレイヤーの/app/classesにあります。
JVM引数ファイル
最後に、コンテナ内でJVMを起動する際に使う引数を含むファイルがいくつか、最上位の/appフォルダーにあります。この例では、これら2つのファイルの内容を確認すると、ランタイムのクラスパスとメインクラスの情報がわかります。
注:Maven/Gradleビルドの構成によっては、Jibで生成したイメージにさらにレイヤーが含まれる場合があります。その他のレイヤーについては、Jib FAQをご覧ください。
Jibが使用するベースイメージは?独自のイメージを使う必要がある場合は?
デフォルトでは、JibはJARファイルのビルドにDockerHub公式のadoptopenjdk:jre-8ベースイメージを、WARのビルドにはDockerHub公式のjettyベースイメージを使います。プラグイン設定で変更できます。必要に応じて独自のENTRYPOINT, USERなどを設定する方法を含め、詳細は公式ドキュメントをご覧ください。同じドキュメントでは、独自のベースイメージを設定し、特定のハッシュを使ってビルドの再現性を確保することが望ましい理由も説明しています。多くの組織では、セキュリティチームや運用チームが監査・強化した、開発チームが使用を義務付けられている「承認済み」のベースイメージが社内に用意されています。Mavenのpom.xmlを変更してそのようなイメージを使う例を紹介します。
標準化されたイメージラベルなど、その他の設定にも対応しています。たとえば、組織で次のラベルをすべてのイメージに含めることが求められているとします:
ソースコードのGit URL
GitのコミットID/ハッシュ
Mavenプロジェクトのビルドバージョン
pom.xmlからこれらのデータにアクセスできる場合、ラベルを追加するJibプラグインの設定はとても簡単です:
作成したイメージを確認すると、DockerfileのLABELS行で追加した場合と同じようにラベルが適用されています(ここでは便利なコマンドラインツールjqを使って、応答から該当する配列だけを取り出しています)。
こうした標準設定の複雑さをすべて、<pluginManagement>やその他の一般的なMaven設定を使って親POMで定義することを想像してみてください。開発者は定型的なXMLを気にしたり、目を通したりする必要がなくなり、アーキテクトは各チームに負担をかけずに、組織全体の標準を適用し、更新できるようになります。
セキュリティを確保するには?
Dockerfileのような汎用的なイメージビルドツールを使う際の課題の1つは、パッケージのインストールやADDによる不特定のWebサーバーからのファイルのダウンロードなど、ビルドの過程でほぼ何でもできてしまうことです。そのため、レビューして精査が必要な手順が数多く発生します。Jibでは、こうした選択の多くが自動的に適用されます。また、上書きするには、依存関係やその他のビルド設定の変更と同じように、コードレビューで明確にわかるMaven/Gradle設定の変更が必要です。
セキュリティスキャンに関して、Snyk Containerの優れた機能の1つは、脆弱性の少ないベースイメージを推奨できることです。現在では、イメージのベースを特定するためのDockerfileがなくても、この機能を利用できます。つまり、JibやDockerfileを使わない他のツールでビルドされたイメージも、同じ推奨を活用できます。
ご自身のJavaコンテナをスキャンして試してみたい場合は、無料アカウントを作成し、Snykのスキャンツールのインストール手順をご確認ください。
この例では、ベースイメージをopenjdk:8u121-jreに設定してmvn packageを実行し、イメージをノートパソコンにプルしました。次にsnyk container testを実行してスキャンします。
ご覧のとおり、スキャンではベースイメージとしてopenjdk:8u181-jre-stretch(openjdk:8u181-jreのエイリアスタグ)を自動検出し、410件の脆弱性が報告されました。また、ベースイメージを変更するための推奨も提示されます。軽微なバージョンアップから、問題が約半分の最新のopenjdk:8-jreタグ、さらには脆弱性がまったくない新しいJVMまで、幅広い選択肢があります。
つまり、使用中のイメージに存在する脆弱性を特定できるだけでなく、Dockerfileを1行も書かずに、新しいベースイメージを使って解決する方法も提案してもらえます。
まとめ
このようにJibを使えば、Dockerfileの構文を学んだり、使い慣れないツールをインストールしたりする必要がなくなり、Javaアプリケーションのコンテナ化が開発者にとって大幅に簡単になります。また、広く使われているMavenやGradleのプロジェクト階層機能を通じて、アーキテクトが標準化を管理するのにも役立ちます。JibはOCI準拠のイメージをビルドするため、Snyk Containerなどの業界標準ツールを活用して、アプリケーションを検査、デプロイ、実行できます。
Java以外のプロジェクトもサポートしている場合は、Buildah、Bazel、Earthlyや、BuildKit関連の各種プロジェクトなど、この分野の他のツールも検討してみてください。また、同僚のPas Apicellaが最近Cloud Native Build Packsについての記事を公開しました。ぜひお読みください。
無料アカウントに登録して、コンテナ、オープンソースの依存関係、IaCコードのテストを今すぐ始めましょう!
ソースコードの段階からインフラを保護
Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。



