Azure Container Apps に Quarkus を使用する Java アプリケーションをデプロイする

この記事では、シンプルな CRUD アプリケーションを使用して、Microsoft Azure Container Apps に Red Hat Quarkus を迅速にデプロイする方法を説明します。 このアプリケーションは、JavaScript フロントエンドと REST エンドポイントを備えた "To Do リスト" です。 Azure Database for PostgreSQL フレキシブル サーバーは、アプリの永続化レイヤーを提供します。 この記事では、アプリをローカルでテストし、Container Apps にデプロイする方法を説明します。

前提条件

  • Azure サブスクリプション。 Azure サブスクリプションをお持ちでない場合は、開始する前に 無料アカウント を作成してください。
  • Ubuntu、macOS、Windows Subsystem for Linux などの Unix 系オペレーティング システムがインストールされたローカル マシンを準備します。
  • Java Standard Edition 実装バージョン 17 以降をインストールします (例: OpenJDK の Microsoft ビルド)。
  • Maven、バージョン3.9.8以上をインストールします。
  • お使いの OS 用の Docker または Podman をインストールします。
  • jq をインストールします。
  • cURL をインストールします。
  • Quarkus CLI、バージョン3.12.1以降をインストールします。
  • Unix に似た環境用の Azure CLI。 この記事では、Azure CLI の Bash バリアントのみが必要です。
    • Azure CLI をインストールし、 az login コマンドを使って対話形式でサインインし、コードで DefaultAzureCredential を使用する前に Azure にログインします。
      az login
      
    • この記事では、バージョン 2.61.0 以上の Azure CLI が必要です。

アプリ プロジェクトを作成する

次のコマンドを使って、この記事用のサンプル Java プロジェクトをクローンします。 サンプルは GitHub にあります。

git clone https://github.com/Azure-Samples/quarkus-azure
cd quarkus-azure
git checkout 2024-07-08
cd aca-quarkus

HEAD がデタッチされた 状態であることを示すメッセージが表示された場合、このメッセージは無視しても問題ありません。 この記事ではコミットを必要としないため、HEAD がデタッチされた状態が適切です。

Quarkus アプリをローカルでテストする

このセクションの手順では、アプリをローカルで実行する方法を示します。

Quarkus は、開発モードおよびテスト モードでの未構成のサービスの自動プロビジョニングをサポートしています。 Quarkus は、この機能を開発サービスと呼びます。 データベース サービスへの接続などの Quarkus 機能が含まれているとします。 アプリをテストしたいと考えていますが、実際のデータベースへの接続がまだ完全に構成されていません。 Quarkus は、関連するサービスのスタブ バージョンを自動的に開始し、アプリケーションをそれに接続します。 詳細については、Quarkus ドキュメントの「Dev Services の概要」を参照してください。

コンテナー環境 (Docker または Podman) が実行されていることを確認し、次のコマンドを使用して Quarkus 開発モードに入ります。

quarkus dev

quarkus dev の代わりに、 mvn quarkus:dev を使用すると、Maven で同じことを実現できます。

Quarkus 開発モードの使用状況のテレメトリを送信するかどうかを尋ねられる場合があります。 その場合は、好きなように答えてください。

Quarkus 開発モードでは、バックグラウンド コンパイルによるライブ リロードが有効になります。 アプリのソース コードの一部を変更してブラウザーを更新すると、変更内容を確認できます。 コンパイルまたはデプロイに問題がある場合は、エラー ページが表示されます。 Quarkus 開発モードは、ポート 5005 でデバッガーをリッスンします。 デバッガーが接続されるのを待ってから実行する場合は、コマンド ラインで -Dsuspend を渡します。 デバッガーがまったく不要な場合は、 -Ddebug=falseを使用できます。

出力は次の例のようになります。

__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
INFO  [io.quarkus] (Quarkus Main Thread) quarkus-todo-demo-app-aca 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.2.0.Final) started in 14.826s. Listening on: http://localhost:8080
INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-validator, jdbc-postgresql, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vertx]

