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

完了

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

先ほど説明したように、コンテナーは、基本的に別のシステム プロセスとして、ホスト オペレーティング システム、カーネル、ハードウェアの上で直接実行されます。 コンテナーに必要なシステム リソースは少ないため、占有領域はより小さく、オーバーヘッドはより少なく、アプリケーションの起動時間はより速くなります。 これらは、オンデマンドでのスケーリングに最適なユース ケースです。

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

Docker の概要

Docker ランタイムは、コンテナー イメージをビルド、プル、実行、プッシュするのに使用します。 次の図ではこれらのユース ケースを示し、その後に各ユース ケース/Docker コマンドについて説明しています。

Diagram showing Docker commands.

Docker コマンド 説明
docker build コンテナー イメージをビルドします。基本的には、Docker が最終的にイメージから実行コンテナーを作成するために必要な命令/レイヤーです。 このコマンドの結果がイメージになります。
docker pull コンテナーは、Azure Container Registry などのレジストリからプルされるイメージに基づいて初期化されます。Azure Kubernetes Service は、Azure Container Registry からプルします。 このコマンドの結果が、Azure で行われるイメージのネットワーク プルになります。 必要に応じて、イメージをローカルにプルできることに注目してください。これは、アプリケーションに必要な依存関係やレイヤー (アプリケーション サーバーなど) を必要とするイメージをビルドする場合に一般的です。
docker run イメージの実行中インスタンスはコンテナーです。このコマンドでは、実行中のコンテナー アプリケーションの実行と操作に必要なすべてのレイヤーが実行されます。 このコマンドの結果が、ホスト オペレーティング システムで実行されているアプリケーション プロセスになります。
docker push イメージは Azure Container Registry に格納されるため、Azure でのデプロイとスケーリング用にすぐに使用でき、ネットワーク上の近い場所に置かれます。

Java アプリケーションをクローンする

まず、Flight Booking System for Airline Reservations のリポジトリをクローンし、航空会社の Web アプリケーション プロジェクト フォルダーに移動します。

Note

CLI タブで Azure Kubernetes Service の作成が正常に完了した場合は、そのタブを使用します。まだ実行中であれば、新しいタブを開き、Flight Booking System for Airline Reservations をクローンする場所に移動します。

CLI で次のコマンドを実行します。

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

CLI で次のコマンドを実行します。

cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

Note

Java と Maven がインストールされている場合、必要に応じて CLI 内で次のコマンドを実行すると、Docker なしでアプリケーションをビルドするエクスペリエンスを体感することができます。 Java と Maven がインストールされていない場合は、次のセクション「Dockerfile を構築する」に進んでかまいません。 そのセクション内では、Docker を使用して Java と Maven をプルダウンし、ユーザーの代わりにビルドを実行します。

Maven と JDK(8) 以上がインストールされている場合は、必要に応じて CLI で次のコマンドを実行できます。

mvn clean install

Note

mvn clean install コマンドは、Docker マルチステージ ビルド (次に説明します) を使用しない場合の、運用上の課題を示すために使用しました。 このステップも省略できます。どちらの場合も、Maven コマンドを実行せずに次に進んでかまいません。

Maven によって次の出力のように、Flight Booking System for Airline Reservations Web アプリケーションのアーカイブ成果物、FlightBookingSystemSample-0.0.-SNAPSHOT.war が正常にビルドされたはずです。

[INFO] Building war: /mnt/c/Users/chtrembl/dev/git/containerize-and-deploy-Java-app-to-Azure/Project/FlightBookingSystemSample/target/FlightBookingSystemSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  17.698 s
[INFO] Finished at: 2021-09-28T15:18:07-04:00
[INFO] ------------------------------------------------------------------------

あなたは Java 開発者で、この FlightBookingSystemSample-0.0.1-SNAPSHOT.war をビルドしたばかりだとします。 次のステップでは、運用エンジニアと共同で、この成果物をオンプレミス サーバーまたは仮想マシンのどちらかにデプロイすることになるでしょう。 アプリケーションを正常に起動して実行するには、サーバーと仮想マシンを使用可能にし、必要な依存関係を指定して構成します。 このことは、特にアプリケーションの負荷が増加しているときの要求時には、難しく、時間もかかります。 コンテナーを使用すると、これらの課題が軽減されます。

Dockerfile を構築する

この時点で、Dockerfile を構築する準備ができました。 Dockerfile は、ユーザーがコンテナー イメージをアセンブルするためにコマンド ライン上で実行することができる、すべてのコマンドを含むテキスト ドキュメントです。各コマンドは相互に重なり合って構築されたレイヤー (効率を良くするためにキャッシュすることができます) になっています。

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

