Java アプリをコンテナー化する

完了済み

このユニットでは、Java アプリケーションをコンテナー化します。

前述のように、コンテナーは通常のシステム プロセスとしてホスト オペレーティング システム、カーネル、ハードウェアの上で直接実行されます。 コンテナーの必要なシステム リソースが少なくなり、フットプリントが小さくなり、オーバーヘッドが少なくなり、アプリケーションの起動時間が短縮されます。 これらの利点は、オンデマンドでスケーリングするための優れたユース ケースです。

Windows コンテナーと Linux コンテナーがあります。 このモジュールでは、広く使用されている Docker ランタイムを使用して Linux コンテナー イメージを構築します。 次に、Linux コンテナー イメージをローカル コンピューターのホスト オペレーティング システムにデプロイします。 最後に、Linux コンテナー イメージを Azure Kubernetes Service にデプロイします。

Docker の概要

Docker ランタイムは、次の図に示すように、コンテナー イメージのビルド、プル、実行、プッシュに使用されます。

クライアント、Docker ホスト、およびレジストリを接続する Docker のビルド、プル、プッシュ、および実行コマンドを示す図。

次の表では、各 Docker コマンドについて説明します。

Docker コマンド 説明
docker build Docker がイメージから実行中のコンテナーを作成するために必要な手順またはレイヤーで構成されるコンテナー イメージを構築します。 このコマンドの結果はイメージです。
docker pull レジストリから、クライアントが選択した Docker デーモンのイメージ ストアにイメージをダウンロードします。 既存のイメージ レイヤーを再利用できます。 このモジュールでは、ローカルの Docker 環境で開発中にレジストリのイメージを使用します。後で、AKS ノードはデプロイのためにAzure Container Registryからアプリケーション イメージをプルします。
docker run イメージから新しいコンテナーを作成して開始します。 コンテナーは、コマンドラインによる上書きが可能な、構成されたスタートアップ プロセスを実行します。 イメージ レイヤーはコンテナーのファイルシステムを提供します。コンテナーの起動時に Dockerfile ビルド命令が再度実行されることはありません。
docker push Azure Container Registry にはイメージが格納されるため、すぐに使用でき、Azure のデプロイとスケーリングのためにネットワークが閉じられます。

Java アプリケーションを複製する

最初に、Flight Booking System for Airline Reservations リポジトリを複製し、Airlines Web アプリケーション プロジェクト フォルダーに移動します。

注

CLI タブで Azure Kubernetes Service の作成が完了した場合は、そのタブを使用します。まだ実行されている場合は、新しいタブを開き、航空会社予約のフライト予約システムを複製する場所に移動します。

次のコマンドを実行してください:

git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

必要に応じて、Java と Maven がインストールされている場合は、ターミナル コンソールで次のコマンドを実行して、Docker なしでアプリケーションをビルドするエクスペリエンスを把握できます。 Java と Maven がインストールされていない場合は、次のセクション「 Docker ファイルを作成する」に進んでも問題ありません。 このセクションでは、Docker を使用して Java と Maven をプルダウンし、代わりにビルドを実行します。

mvn clean package

注

mvn clean package コマンドを使用して、Docker マルチステージ ビルドを使用しないという運用上の課題を示しました。これについては、次に説明します。 ここでも、この手順は省略可能です。 どちらの方法でも、Maven コマンドを実行せずに安全に続行できます。

このプロセスが成功した場合、Maven は、次の過去の出力に示すように、エアライン予約ウェブ アプリケーション アーカイブ アーティファクト AirlinesReservationSample-0.0.1-SNAPSHOT.war のフライト予約システムを正常に構築しました。 ビルドのタイムスタンプと期間は異なる場合があります。