--
Tests paused
Press [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>

Quarkus 開発モードが実行されているターミナルで w キーを押します。 w キーを押すとデフォルトの Web ブラウザーが開き、 Todo アプリケーションが表示されます。 http://localhost:8080 にあるアプリケーション GUI に直接アクセスすることもできます。

Todo サンプル アプリのスクリーンショット。

Todo リストで Todo 項目をいくつか選択してみてください。 UI は取り消し線のテキスト スタイルで選択を示します。 次のスクリーンショットに示すように、「Todo アプリの確認」と入力して Enter キー を押して、新しい Todo 項目を Todo リストに追加することもできます。

新しい項目が追加された Todo サンプル アプリのスクリーンショット。

RESTful API (/api) にアクセスして、ローカル PostgreSQL データベースに保存されているすべての ToDo 項目を取得します。

curl --verbose http://localhost:8080/api | jq .

出力は次の例のようになります。

* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /api HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 664
< Content-Type: application/json;charset=UTF-8
<
{ [664 bytes data]
100   664  100   664    0     0  13278      0 --:--:-- --:--:-- --:--:-- 15441
* Connection #0 to host localhost left intact
[
  {
    "id": 1,
    "title": "Introduction to Quarkus Todo App",
    "completed": false,
    "order": 0,
    "url": null
  },
  {
    "id": 2,
    "title": "Quarkus on Azure App Service",
    "completed": false,
    "order": 1,
    "url": "https://video2.skills-academy.com/en-us/azure/developer/java/eclipse-microprofile/deploy-microprofile-quarkus-java-app-with-maven-plugin"
  },
  {
    "id": 3,
    "title": "Quarkus on Azure Container Apps",
    "completed": false,
    "order": 2,
    "url": "https://video2.skills-academy.com/en-us/training/modules/deploy-java-quarkus-azure-container-app-postgres/"
  },
  {
    "id": 4,
    "title": "Quarkus on Azure Functions",
    "completed": false,
    "order": 3,
    "url": "https://video2.skills-academy.com/en-us/azure/azure-functions/functions-create-first-quarkus"
  },
  {
    "id": 5,
    "title": "Verify Todo apps",
    "completed": false,
    "order": 5,
    "url": null
  }
]

q キーを押して Quarkus 開発モードを終了します。

Azure リソースを作成して Quarkus アプリを実行する

このセクションの手順では、次の Azure リソースを作成して Quarkus サンプル アプリを実行する方法を示します。

  • Azure Database for PostgreSQL フレキシブル サーバー
  • Azure Container Registry
  • Azure Container Apps

これらのリソースの一部は、Azure サブスクリプションのスコープ内で一意の名前を持つ必要があります。 この一意性を確保するために、 イニシャル、シーケンス、日付、サフィックス パターンを使用できます。 このパターンを適用するには、イニシャル、シーケンス番号、今日の日付、およびリソース固有のサフィックス (たとえば、"リソース グループ" の場合は rg) を一覧表示してリソースに名前を付けます。 次の環境変数はこのパターンを使用します。 UNIQUE_VALUELOCATION および DB_PASSWORD のプレースホルダー値を独自の値に置き換え、ターミナルでコマンドを実行します。

export UNIQUE_VALUE=<your unique value, such as ejb091223>
export RESOURCE_GROUP_NAME=${UNIQUE_VALUE}rg
export LOCATION=<your desired Azure region for deploying your resources - for example, eastus>
export REGISTRY_NAME=${UNIQUE_VALUE}reg
export DB_SERVER_NAME=${UNIQUE_VALUE}db
export DB_NAME=demodb
export DB_ADMIN=demouser
export DB_PASSWORD='<your desired password for the database server - for example, Secret123456>'
export ACA_ENV=${UNIQUE_VALUE}env
export ACA_NAME=${UNIQUE_VALUE}aca

Azure Database for PostgreSQL フレキシブル サーバーを作成する

Azure Database for PostgreSQL フレキシブル サーバーは、データベース管理機能と構成設定のよりきめ細かな制御と柔軟性を提供するように設計されたフル マネージド データベース サービスです。 このセクションでは、Azure CLI を使用して Azure Database for PostgreSQL フレキシブル サーバー インスタンスを作成する方法について説明します。 詳細については、「クイックスタート: Azure CLI を使用して Azure Database for PostgreSQL - フレキシブル サーバー インスタンスを作成する」を参照してください。

まず、次のコマンドを使用して、データベース サーバーとその他のリソースを含むリソース グループを作成します。

az group create \
    --name $RESOURCE_GROUP_NAME \
    --location $LOCATION

次に、次のコマンドを使用して、Azure Database for PostgreSQL フレキシブル サーバー インスタンスを作成します。

az postgres flexible-server create \
    --name $DB_SERVER_NAME \
    --resource-group $RESOURCE_GROUP_NAME \
    --admin-user $DB_ADMIN \
    --admin-password $DB_PASSWORD \
    --database-name $DB_NAME \
    --public-access 0.0.0.0 \
    --yes

サーバー、データベース、管理者ユーザー、ファイアウォール ルールの作成には数分かかります。 コマンドが成功した場合、出力は次の例のようになります。

{
  "connectionString": "postgresql://<DB_ADMIN>:<DB_PASSWORD>@<DB_SERVER_NAME>.postgres.database.azure.com/<DB_NAME>?sslmode=require",
  "databaseName": "<DB_NAME>",
  "firewallName": "AllowAllAzureServicesAndResourcesWithinAzureIps_2024-7-5_14-39-45",
  "host": "<DB_SERVER_NAME>.postgres.database.azure.com",
  "id": "/subscriptions/REDACTED/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.DBforPostgreSQL/flexibleServers/<DB_SERVER_NAME>",
  "location": "East US",
  "password": "<DB_PASSWORD>",
  "resourceGroup": "<RESOURCE_GROUP_NAME>",
  "skuname": "Standard_D2s_v3",
  "username": "<DB_ADMIN>",
  "version": "13"
}

Microsoft Azure Container Registry インスタンスを作成する

Quarkus はクラウド ネイティブ テクノロジーであるため、Container Apps で実行されるコンテナーを作成するためのサポートが組み込まれています。 Container Apps は、実行するコンテナー イメージを見つけるコンテナー レジストリの存在に完全に依存しています。 Container Apps には、Azure Container Registry のサポートが組み込まれています。

az acr create コマンドを使用して、Container Registry インスタンスを作成します。 次の例では、環境変数 ${REGISTRY_NAME}の値で名前が付けられた Container Registry インスタンスを作成します。

az acr create \
    --resource-group $RESOURCE_GROUP_NAME \
    --location ${LOCATION} \
    --name $REGISTRY_NAME \
    --sku Basic \
    --admin-enabled

しばらくすると、次の行を含む JSON 出力が表示されます。

  "provisioningState": "Succeeded",
  "publicNetworkAccess": "Enabled",
  "resourceGroup": "<YOUR_RESOURCE_GROUP>",

Dockerを Container Registry インスタンスに接続する

Container Registry インスタンスにサインインします。 サインインすると、イメージをプッシュできます。 次のコマンドを使用して、接続を確認します。

export LOGIN_SERVER=$(az acr show \
    --name $REGISTRY_NAME \
    --query 'loginServer' \
    --output tsv)
echo $LOGIN_SERVER
export USER_NAME=$(az acr credential show \
    --name $REGISTRY_NAME \
    --query 'username' \
    --output tsv)
echo $USER_NAME
export PASSWORD=$(az acr credential show \
    --name $REGISTRY_NAME \
    --query 'passwords[0].value' \
    --output tsv)
echo $PASSWORD
docker login $LOGIN_SERVER -u $USER_NAME -p $PASSWORD

Docker の代わりに Podman を使用している場合は、 コマンドに必要な変更を加えます。

Container Registry インスタンスに正常にサインインすると、コマンド出力の最後に Login Succeeded が表示されます。

環境の作成

Azure Container Apps 環境では、コンテナー アプリのグループを囲むセキュリティ保護された境界が作成されます。 同じ環境にデプロイされた Container Apps は、同じ仮想ネットワークにデプロイされ、同じ Log Analytics ワークスペースにログを書き込みます。 次の例に示すように、 az containerapp env create コマンドを使用して環境を作成します。

az containerapp env create \
    --resource-group $RESOURCE_GROUP_NAME \
    --location $LOCATION \
    --name $ACA_ENV

拡張機能をインストールするように求められたら、 Yと答えます。

クラウド ネイティブ構成をカスタマイズする

クラウド ネイティブ テクノロジーである Quarkus は、コンテナ イメージを自動的に生成する機能を提供します。 詳細については、 コンテナ イメージを参照してください。 Developer は、アプリケーション イメージをターゲットのコンテナー化されたプラットフォーム (Azure Container Apps など) にデプロイできます。

コンテナ イメージを生成するには、次のコマンドを使用して、ローカル ターミナルに container-image-jib 拡張機能を追加します。

quarkus ext add container-image-jib

Quarkus は POM を変更して、拡張機能が <dependencies>に含まれるようにします。 JBangと呼ばれるものをインストールするように求められたら、 はい と答えてインストールを許可します。

出力は次の例のようになります。

[SUCCESS] ✅  Extension io.quarkus:quarkus-container-image-jib has been installed

拡張機能が追加されたことを確認するには、 git diff を実行して出力を調べます。

Quarkus はクラウド ネイティブ テクノロジーとして、構成プロファイルの概念をサポートしています。 Quarkus には、次の 3 つの組み込みプロファイルがあります。

  • dev - 開発モードのときにアクティブになります。
  • test - テストを実行するときにアクティブになります。
  • prod - 開発モードまたはテストモードで実行されていない場合のデフォルト プロファイル。

Quarkus は、必要に応じて、任意の数の名前付きプロファイルをサポートします。

このセクションの残りの手順では、 src/main/resources/application.properties ファイルのコメントを解除して値をカスタマイズする方法を説明します。 先頭の #を削除して、 # %prod. で始まるすべての行のコメントが解除されていることを確認します。

%prod. プレフィックスは、 prod プロファイルでの実行時にこれらのプロパティがアクティブであることを示します。 構成プロファイルの詳細については、 Quarkus のドキュメントを参照してください。

データベース構成を調べる

次のデータベース構成変数を追加します。 データベース接続関連のプロパティ %prod.quarkus.datasource.jdbc.url%prod.quarkus.datasource.username、および %prod.quarkus.datasource.password は、セキュリティ上の理由から Azure Container Apps 環境によって実行時に提供されるため、意図的に空のままになっています。

# Database configurations
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.jdbc.driver=org.postgresql.Driver
%prod.quarkus.datasource.jdbc.url=
%prod.quarkus.datasource.username=
%prod.quarkus.datasource.password=
%prod.quarkus.hibernate-orm.database.generation=create
%prod.quarkus.hibernate-orm.sql-load-script=no-file

通常、データベースに保存されているデータが削除され、実稼働環境でサンプル データで再入力されることは想定されません。 そのため、 quarkus.hibernate-orm.database.generation のスキーマが create として指定され、アプリは最初の起動時にスキーマが存在しない場合にのみスキーマを作成することがわかります。 また、 hibernate-orm.sql-load-scriptno-fileとして指定されているため、データベースにはサンプル データが事前に入力されていません。 この設定は、以前に開発モードでアプリをローカルに実行したときとは異なります。 開発モードでの quarkus.hibernate-orm.database.generationhibernate-orm.sql-load-script のデフォルト値はそれぞれ drop-and-createimport.sql です。つまり、アプリは常にデータベース スキーマを削除して再作成し、 import.sql で定義されたデータを読み込みます。 import.sql ファイルは、Quarkus の便利な機能です。 Quarkus jar に src/main/resources/import.sql ファイルが存在し、 hibernate-orm.sql-load-script プロパティの値が import.sqlの場合、このファイル内の SQL DML ステートメントはアプリの起動時に実行されます。

コンテナイメージ構成をカスタマイズする

Quarkus は、クラウド ネイティブ テクノロジーとして、Docker および Podman と互換性のある OCI コンテナー イメージの生成をサポートしています。 次のコンテナー イメージ変数を追加します。 <LOGIN_SERVER_VALUE><USER_NAME_VALUE> の値を、それぞれ ${LOGIN_SERVER}${USER_NAME} 環境変数の実際の値に置き換えます。

# Container Image Build
%prod.quarkus.container-image.build=true
%prod.quarkus.container-image.registry=<LOGIN_SERVER_VALUE>
%prod.quarkus.container-image.group=<USER_NAME_VALUE>
%prod.quarkus.container-image.name=todo-quarkus-aca
%prod.quarkus.container-image.tag=1.0

コンテナイメージをビルドして Container Registry にプッシュする

次に、次のコマンドを使用してアプリケーション自体をビルドします。 このコマンドは Jib 拡張機能を使用してコンテナ イメージを構築します。

quarkus build --no-tests

出力は BUILD SUCCESS で終わる必要があります。

docker または podman コマンドライン (CLI) を使用して、コンテナ イメージが生成されているかどうかを確認することもできます。 出力は次の例のようになります。

docker images | grep todo-quarkus-aca
<LOGIN_SERVER_VALUE>/<USER_NAME_VALUE>/todo-quarkus-aca   1.0       0804dfd834fd   2 minutes ago   407MB

次のコマンドを使用して、コンテナ イメージを Container Registry にプッシュします。

export TODO_QUARKUS_TAG=$(docker images | grep todo-quarkus-aca | head -n1 | cut -d " " -f1):1.0
echo ${TODO_QUARKUS_TAG}
docker push ${TODO_QUARKUS_TAG}

出力は次の例のようになるはずです。

The push refers to repository [<LOGIN_SERVER_VALUE>/<USER_NAME_VALUE>/todo-quarkus-aca]
188a550fce3d: Pushed
4e3afea591e2: Pushed
1db0eba807a6: Pushed
c72d9ccda0b2: Pushed
d7819b8a2d18: Pushed
d0e5cba6b262: Pushed
e0bac91f0f10: Pushed
1.0: digest: sha256:f9ccb476e2388efa0dfdf817625a94f2247674148a69b7e4846793e63c8be994 size: 1789

アプリ イメージを Container Registry にプッシュしたので、Container Registry からイメージをプルした後、次のコマンドを使用して Container Apps インスタンスを作成し、アプリを実行します。

export DATASOURCE_JDBC_URL=jdbc:postgresql://${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require
az containerapp create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $ACA_NAME \
    --image $TODO_QUARKUS_TAG \
    --environment $ACA_ENV \
    --registry-server $LOGIN_SERVER \
    --registry-username $USER_NAME \
    --registry-password $PASSWORD \
    --target-port 8080 \
    --secrets \
        jdbcurl=${DATASOURCE_JDBC_URL} \
        dbusername=${DB_ADMIN} \
        dbpassword=${DB_PASSWORD} \
    --env-vars \
        QUARKUS_DATASOURCE_JDBC_URL=secretref:jdbcurl \
        QUARKUS_DATASOURCE_USERNAME=secretref:dbusername \
        QUARKUS_DATASOURCE_PASSWORD=secretref:dbpassword \
    --ingress 'external'

--secrets オプションは、データベース接続関連の環境変数 QUARKUS_DATASOURCE_JDBC_URLQUARKUS_DATASOURCE_USERNAME および QUARKUS_DATASOURCE_PASSWORDによって参照されるシークレットを作成するために使用されます。 これらの環境変数の値は、プロパティ %prod.quarkus.datasource.password%prod.quarkus.datasource.username および %prod.quarkus.datasource.passwordに渡されます。 Quarkus は、 application.properties ファイルに値がない場合、対応する環境変数から値を検索することを認識しています。

成功すると、プロパティ "type": "Microsoft.App/containerApps" を含む JSON オブジェクトが出力されます。

次のコマンドを使用して、Todo アプリケーションにアクセスするための完全修飾 URL を取得します。

export QUARKUS_URL=https://$(az containerapp show \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $ACA_NAME \
    --query properties.configuration.ingress.fqdn -o tsv)
echo $QUARKUS_URL

新しい Web ブラウザーを開いて値を ${QUARKUS_URL} に設定します。 次に、テキスト Deployed the Todo app to Container Apps を含む新しい Todo 項目を追加します。 この項目を選択すると、完了としてマークされます。

Container Apps で実行されている Todo サンプル アプリのスクリーンショット。

次の例に示すように、RESTful API (/api) にアクセスして、Azure Database for PostgreSQL に保存されているすべての ToDo 項目を取得します。

curl --verbose -k ${QUARKUS_URL}/api | jq .

出力は次の例のようになります。

* Connected to <aca-name>.<random-id>.eastus.azurecontainerapps.io (20.231.235.79) port 443 (#0)
> GET /api HTTP/2
> Host: <aca-name>.<random-id>.eastus.azurecontainerapps.io
> user-agent: curl/7.88.1
> accept: */*
>
< HTTP/2 200
< content-length: 88
< content-type: application/json;charset=UTF-8
<
[
  {
    "id": 1,
    "title": "Deployed the Todo app to Container Apps",
    "completed": true,
    "order": 1,
    "url": null
  }
]

Azure Cloud Shell を使用してデータベースが更新されたことを確認する

検索ボックスの横にある Cloud Shell アイコン ( ) を選択して、Azure ポータルで Azure Cloud Shell を開きます。

次のコマンドをローカルで実行し、結果を Azure Cloud Shell に貼り付けます。

echo psql --host=${DB_SERVER_NAME}.postgres.database.azure.com --port=5432 --username=${DB_ADMIN} --dbname=${DB_NAME}

パスワードを求められたら、データベースの作成時に使用した値を使用します。

次のクエリを使用して、すべての ToDo 項目を取得します。

select * from todo;

出力は次の例のようになり、前に示した Todo アプリ GUI に同じ項目が含まれているはずです。

ASCII テーブルとして出力されたクエリのスクリーンショット。

\q」と入力して、 psql プログラムを終了し、Cloud Shell に戻ります。

リソースをクリーンアップする

Azure の課金を回避するには、不要なリソースをクリーンアップする必要があります。 クラスターが必要なくなったら、 az group delete コマンドを使って、リソース グループ、コンテナー サービス、コンテナー レジストリ、およびすべての関連リソースを削除してください。

git reset --hard
docker rmi ${TODO_QUARKUS_TAG}
az group delete --name $RESOURCE_GROUP_NAME --yes --no-wait

また、 docker rmi を使用して、Quarkus 開発モードで生成された postgres および testcontainers コンテナ イメージを削除することもできます。

次のステップ