Dockerfile を使用すると、これを自動化するために必要な命令 (レイヤー) を記述できます。それには、Flight Booking System for Airline Reservations が Docker コンテナー ランタイムへのデプロイに必要なすべての依存関係を確保するために必要なステップをレイヤー化します。 これは、予定外の間隔でのオンデマンド スケーリングについて検討を始めるときに非常に有用です。 各レイヤーでは Docker キャッシュが利用されています。これには命令の各マイルストーンにおけるコンテナー イメージの状態が含まれ、コンピューティング時間と再利用が最適化されます。 レイヤーが変更されていない場合は、キャッシュされたレイヤーが使用されます。 キャッシュされたレイヤーの一般的なユース ケースとしては、Flight Booking System for Airline Reservations Web アプリケーションの Java ランタイム、アプリケーション サーバー、その他の依存関係などがあります。 前にキャッシュされたレイヤー上でバージョンが変更された場合は、新しいキャッシュ エントリが作成されます。

次の図は、コンテナー イメージのレイヤーを示しています。 上位のレイヤーは、前の読み取り専用レイヤーの上に構築された読み取り/書き込みの Flight Booking System for Airline Reservations Web アプリケーション レイヤーです。これらはすべて Dockerfile 内のコマンドから生成されます。

Diagram showing the Docker layers.

Docker には、マルチステージ ビルドの概念もあります。これは、より優れたキャッシュとより小さいセキュリティ フットプリントを備えた、より小さいコンテナー イメージを作成することができる機能で、時間の経過と共に Dockerfile の最適化とメンテナンスを強化することができます。たとえば、アプリケーション (FlightBookingSystemSample-0.0.1-SNAPSHOT.war) のコンパイルとコンテナー イメージ自体のビルドの両方を実行するのに使用することができる命令では、FlightBookingSystemSample-0.0.1-SNAPSHOT.war のコンパイル後の不要物を含めないことで、フットプリントがより小さくなります。 これらのイメージがネットワーク上を移動することについて考えれば、長期的には、これが利益になります。 マルチステージ ビルドでは、Dockerfile 内で複数の FROM ステートメントを使用します。 各 FROM 命令ではそれぞれ異なるベースを使用でき、これらの各ステートメントは白紙の状態から始まるため、通常はキャッシュされる可能性のあるキャッシュ レイヤー内の不要なファイルが削除されます。

アプリケーションが、同じ JRE (実行時にコンテナー イメージ内で分離されます) に対応する同じ JDK によってビルドされることを確認する必要があります。 次の例には、特定のバージョンの Maven と特定のバージョンの JDK を利用して FlightBookingSystemSample-0.0.1-SNAPSHOT.war をコンパイルするビルド ステージが含まれます。 このステージでは、このステージを実行している Docker ランタイムで、Dockerfile の作成者が指定した想定された生成済みバイト コードが取得されるようにします (そうでない場合、運用エンジニアは自分の Java とアプリケーション サーバー ランタイムを開発者のものと相互参照する必要があります)。 パッケージ ステージでは、特定のバージョンの Tomcat と、ビルド ステージの JDK に対応する JRE を使用します。 この場合も、すべての依存関係 (Java Development Kit (JDK)、Java Runtime Environment (JRE)、アプリケーション サーバー) を制御および分離して、このイメージが実行されるすべてのマシンで想定された動作が行われるようにします。

このマルチステージ ビルドでは、技術的には Maven と Java をシステム上にインストールする必要がないことも注目に値します。 Docker は、アプリケーションのビルドとアプリケーションのランタイムの両方でそれらをプルして使用することで、起こり得るバージョン管理の競合と想定外の動作を回避します。もちろん、コードのコンパイルと成果物のビルドを Docker の外部で行っていない場合に限ります。

次の画像は、マルチステージ ビルドと、Dockerfile 内で指定されたコマンドに基づいて各ステージで行われる処理を示します。 ステージ 0 であるビルド ステージでは、Flight Booking System for Airline Reservations Web アプリケーションがコンパイルされ、FlightBookingSystemSample-0.0.1-SNAPSHOT.war が生成されます。 このステージでは、このアプリケーションのコンパイルに使用される Maven と Java のバージョンの整合性を確保することができます。 FlightBookingSystemSample-0.0.1-SNAPSHOT.war が作成されたら、それがステージ 1 (ランタイム ステージ) に必要な唯一のレイヤーになります。それ以前のレイヤーはすべて破棄できます。 その後、Docker ではステージ 0 で生成されたこの FlightBookingSystemSample-0.0.1-SNAPSHOT.war レイヤーを使用して、ランタイムに必要な残りのレイヤー (この場合、アプリケーション サーバーの構成とアプリケーションの起動) を構築します。

Diagram showing the Docker multistage build.

プロジェクト containerize-and-deploy-Java-app-to-Azure/Project/Airlines のルート内で、Dockerfile という名前のファイルを作成します。

vi Dockerfile

Dockerfile に次の内容を追加し、Esc キーを押してから「:wq!」と入力し、Enter キーを押すことで、保存して終了します。

