Spring ブートコードをコンテナ化するための 9 つのヒント

Dockerでは、活気に満ちた多様で創造的なコミュニティを非常に誇りに思っています。 時々、私たちはブログでコミュニティからのクールな貢献を特集し、私たちのコミュニティが行っている素晴らしい仕事のいくつかを強調しています。 Dockerで何か素晴らしいことに取り組んでいますか? あなたの貢献をアジート・シン・ライナ(@ajeetraina)に送ってください Docker Community Slack そして、私たちはあなたの作品を特集するかもしれません!

多くの開発者がDockerコンテナを使用して Spring Boot アプリケーションをパッケージ化しています。 VMWareの2021年春の現状 レポートによると、コンテナ化されたSpringアプリを実行している組織の数は、2020年の44%と比較して57%に急増しました。

この大幅な成長を推進しているのは何ですか? Webアプリケーションの起動時間を短縮し、リソース使用量を最適化するという需要がますます高まっているため、開発者の生産性が大幅に向上しています。

Spring Bootアプリのコンテナ化が重要なのはなぜですか?

Spring Boot アプリケーションを Docker コンテナーで実行すると、多くの利点があります。 まず、Dockerの使いやすいCLIベースのワークフローにより、開発者はあらゆるスキルレベルの他の開発者向けにコンテナ化されたSpringアプリケーションを構築、共有、実行できます。 次に、開発者は単一のパッケージからアプリをインストールし、数分で起動して実行できます。 第三に、Spring 開発者は、開発と本番の一貫性を確保しながら、ローカルでコーディングとテストを行うことができます。

Spring Boot アプリケーションのコンテナ化は簡単です。 これを行うには .jar 、 又は .war ファイルを JDK 基本イメージに直接保存し、Docker イメージとしてパッケージ化します。 オンラインには、アプリを効果的にパッケージ化するのに役立つ記事が多数あります。 ただし、Docker イメージの脆弱性、イメージの肥大化、イメージ タグの欠落、ビルド パフォーマンスの低下など、多くの重要な懸念事項は対処されていません。 これらの一般的な懸念事項に取り組みながら、Spring Bootコードをコンテナ化するための9つのヒントを共有します。

シンプルな "Hello World" Spring Boot アプリケーション

無人の問題をよりよく理解するために、サンプルの "Hello World" アプリケーションを作成しましょう。 前回のブログ記事では、 この 初期化済みのプロジェクト をダウンロードして ZIP ファイルを生成することで、"Hello World!" アプリケーションを簡単に構築できることを説明しました。 次に、それを解凍し、次の手順を実行してアプリを実行します。

画像4 2

src/main/java/com/example/dockerapp/ ディレクトリの下で、次の内容でファイルを変更できます DockerappApplication.java


package com.example.dockerapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class DockerappApplication {

@RequestMapping("/")
public String home() {
return "Hello World!";
}

public static void main(String[] args) {
SpringApplication.run(DockerappApplication.class, args);
}

}

次のコマンドは、コンパイルされたコードを取得し、JAR などの配布可能な形式にパッケージ化します。