[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------

あなたは Java 開発者であり、 AirlinesReservationSample-0.0.1-SNAPSHOT.war を構築したとします。 次の手順は、運用エンジニアと協力して、この成果物をオンプレミス サーバーまたは仮想マシンにデプロイすることです。 アプリケーションを正常に起動して実行するには、サーバーと仮想マシンを使用でき、必要な依存関係を使用して構成する必要があります。 このプロセスは困難で時間がかかります。特に、負荷が増加してアプリケーションに影響を与えるオンデマンドの際には、なおさらです。 コンテナーを使用すると、これらの課題が軽減されます。

Dockerfile を構築する

これで、Dockerfile を構築する準備ができました。 Dockerfile は、ユーザーがコマンド ラインで実行してコンテナー イメージをアセンブルするすべてのコマンドを含むテキスト ドキュメントです。 コンテナー イメージには、複数の読み取り専用ファイルシステム レイヤーを含めることができます。 Docker では、変更されていない手順のキャッシュされたビルド結果を再利用できます。 レイヤーは互いに重なって構築されます。

たとえば、フライト予約システム for Airline Reservations は、アプリケーション サーバーにデプロイして実行する必要があります。 アプリケーション サーバーは AirlinesReservationSample-0.0.1-SNAPSHOT.war 内にパッケージ化されていません。 AirlinesReservationSample-0.0.1-SNAPSHOT.war を実行し、HTTP 要求をリッスンして処理し、ユーザー セッションを管理し、フライト予約を容易にするために必要な外部依存関係です。 従来のコンテナー化されていないデプロイを使用した場合、運用エンジニアは 、AirlinesReservationSample-0.0.1-SNAPSHOT.war をデプロイする前に、物理サーバーまたは仮想マシンにアプリケーション サーバーをインストールして構成します。 これらの操作エンジニアは、マシンで使用されている JDK (WAR ファイルのコンパイルに使用 mvn clean package ) が、実際にアプリケーション サーバーで使用されているのと同じ JRE に対応していることを確認する必要があります。 これらの依存関係の管理は困難で時間がかかります。

Dockerfile を使用すると、Docker コンテナー ランタイムにデプロイするために必要なすべての依存関係がフライト予約システムに確実に含まれるようにするために必要な手順を重ねて、この目標を自動的に達成するために必要な手順またはレイヤーを記述できます。 このソリューションは、計画外の間隔でオンデマンド スケールを使用する場合に魅力的です。 各レイヤーは Docker キャッシュを使用します。Docker キャッシュには、各説明マイルストーンでのコンテナー イメージの状態が含まれており、コンピューティング時間と再利用が最適化されています。 レイヤーが変更されていない場合は、キャッシュされたレイヤーが使用されます。 キャッシュされたレイヤーの一般的なユース ケースは、Java ランタイム、アプリケーション サーバー、およびフライト予約システム for Airline Reservations Web アプリケーションのその他の依存関係です。 以前にキャッシュされたレイヤーでバージョンが変更された場合、新しいキャッシュされたエントリが作成されます。

コンテナー イメージには、読み取り専用のファイルシステム レイヤーが含まれています。 この例では、Dockerfile によって Tomcat と Flight Booking System WAR ファイルがビルド時にイメージにコピーされます。 Docker は、イメージからコンテナーを作成するときに、個別の書き込み可能なレイヤーを追加します。 各コンテナーの書き込み可能レイヤーは、イメージ レイヤーを変更せずに、独自のファイルシステムの変更を記録します。

注

コンテナーの書き込み可能レイヤーに格納されているファイルの場合、追加、変更、削除は、元のイメージではなく、そのコンテナーに影響します。 イメージ レイヤーから取得したファイルを変更すると、書き込み時のコピー動作が使用されます。

Docker にはマルチステージ ビルドという概念があります。これは、キャッシュが向上し、セキュリティフットプリントが小さい小さい小さなコンテナー イメージを作成できる機能であり、時間の経過と共に Dockerfile の最適化とメンテナンスを増やすことができます。 たとえば、アプリケーションをコンパイルおよびビルドするためのコンテナー ビルド ステージと、アプリケーションを実行するためのステージを分離できます。 この機能を使用すると、ビルド中に生成された成果物のみを運用コンテナー イメージにコピーできるため、フットプリントが削減されます。 コンテナー イメージはキャッシュされるため、変更がない場合は、キャッシュされたイメージを再利用できるため、ネットワークからダウンロードするコストと時間が削減されます。

運用環境で公開されるサービスは、セキュリティのために慎重に管理する必要があります。 そのため、運用環境では、セキュリティで保護されたコンテナー イメージを使用して操作します。 この例では、Microsoftによって提供されるAzure Linux イメージを使用します。

最後のステージでは、Azure Linux 3.0 で Microsoft Build of OpenJDK 17 を使用します。 17-azurelinux タグは、このベースを明示的に指定します。 古い17-mariner タグは、Azure Linux イメージを反映する互換性エイリアスのままです。廃止された17-mariner-cm1や17-mariner-cm2イメージと混同しないでください。 詳細については、「コンテナー イメージのMicrosoft Build of OpenJDK」を参照してください。

注

Microsoft では、Ubuntu、Azure Linux、distroless イメージなど、OpenJDK がバンドルされたコンテナー イメージを提供しています。 distrolessイメージのイメージ サイズは最小ですが、Tomcat を実行するのは困難です。 軽量な設計を実現するために、 distroless イメージはシェルを含む多くのコマンドとツールを削除します。つまり、 catalina.sh を呼び出して Tomcat を起動することはできません。 distroless イメージは、Spring Boot や Quarkus で使用される実行可能 JAR を実行するのに適しています。

次の例では、ビルド ステージと最終ステージの両方で、同じバージョンの Microsoft Build of OpenJDK が使用されています。 この方法により、サービス デプロイ Tomcat が使用するのと同じバージョンの JDK を使用してソース コードをビルドできます。これにより、バージョンの不一致による予期しない動作を回避できます。

次の図は、Dockerfile で指定されたコマンドに基づいて、マルチステージ ビルドと各ステージで発生している内容を示しています。

Tomcat のインストール、コード ビルド、およびランタイム ステージでイメージ レイヤーを作成する Dockerfile の手順を示す図。

ステージ 0 では、Tomcat はバージョン固有の Apache アーカイブからダウンロードされ、SHA-512 チェックサムで検証され、Ubuntu イメージ上の環境変数で指定されたディレクトリに抽出されます。 TOMCAT_VERSION変数はリリースを指定し、TOMCAT_SHA512ビルド引数は予想されるチェックサムを指定します。 サポートされている新しいリリースを選択するときに、両方の値を一緒に更新します。 Docker は、命令と入力に変更がなければ、キャッシュされたインストールレイヤーを再利用できます。 Tomcat のインストールは、使用するために最終ステージ環境にコピーされます。

ステージ 1 では、Maven が Ubuntu イメージにインストールされ、Maven プロジェクトをビルドする前に作成されたソース コードと構成ファイルがコピーされます。 各レイヤーはキャッシュされるため、OS イメージと Maven イメージ レイヤーはキャッシュを再利用します。 構成ファイル、ソース コード ファイル、または Web ディレクトリが更新されると、変更以降のレイヤーが再構築されます。 コンパイル中にエラーなしでビルドが正常に完了すると、 AirlinesReservationSample-0.0.1-SNAPSHOT.war という名前のアーティファクトが ターゲット ディレクトリに生成されます。 この成果物は、使用するために最終ステージ環境にコピーされます。

最後のステージでは、ステージ 0 とステージ 1 の Tomcat と Java ビルド成果物がそれぞれ、Azure Linux 3.0 上の Microsoft Build of OpenJDK 17 イメージにコピーされます。 appという名前のユーザーは、プロジェクト内で使用されるすべてのファイルを所有しており、アプリケーションはapp特権を持つ代わりにrootユーザーとしても実行されます。 このセットアップにより、不要なアクセス許可を付与することなく、コンテナー イメージを安全に操作できるようになります。 最後に、ポート番号 8080 が公開され、tomcat を起動するために catalina.sh スクリプトが実行されます。 ローカルの Docker Desktop でこれを実行すると、URL http://localhost:8080/AirlinesReservationSample経由でアクセスできます。

containerize-and-deploy-Java-app-to-Azure/Project/Airlinesで、インストールされているテキスト エディターを使用して、Dockerfileという名前のファイルを作成します。 次の内容を追加し、ファイルを保存してください。

Important

公開するイメージをビルドする前に、 Apache Tomcat ダウンロード ページ で現在サポートされている Tomcat 10.1.x リリースを確認し、 セキュリティに関する通知を確認してください。 Apache の公開されたリリース情報を使用して、 TOMCAT_VERSION と TOMCAT_SHA512 をまとめて更新します。 Apache アーカイブに残っている成果物は、そのバージョンが最新または推奨されていることを意味するものではありません。

############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat

ENV CATALINA_HOME=/usr/local/tomcat

# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb

# Install Tomcat and required packages
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl ca-certificates && \
    curl -fSL \
      "https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
      -o /tmp/tomcat.tar.gz && \
    echo "${TOMCAT_SHA512}  /tmp/tomcat.tar.gz" | sha512sum -c - && \
    mkdir -p "${CATALINA_HOME}" && \
    tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
    rm /tmp/tomcat.tar.gz && \
    apt-get remove --purge -y curl && \
    apt-get autoremove -y && \
    apt-get clean

############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build

WORKDIR /build

# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version

# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web

# Build the project
RUN mvn clean package

############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-azurelinux

# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH

# This is the user that runs the Tomcat process
USER app

# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war

# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]

注

この演習では、このユニットに示されている Dockerfile を使用します。 次のユニットの URL がwebapps/AirlinesReservationSample.war コンテキストを使用するように、アプリケーションを/AirlinesReservationSample/にコピーする必要があります。 別の WAR ファイル名によって、既定の Tomcat アプリケーション コンテキストが変更されます。

Dockerfile は、次の表に示す 3 つのステージに分かれています。

  • Tomcat のインストール ステージ:

    Docker コマンド 説明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat は、Ubuntu 上の OpenJDK 17 の Microsoft Build に基本イメージを設定し、このステージに tomcatという名前を付けます。 Tomcat がインストールされている場所です。
    ENV ENV CATALINA_HOME=/usr/local/tomcat は、Tomcat インストール ディレクトリの環境変数を設定します。
    ENV ENV TOMCAT_VERSION=10.1.61 は、この例でピン留めされている Tomcat のリリースを設定します。 サポートされているリリースを確認し、必要に応じて TOMCAT_SHA512 と共に更新してください。
    ARG ARG TOMCAT_SHA512 は、選択したアーカイブに対する Apache の公開済み SHA-512 チェックサムをビルド引数として提供します。
    RUN curlおよび CA 証明書をインストールし、curl -fSLを使用してバージョン固有の Apache アーカイブをダウンロードします。 -f により HTTP エラーで失敗します。-L リダイレクトに従います。 アーカイブは、sha512sum -c -を使用してCATALINA_HOMEに抽出する前に、--strip-components=1で検証されます。 && チェーンは、クリーンアップ中にエラーをマスクするのではなく、障害時に停止します。 最後に、アーカイブと不要なパッケージを削除し、パッケージ キャッシュをクリーンアップします。
  • Java 17 でコンパイルされるビルド ステージ:

    Docker コマンド 説明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build は、Ubuntu 上の OpenJDK 17 の Microsoft Build に基本イメージを設定し、このステージに buildという名前を付けます。 このステージは、Java アプリケーションのコンパイルに使用されます。
    WORKDIR WORKDIR /build では、コンテナー内の作業ディレクトリが /buildに設定され、ソース コードがコピーされてコンパイルされます。
    RUN RUN apt-get update && apt-get install -y maven && mvn --version は、Java プロジェクトに使用されるビルド自動化ツールである Maven をインストールし、そのインストールを検証します。
    COPY COPY pom.xml . は、Maven 構成ファイルを作業ディレクトリにコピーします。 このファイルは、プロジェクトのビルドに不可欠です。
    COPY COPY src ./src は、ソース コード ディレクトリをコンテナーにコピーします。 これは、Java アプリケーション コードが存在する場所です。
    COPY COPY web ./web は、Web リソース ディレクトリをコンテナーにコピーします。 これには、ビルドに必要な Web アプリケーション リソースが含まれます。
    RUN RUN mvn clean package は Maven ビルド プロセスを実行し、Java アプリケーションをコンパイルして WAR ファイルにパッケージ化します。
  • パッケージの最終段階:

    Docker コマンド 説明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-azurelinuxは、Azure Linux 3.0 上の Microsoft Build of OpenJDK 17 をベース イメージとして設定します。これは、アプリケーションの最終的なデプロイに使用されます。
    ENV ENV CATALINA_HOME=/usr/local/tomcat は、インストール ステージと同様に、Tomcat インストール ディレクトリの環境変数を設定します。
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH は Tomcat bin ディレクトリをシステム PATHに追加し、Tomcat コマンドを簡単に実行できるようにします。
    USER USER app は Tomcat プロセスを実行するユーザーを指定し、ルート ユーザーとして実行しないことでセキュリティを強化します。
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME} は、 tomcat ステージから Tomcat のインストールをコピーし、所有権を app ユーザーに設定します。
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf は Tomcat ユーザー構成ファイルをコンテナーにコピーし、所有権を app ユーザーに設定します。
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war は、コンパイル済みの WAR ファイルを build ステージから Tomcat Webapps ディレクトリにコピーし、所有権を app ユーザーに設定します。
    EXPOSE EXPOSE 8080 は、アプリケーションが TCP ポート 8080 で待ち受ける想定であることを記載しています。 ホスト ポートは公開しません。 次のユニットでは、-p 8080:8080 で docker run オプションを使用してポートを発行します。
    CMD CMD ["catalina.sh", "run"] は、コンテナーの実行時に Tomcat を起動するコマンドを指定します。

Dockerfile の構築の詳細については、 Dockerfile リファレンスを参照してください。