#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
COPY web ./web
RUN mvn clean package

#
# Package stage
#
FROM tomcat:8.5.72-jre11-openjdk-slim
COPY tomcat-users.xml /usr/local/tomcat/conf
COPY --from=build /build/target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war
EXPOSE 8080
CMD ["catalina.sh", "run"]

Note

必要に応じて、プロジェクトのルート内の Dockerfile_Solution に必要な内容が格納されます。

この Dockerfile ビルド ステージには、次の 6 つの命令があります。

Docker コマンド 説明
FROM FROM maven は基本レイヤーとなり、これに基づいてこの FlightBookingSystemSample-0.0.1-SNAPSHOT.war がビルドされます。特定のバージョンの Maven と特定のバージョンの JDK を指定し、このビルドを実行しているすべてのマシン上でバイト コードが同じであるコンパイルが確実に行われるようにします。
WORKDIR WORKDIR は、任意の時点におけるコンテナーの作業ディレクトリ (この場合はコンパイルされた成果物が格納される場所) を定義するために使用します。
COPY COPY は、Docker クライアントの現在のディレクトリからファイルを追加します。 Maven によるコンパイルに必要なファイルの設定では、pom.xml が Docker コンテキストで必要になります。
COPY Maven によるコンパイルに必要なファイルを設定します。 この Docker コンテキストでは、Flight Booking System for Airline Reservations Web アプリケーションを含む src フォルダーが必要になります。
COPY Maven によるコンパイルに必要なファイルを設定します。 この Web Docker コンテキストには、Flight Booking System for Airline Reservations Web アプリケーションの依存関係を含むフォルダーが必要になります。
実行 RUN mvn clean package 命令は、現在のイメージ上で任意のコマンドを実行するために使用します。 この場合、RUN は、FlightBookingSystemSample-0.0.1-SNAPSHOT.war をコンパイルする Maven ビルドの実行に使用します。

この Docker ファイル パッケージ ステージには 5 つの命令があります。

Docker コマンド 説明
FROM FROM tomcat は基本レイヤーとなり、これに基づいてコンテナー イメージがビルドされます。 Flight Booking System for Airline Reservations コンテナー イメージが、tomcat イメージの上にビルドされるイメージになります。 Docker ランタイムは、tomcat イメージをローカルで見つけようとします。 このバージョンがない場合は、レジストリからプル ダウンされます。 ここで参照されている Tomcat イメージを調べると、それが他の多くのレイヤーを使用してビルドされていることがわかります。それらすべてのレイヤーによって、Java アプリケーションをデプロイすれば世界中で使用することができる、1 つのパッケージ化されたアプリケーション サーバー コンテナー イメージとして再利用することができます。 モジュールのために tomcat:8.5.72-jre11-openjdk-slim を選択してテストしました。 Docker がこの 2 番目の FROM 命令を認識すると、最初のビルド ステージにあった前のレイヤーはすべて削除されます。
COPY COPY tomcat-users.xml は、Flight Booking System for Airline Reservations のユーザー (Tomcat ID を使用してソース管理内で管理されます。通常、これは外部の ID 管理システム内にあります) を管理する tomcat-users.xml ファイルを、tomcat コンテナー イメージ内にコピーします。これにより、コンテナー イメージが作成されるたびに、tomcat-users.xml ファイルがコンテナー イメージ内に存在するようになります。
ADD ADD target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war は、Maven によりコンパイルされた FlightBookingSystemSample-0.0.1-SNAPSHOT.war を Tomcat イメージの webapps フォルダーにコピーすることで、Tomcat が初期化される際に、そのアプリケーション サーバー上にインストールされる FlightBookingSystemSample-0.0.1-SNAPSHOT.war が検出されることを保証します。
EXPOSE EXPOSE 8080 は、Tomcat がポート 8080 のトラフィックをリッスンするように構成されているために必要です。 これにより、Docker プロセスがこのポートでリッスンするようになります。
CMD CMD 命令は、コンテナーの実行時に実行されるコマンドを設定します。 この場合、CMD ["catalina.sh", "run"] では、Tomcat アプリケーション サーバーを初期化するように Docker に指示します。

Note

FROM tomcat 行上にバージョン タグがない場合は、最新バージョンが適用されます。 通常は、バージョン タグを活用します (キャッシュが適用されるため、レイヤーが絶えず変更される場合は、帯域幅、待機時間、コンピューティング時間や、テストされていないビルド/レイヤーの副作用を引き起すことに注意してください)。 このモジュール用に、実行時に FlightBookingSystemSample-0.0.1-SNAPSHOT.war で動作することがテスト済みの特定の Maven、Tomcat、Java JRE/JDK タグを事前に選択しています。

Dockerfile の構築について詳しくは、「Dockerfile reference」を参照してください