./mvnw package
java -jar target/*.jar

これで、 http://localhost:8080 経由で「Hello World」にアクセスできるはずです。

このアプリを Docker 化するには、 Dockerfile. A Dockerfile は、ユーザーが Docker イメージをアセンブルするためにコマンド ラインで呼び出すことができるすべての命令を含むテキスト ドキュメントです。 Dockerイメージはレイヤーのスタックで構成され、それぞれ Dockerfileが. 後続の各レイヤーには、その下にあるレイヤーへの変更が含まれます。

通常、開発者は次の Dockerfile テンプレートを使用して Docker イメージをビルドします。


FROM eclipse-temurin
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

最初の行は、約 457 MB の基本イメージを定義します。 ザ  ティッカー 命令は、 写し 命令。 ザ 写し JAR ファイルを ターゲット/ フォルダーを Docker イメージのルートに追加します。 ザ 晒す 命令は、コンテナが実行時に指定されたネットワークポートでリッスンすることをDockerに通知します。 最後に、 エントリポイント 実行可能ファイルとして実行されるコンテナーを構成できます。 それはあなたの java -jar target/*.jar  命令。

次のようなコマンドを使用してイメージを docker build ビルドします。


$ docker build -t spring-boot-docker .
Sending build context to Docker daemon  15.98MB
Step 1/5 : FROM eclipse-temurin
---a3562aa0b991
Step 2/5 : ARG JAR_FILE=target/*.jar
---Running in a8c13e294a66
Removing intermediate container a8c13e294a66
---aa039166d524
Step 3/5 : COPY ${JAR_FILE} app.jar
COPY failed: no source files were specified

上記の例の主な欠点の1つは、完全にコンテナ化されていないことです。 最初に、 ./mvnw パッケージ コマンドをホスト・システム上で実行します。 これには、Javaを手動でインストールし、環境変数を設定し  JAVA_HOME 、Mavenをインストールする必要があります。 一言で言えば、JDKはDockerコンテナの外部に存在する必要があり、ビルド環境がさらに複雑になります。 もっと良い方法があるはずです。

1)すべての手動ステップを自動化する

JAR は、ビルドプロセス中に JAR を独自の Dockerfile 内部で構築することをお勧めします。 次の手順 RUN は、プラグイン、レポート、およびそれらの依存関係を含むすべてのプロジェクトの依存関係を解決する目標をトリガーします。

FROM eclipse-temurin
WORKDIR /app

COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline

COPY src ./src

CMD ["./mvnw", "spring-boot:run"]

Dockerfile の書き込み中に JAR ファイルを手動でコピーすることは避けてください

2)最新ではなく、特定のベースイメージタグを使用する

Docker イメージをビルドするときは、バージョン情報、目的の宛先 (本番環境やテストなど)、安定性、またはさまざまな環境にアプリケーションをデプロイするためのその他の有用な情報を体系化した便利なタグを指定することを常にお勧めします。 自動的に作成された latest タグに依存しないでください。 使用する latest と予測できず、予期しない動作が発生する可能性があります。 最新のイメージをプルするたびに、アプリケーションを破損させる可能性のある新しいビルドまたはテストされていないリリースが含まれている可能性があります。

たとえば、 eclipse-temurin:latest Docker イメージを基本イメージとして使用することは理想的ではありません。 代わりに、次のような eclipse-temurin:17-jdk-jammy 特定のタグを使用する必要があります , eclipse-temurin:8u332-b09-jre-alpin etc.

での使用 FROM eclipse-temurin:latest は避けてください Dockerfile

3)可能であれば、JDKの代わりにEclipse Temurinを使用してください

OpenJDK Docker Hub ページには、Java アプリケーションの構築時に使用する必要がある推奨 Docker 公式イメージのリストがあります。 アップストリームの OpenJDK イメージは JRE を提供しなくなったため、公式の JRE イメージは生成されません。 公式のOpenJDKイメージには、 Oracle または 関連するプロジェクトリーダーが提供するOpenJDKの「バニラ」ビルドが含まれているだけです。

ビルドに適したJDKで最も人気のある公式イメージの1つは、Eclipse Temurinです。 Eclipse Temurin プロジェクトは、ランタイム・バイナリーおよび関連テクノロジーの構築をサポートするコードとプロセスを提供します。 これらは、高性能、エンタープライズキャリバー、およびクロスプラットフォームです。

FROM eclipse-temurin:17-jdk-jammy

WORKDIR /app

COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline

COPY src ./src

CMD ["./mvnw", "spring-boot:run"]

4)マルチステージビルドを使用する

マルチステージ ビルドでは、Docker ビルドでコンパイル、パッケージ化、単体テストに 1 つの基本イメージを使用できます。 別のイメージは、アプリケーションのランタイムを保持します。 これにより、最終的なイメージがより安全でサイズが小さくなります(開発ツールやデバッグツールが含まれていないため)。 多段 Dockerビルドは、ビルドが100%再現可能で、可能な限り無駄がないことを保証するための優れた方法です。 内に Dockerfile 複数のステージを作成し、そのイメージの構築方法を制御できます。

Spring Boot アプリケーションは、多層アプローチを使用してコンテナー化できます。 各レイヤーには、依存関係、ソース コード、リソース、さらにはスナップショットの依存関係など、アプリケーションのさまざまな部分が含まれる場合があります。 または、実行可能なアプリケーションを含む最終イメージとは別のイメージとしてアプリケーションをビルドすることもできます。 これをよりよく理解するために、次のことを考え Dockerfileてみましょう。

FROM eclipse-temurin:17-jdk-jammy
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

スプリングブートは、デフォルトのパッケージ形式として 「ファットJAR」 を使用します。 ファットJARを調べると、アプリケーションがJAR全体のごく一部を形成していることがわかります。 この部分は最も頻繁に変更されます。 残りの部分には、Spring フレームワークの依存関係が含まれています。 最適化には、通常、アプリケーションを Spring Framework の依存関係とは別のレイヤーに分離することが含まれます。 ファットJARの大部分を形成する依存関係レイヤーを一度ダウンロードするだけで、ホストシステムにキャッシュされます。

上記 Dockerfile は、fatJARがすでにコマンドラインで構築されていることを前提としています。 これは、マルチステージ ビルドを使用し、あるイメージから別のイメージに結果をコピーして Docker で行うこともできます。 Maven または Gradle プラグインを使用する代わりに、を使用してレイヤー化された JAR Docker イメージ Dockerfileを作成することもできます。 Dockerを使用している間、さらに2つの手順に従ってレイヤーを抽出し、それらを最終イメージにコピーする必要があります。

最初の段階では、依存関係を抽出します。 第 2 段階では、抽出した依存関係を最終的な画像にコピーします。

FROM eclipse-temurin:17-jdk-jammy as builder
WORKDIR /opt/app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY ./src ./src
RUN ./mvnw clean install

FROM eclipse-temurin:17-jre-jammy
WORKDIR /opt/app
EXPOSE 8080
COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar
ENTRYPOINT ["java", "-jar", "/opt/app/*.jar" ]

最初の画像には というラベルが付けられ builderています。 それを使用して eclipse-temurin:17-jdk-jammy実行し、太いJARを構築し、開梱します。

これが Dockerfile 2 つのステージに分割されていることに注意してください。 後の層にはアプリケーションのビルド構成とソース・コードが含まれ、前の層には完全な Eclipse JDK イメージ自体が含まれます。 この小さな最適化により、ディレクトリを target Dockerイメージ(ビルドに使用される一時的なイメージであっても)にコピーする必要もなくなります。 最終的なイメージは、第 1 段階のビルドのサイズが 450 MB であるのに対し、わずか 277 MB です。

5).ドッカー無視を使用する

ビルドのパフォーマンスを向上させるには .dockerignoreDockerfileファイルを .このチュートリアルでは、 .dockerignore ファイルには1行だけを含める必要があります。

target

この行は、Maven からの出力を含むターゲットディレクトリを Docker ビルドコンテキストから除外します。 慎重に構成 .dockerignore する多くの正当な理由があります ファイルですが、今のところこの単純なファイルで十分です。 それでは build context 、説明しましょう そしてなぜそれが不可欠であるか 。 このコマンドは、 docker build と "コンテキスト" から Dockerfile Docker イメージをビルドします。 このコンテキストは、指定した PATH または URL にあるファイルのセットです。 ビルド プロセスでは、これらのファイルを参照できます。

一方、コンパイルコンテキストは開発者が作業する場所です。 これは、Mac、Windows、またはLinuxディレクトリ上のフォルダである可能性があります。 このディレクトリには、ソースコード、構成ファイル、ライブラリ、プラグインなど、必要なすべてのアプリケーションコンポーネントが含まれています。 と .dockerignore ファイルを使用すると、新しいイメージの構築中に除外するソースコード、構成ファイル、ライブラリ、プラグインなどの次の要素のどれを決定できます。

方法 .dockerignore は次のとおりです ビルドから、 libraries、およびplugins directoryを除外 conf することを選択した場合、ファイルが表示される場合があります。

画像1 3

6)マルチアーキテクチャのDockerイメージを優先する

CPUは、ネイティブアーキテクチャのバイナリのみを実行できます。 たとえば、x86 システム用に構築された Docker イメージは、Arm ベースのシステムでは実行できません。 AppleがカスタムArmベースのシリコンに完全に移行しているため、x86(IntelまたはAMD)Docker ImageがAppleの最近のMシリーズチップで動作しない可能性があります。 そのため、 マルチアーキテクチャコンテナイメージの構築を常に推奨しました。 以下は、 mplatform/mquery 任意のパブリックレジストリ内のパブリックイメージのマルチプラットフォームステータスを照会できるDockerイメージです。

docker run --rm mplatform/mquery eclipse-temurin:17-jre-alpine
Image: eclipse-temurin:17-jre-alpine (digest: sha256:ac423a0315c490d3bc1444901d96eea7013e838bcf7cc09978cf84332d7afc76)
* Manifest List: Yes (Image type: application/vnd.docker.distribution.manifest.list.v2+json)
* Supported platforms:
- linux/amd64

マルチアーキテクチャ イメージの構築に役立つコマンドを導入し docker buildx ました。 Buildx は、使い慣れた Docker ユーザー エクスペリエンスで多くの強力なビルド機能を有効にする Docker コンポーネントです。 Buildx を介して実行されるすべてのビルドは、 Moby BuildKit ビルダー エンジンを介して実行されます。 BuildKitは、マルチプラットフォームビルド、またはユーザーのローカルプラットフォームをターゲットにしないビルドに優れているように設計されています。 ビルドを呼び出すときに、フラグを設定して --platform 、ビルド出力のターゲットプラットフォームを指定できます ( linux/amd64linux/arm64、または darwin/amd64など)。

docker buildx build --platform linux/amd64, linux/arm64 -t spring-helloworld .

 

7)セキュリティ上の目的で非rootユーザーとして実行する

ユーザー特権でアプリケーションを実行すると、リスクの軽減に役立つため、より安全です。 同じことがDockerコンテナにも当てはまります。 既定では、Docker コンテナーと実行中のアプリにはルート権限があります。 したがって、Dockerコンテナは非ルートユーザーとして実行することをお勧めします。 これを行うには、内に指示 Dockerfile を追加します USER。この命令は USER 、イメージの実行中に優先ユーザー名 (または UID) とオプションでユーザーグループ (または GID) を設定し、後続の RUN, CMDまたは ENTRYPOINT 命令に対して設定します。

FROM eclipse-temurin:17-jdk-alpine
RUN addgroup demogroup; adduser  --ingroup demogroup --disabled-password demo
USER demo

WORKDIR /app

COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline

COPY src ./src

CMD ["./mvnw", "spring-boot:run"]

8)Javaイメージのセキュリティの脆弱性を修正する

今日の開発者は、サービスを構築する際にサードパーティのコードとアプリケーションに依存しています。 注意せずに外部ソフトウェアを使用すると、コードがより脆弱になる可能性があります。 これに対抗するには、信頼できるイメージを活用し、コンテナを継続的に監視することが不可欠です。

Docker Scoutは、開発者のワークフローに統合された修復オプションとともに、特定のイメージへの依存関係を詳細に可視化する機能で、利用可能なセキュリティツールの中で際立っています。 依存関係の脆弱性を詳細に分析し、更新またはパッチが適用されたベースイメージを検索するか、さまざまなレイヤーの脆弱性の正確な場所を特定することで、脆弱性を迅速に修正するための推奨事項を提供します。

開発者の作業を簡素化するように設計されたDocker Scoutは、Dockerに直接統合されているため、脆弱性を制御しながらコードの開発により多くの時間を費やすことができます。 さらに、Dockerは、CVEをパッケージに、SBOMをCVEdbに一致させることで開発者エクスペリエンスを向上させることに重点を置いており、追加のスキャンを必要とせずに脆弱性の検出と解決を改善します。

Docker Desktop では、Docker イメージ ("jsgiraldoh/spring-helloworld:latest" など) をダウンロードするたびに、Log4Shell などの既知の脆弱性とイメージの推奨事項の概要を確認することをお勧めします。

$ docker pull jsgiraldoh/spring-helloworld:latest
latest: Pulling from jsgiraldoh/spring-helloworld
df9b9388f04a: Pull complete
d3277e9a7631: Pull complete
13f2c9707109: Pull complete
28060bb0256d: Pull complete
6c5e62ebc1c8: Pull complete
33c5943bedda: Pull complete
eac578d9fcdc: Pull complete
491863a4fe37: Pull complete
063454f659da: Pull complete
Digest: sha256:194b3574389ea82ee6cca08d9b707c1965c3242b0765f2830c1a511f3a1980e3
Status: Downloaded newer image for jsgiraldoh/spring-helloworld:latest
docker.io/jsgiraldoh/spring-helloworld:latest

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview jsgiraldoh/spring-helloworld:latest

Dockerが推奨するコマンドを実行すると、次の結果が得られます。

脆弱性の概要を示す docker scout のイメージのスクリーンショット。

コマンドの結果は、イメージの各レイヤーで見つかった脆弱性の概要です。 また、CVEとこれらの脆弱性を解決するための推奨事項を観察できる2つの追加オプションもあります。

分析されたイメージに関連する重大で高の脆弱性を示す docker scout のスクリーンショット。
分析されたイメージの脆弱性に対する推奨される修正を示す docker scout のスクリーンショット。

CLIを使用するだけでなく、Docker Scoutオプションを使用して、Docker DesktopからDocker Scout 機能にアクセスすることもできます。 まず、 Docker Desktop 4.25.1MacWindows、または Linux マシンにインストールします。

高度な画像解析ページを示す docker scout のスクリーンショット。

分析するイメージを選択したら、[ パッケージと CVE の表示 ] セクションをクリックする必要があります。

docker scout のスクリーンショットとパッケージとレイヤーの表示。

9) OpenTelemetry API を使用して Java のパフォーマンスを測定する

Spring Boot開発者は、アプリの高速化とパフォーマンスをどのように保証していますか? 一般に、開発者はサードパーティの可観測性ツールを使用してJavaアプリケーションのパフォーマンスを測定します。 アプリケーションのパフォーマンス監視は、あらゆる種類のJavaアプリケーションにとって不可欠であり、開発者は一流のユーザーエクスペリエンスを作成する必要があります。

可観測性は、アプリケーションのパフォーマンスだけに限定されません。 マイクロサービス アーキテクチャの台頭に伴い、可観測性の 3 つの柱 (メトリック、トレース、ログ) が中心になっています。 メトリックは開発者がシステムの問題点を理解するのに役立ち、トレースはシステムがどのように間違っているかを発見するのに役立ちます。 ログはそれが間違っている理由を示し、開発者は特定のメトリックやトレースを掘り下げてシステムの動作を全体的に理解できます。

Java アプリケーションを監視するには、JMX、基盤となるホストメトリック、および Java アプリケーショントレースを介して Java VM メトリックを監視する必要があります。 Java 開発者は、 Java OpenTelemetry API を使用して、アプリケーションのパフォーマンスを監視、分析、および診断する必要があります。 OpenTelemetryは、アプリケーションから分散トレースとメトリックをキャプチャするためのAPI、ライブラリ、エージェント、およびコレクターサービスの単一のセットを提供します。 詳細については、このビデオをご覧ください

結論

このブログ投稿では、Dockerfile を慎重に作成し、Snyk Docker 拡張機能マーケットプレースを使用してイメージをセキュリティで保護することで、Docker イメージを最適化する多くの方法のいくつかについて説明しました。 さらに詳しく知りたい場合は、安全な運用グレードの Docker イメージを構築するための推奨事項とベスト プラクティスをカバーするこれらのボーナス リソースを確認してください。

ドッカーブログチートシートv3a 1

フィードバック

「春のブートコードをコンテナ化するための0つのヒント」に関する9つの考え