在 Azure 容器应用上使用 Quarkus 部署 Java 应用程序
本文介绍如何使用简单的 CRUD 应用程序在 Microsoft Azure 容器应用上快速部署 Red Hat Quarkus。 该应用程序是具有 JavaScript 前端和 REST 终结点的“待办事项列表”。 Azure Database for PostgreSQL 灵活服务器为应用提供暂留层。 本文介绍如何在本地测试应用并将其部署到容器应用。
先决条件
- Azure 订阅。 如果还没有 Azure 订阅,可以在开始前创建一个免费帐户。
- 准备一台安装了类似于 Unix 的操作系统(例如 Ubuntu、macOS 或适用于 Linux 的 Windows 子系统)的本地计算机。
- 安装 Java SE 实现版本 17 或更高版本 - 例如,Microsoft 版 OpenJDK。
- 安装 Maven 3.9.8 或更高版本。
- 安装适用于 OS 的 Docker 或Podman。
- 安装 jq。
- 安装 cURL。
- 安装 Quarkus CLI 3.12.1 或更高版本。
- 安装 Azure CLI 以运行 Azure CLI 命令。
- 通过使用 az login 命令登录到 Azure CLI。 若要完成身份验证过程,请遵循终端中显示的步骤。 有关其他登录选项,请参阅使用 Azure CLI 登录到 Azure。
- 出现提示时,请在首次使用时安装 Azure CLI 扩展。 有关扩展详细信息,请参阅使用和管理 Azure CLI 的扩展。
- 运行 az version 以查找安装的版本和依赖库。 若要升级到最新版本,请运行 az upgrade。 本文需要的 Azure CLI 最低版本为 2.61.0。
创建应用项目
使用以下命令克隆本文的示例 Java 项目。 该示例位于 GitHub 上。
git clone https://github.com/Azure-Samples/quarkus-azure
cd quarkus-azure
git checkout 2024-10-14
cd aca-quarkus
如果看到有关处于拆离的 HEAD 状态的消息,可以放心忽略此消息。 由于本文不需要任何提交,因此拆离的 HEAD 状态是合适的。
在本地测试 Quarkus 应用
本部分的步骤介绍如何在本地运行应用。
Quarkus 支持在开发和测试模式下自动预配未配置的服务。 Quarkus 将此功能称为开发服务。 假设你有 Quarkus 功能,例如连接到数据库服务。 你想要测试应用,但尚未完全配置与真实数据库的连接。 Quarkus 会自动启动相关服务的存根版本,并将应用程序连接到该服务。 更多信息请参阅 Quarkus 文档中的《开发服务概述》 。
请确保容器环境(Docker 或 Podman)正在运行,并使用以下命令进入 Quarkus 开发模式:
quarkus dev
使用 mvn quarkus:dev
取代 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。
请尝试在待办事项列表中选择待办事项。 UI 用删除线文本样式提示选择。 还可以通过键入“验证待办事项应用”并按 ENTER 键将新的待办事项添加到待办事项列表,如以下屏幕截图所示:
访问 RESTful API (/api
) 以获取存储在本地 PostgreSQL 数据库中的所有待办事项:
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://learn.microsoft.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://learn.microsoft.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://learn.microsoft.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 容器注册表
- Azure Container Apps
其中一些资源必须在 Azure 订阅范围内具有唯一的名称。 若要确保这种唯一性,可以使用首字母缩写、序列、日期、后缀模式。 若要应用此模式,请通过首字母缩写、序列号、当日日期和特定资源后缀来命名资源,例如,rg
表示“资源组”。 以下环境变量使用此模式。 将占位符值替换为自己的值UNIQUE_VALUE
LOCATION
,并在终端中运行命令。
export UNIQUE_VALUE=<your unique value, such as mjg101424>
export RESOURCE_GROUP_NAME=${UNIQUE_VALUE}rg-passwordless
export LOCATION=<your desired Azure region for deploying your resources - for example, eastus>
export REGISTRY_NAME=${UNIQUE_VALUE}regpasswordless
export DB_SERVER_NAME=${UNIQUE_VALUE}dbpasswordless
export DB_NAME=demodb
export ACA_ENV=${UNIQUE_VALUE}envpasswordless
export ACA_NAME=${UNIQUE_VALUE}acapasswordless
接下来,请使用以下命令创建资源组:
az group create \
--name $RESOURCE_GROUP_NAME \
--location $LOCATION
创建 Azure Database for PostgreSQL 灵活服务器
Azure Database for PostgreSQL 灵活服务器是一种完全托管的数据库服务,旨在为数据库管理功能和配置设置提供更精细的控制和灵活性。 本部分介绍如何使用 Azure CLI 创建 Azure Database for PostgreSQL 灵活服务器实例。 若要了解更多信息,请参考快速入门:使用 Azure CLI 创建 Azure Database for PostgreSQL 灵活服务器实例。
使用以下命令创建 Azure Database for PostgreSQL 灵活服务器实例:
az postgres flexible-server create \
--name $DB_SERVER_NAME \
--resource-group $RESOURCE_GROUP_NAME \
--database-name $DB_NAME \
--public-access None \
--sku-name Standard_B1ms \
--tier Burstable \
--active-directory-auth Enabled
创建服务器、数据库、管理员用户和防火墙规则需要几分钟时间。 如果命令成功,输出将类似于以下示例:
{
"connectionString": "postgresql://REDACTED:REDACTED@<DB_SERVER_NAME>.postgres.database.azure.com/<DB_NAME>?sslmode=require",
"databaseName": "<DB_NAME>",
"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": "REDACTED",
"resourceGroup": "<RESOURCE_GROUP_NAME>",
"skuname": "Standard_B1ms",
"username": "REDACTED",
"version": "13"
}
使用以下命令将当前已登录用户添加为 Microsoft Entra Admin 的 Azure Database for PostgreSQL 灵活服务器实例:
ENTRA_ADMIN_NAME=$(az ad signed-in-user show --query userPrincipalName -o tsv)
az postgres flexible-server ad-admin create \
--resource-group $RESOURCE_GROUP_NAME \
--server-name $DB_SERVER_NAME \
--display-name $ENTRA_ADMIN_NAME \
--object-id $(az ad signed-in-user show --query id -o tsv)
成功的输出是一个包含属性 "type": "Microsoft.DBforPostgreSQL/flexibleServers/administrators"
的 JSON 对象。
创建 Microsoft Azure 容器注册表实例
由于 Quarkus 是一种云原生技术,因此它内置了对创建在容器应用中运行的容器的支持。 容器应用完全依赖于拥有一个容器注册表,从中可以找到要运行的容器映像。 容器应用内置了对 Azure 容器注册表的支持。
使用 az acr create 命令以创建容器注册表实例。 以下示例创建 n 个以环境变量 ${REGISTRY_NAME}
的值命名的容器注册表实例:
az acr create \
--resource-group $RESOURCE_GROUP_NAME \
--location ${LOCATION} \
--name $REGISTRY_NAME \
--sku Basic
很快,你应该会在 JSON 输出中看到以下行:
"provisioningState": "Succeeded",
"publicNetworkAccess": "Enabled",
"resourceGroup": "<YOUR_RESOURCE_GROUP>",
使用以下命令获取容器注册表实例的登录服务器:
export LOGIN_SERVER=$(az acr show \
--name $REGISTRY_NAME \
--query 'loginServer' \
--output tsv)
echo $LOGIN_SERVER
将 Docker 连接到容器注册表实例
登录到 Azure 容器注册表实例。 登录后可以推送映像。 使用以下命令登录到注册表:
az acr login --name $REGISTRY_NAME
如果使用 Podman 而不是 Docker,请对命令进行必要的更改。
如果已成功登录到容器注册表实例,则应在命令输出的末尾看到 Login Succeeded
。
创建环境
Azure 容器应用中的环境围绕一组容器应用创建安全边界。 部署到相同环境的容器应用部署在同一虚拟网络中,并将日志写入同一个 Log Analytics 工作区。 使用 az containerapp env create 命令创建环境,如以下示例所示:
az containerapp env create \
--resource-group $RESOURCE_GROUP_NAME \
--location $LOCATION \
--name $ACA_ENV
如果系统要求你安装扩展,请回答 Y。
自定义云原生配置
作为一种云原生技术,Quarkus 提供了自动生成容器映像的能力。 有关详细信息,请参阅容器映像。 然后,开发人员可以将应用程序映像部署到目标容器化平台,例如 Azure 容器应用。
若要生成容器映像,请使用以下命令在本地终端中添加 container-image-jib
扩展:
quarkus ext add container-image-jib
Quarkus 修改 POM,以确保扩展包含在 <dependencies>
中。 如果系统要求你安装名为 JBang
的内容,请回答是并允许安装。
输出应如以下示例所示:
[SUCCESS] ✅ Extension io.quarkus:quarkus-container-image-jib has been installed
打开pom.xml文件,应会看到扩展添加的container-image-jib
以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
然后,将以下依赖项添加到 pom.xml 文件中,以支持使用 Azure Database for PostgreSQL 灵活服务器进行无密码身份验证:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity-extensions</artifactId>
<version>1.1.20</version>
</dependency>
作为一项云原生技术,Quarkus 支持配置文件概念。 Quarkus 拥有以下三个内置的配置文件:
dev
- 处于开发模式时激活。test
- 在运行测试时激活。prod
- 未在开发或测试模式下运行时的默认配置文件。
Quarkus 根据需要支持任意数量的命名配置文件。
本部分中的剩余步骤指导取消注释和自定义 src/main/resources/application.properties 文件中的值。 通过删除前导 # %prod.
,确保取消所有以 #
开头行的注释。
%prod.
前缀表明在 prod
配置文件中运行时这些属性处于活动状态。 有关配置文件的详细信息,请参阅Quarkus 文档。
检查数据库配置
取消注释属性后,src/main/resources/application.properties 文件中的数据库配置应如以下示例所示:
# Database configurations
%prod.quarkus.datasource.db-kind=postgresql
%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
删除属性 %prod.quarkus.datasource.password
,因为在 Azure Database for PostgreSQL 灵活服务器中使用无密码身份验证时不需要此属性。 使用以下示例所示更新其他数据库连接相关属性 %prod.quarkus.datasource.jdbc.url
和 %prod.quarkus.datasource.username
值。 最终配置应如以下示例所示:
# Database configurations
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://${AZURE_POSTGRESQL_HOST}:${AZURE_POSTGRESQL_PORT}/${AZURE_POSTGRESQL_DATABASE}?\
authenticationPluginClassName=com.azure.identity.extensions.jdbc.postgresql.AzurePostgresqlAuthenticationPlugin\
&sslmode=require
%prod.quarkus.datasource.username=${AZURE_POSTGRESQL_USERNAME}
%prod.quarkus.hibernate-orm.database.generation=create
%prod.quarkus.hibernate-orm.sql-load-script=no-file
本文后面的服务连接器无密码扩展在运行时由 Azure 容器应用环境提供的值${AZURE_POSTGRESQL_HOST}
${AZURE_POSTGRESQL_DATABASE}
${AZURE_POSTGRESQL_PORT}
${AZURE_POSTGRESQL_USERNAME}
。
通常,你不会期望数据库中持久化的数据被删除,并在生产环境中用示例数据重新填充。 这就是为什么你可以看到 quarkus.hibernate-orm.database.generation
的模式被指定为 create
,这样应用只有在初始启动时不存在模式时才会创建模式。 此外,数据库不会预先填充任何示例数据,因为 hibernate-orm.sql-load-script
被指定为 no-file
。 此设置与之前在开发模式下在本地运行应用时不同。 在开发模式下,quarkus.hibernate-orm.database.generation
和 hibernate-orm.sql-load-script
的默认值分别为 drop-and-create
和 import.sql
,这意味着应用总是删除并重新创建数据库架构,并加载 import.sql 中定义的数据。 import.sql 文件是 Quarkus 提供的一个便利工具。 如果 Quarkus jar 中存在 src/main/resources/import.sql 文件,并且 hibernate-orm.sql-load-script
属性的值为 import.sql
,则此文件中的 SQL DML 语句将在应用启动时执行。
使用 Azure Database for PostgreSQL 灵活服务器在本地测试 Quarkus 应用
将 Quarkus 应用部署到 Azure 容器应用之前,请在本地测试与 Azure Database for PostgreSQL 灵活服务器实例的连接。
首先,使用以下命令将本地 IP 地址添加到 Azure Database for PostgreSQL 灵活服务器实例防火墙规则:
export AZ_LOCAL_IP_ADDRESS=$(curl -s https://whatismyip.akamai.com)
az postgres flexible-server firewall-rule create \
--resource-group $RESOURCE_GROUP_NAME \
--name $DB_SERVER_NAME \
--rule-name $DB_SERVER_NAME-database-allow-local-ip \
--start-ip-address $AZ_LOCAL_IP_ADDRESS \
--end-ip-address $AZ_LOCAL_IP_ADDRESS
接下来,在上一终端中设置以下环境变量。 这些环境变量用于从本地运行的 Quarkus 应用连接到 Azure Database for PostgreSQL 灵活服务器实例:
export AZURE_POSTGRESQL_HOST=${DB_SERVER_NAME}.postgres.database.azure.com
export AZURE_POSTGRESQL_PORT=5432
export AZURE_POSTGRESQL_DATABASE=${DB_NAME}
export AZURE_POSTGRESQL_USERNAME=${ENTRA_ADMIN_NAME}
在本地运行 Quarkus 应用以测试与 Azure Database for PostgreSQL 灵活服务器实例的连接。 使用以下命令在生产模式下启动应用:
mvn clean package -DskipTests
java -jar target/quarkus-app/quarkus-run.jar
打开新的 Web 浏览器以 http://localhost:8080
访问 Todo 应用程序。 在开发模式下本地运行应用时,应会看到与在本地运行应用时相同的 Todo 应用,而无需任何待办事项。
按 Control+C 停止应用。
生成容器映像并将其推送到容器注册表
现在,运行以下命令以生成应用程序。 此命令使用 Jib 扩展来生成容器映像。
export TODO_QUARKUS_IMAGE_NAME=todo-quarkus-aca
export TODO_QUARKUS_IMAGE_TAG=${LOGIN_SERVER}/${TODO_QUARKUS_IMAGE_NAME}:1.0
quarkus build -Dquarkus.container-image.build=true -Dquarkus.container-image.image=${TODO_QUARKUS_IMAGE_TAG} --no-tests
输出应 BUILD SUCCESS
结尾。
可以使用命令行podman
(CLI)验证容器映像是否也docker
生成,如以下示例所示:
docker images | grep ${TODO_QUARKUS_IMAGE_NAME}
输出与以下示例类似:
<LOGIN_SERVER_VALUE>/todo-quarkus-aca 1.0 0804dfd834fd 2 minutes ago 407MB
使用以下命令将容器映像推送到容器注册表:
docker push ${TODO_QUARKUS_IMAGE_TAG}
输出应类似于以下示例:
The push refers to repository [<LOGIN_SERVER_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
将 Quarkus 应用部署到 Azure 容器应用
将应用映像推送到容器注册表后,请使用以下命令创建容器应用实例,在从容器注册表拉取映像后运行该应用:
az containerapp create \
--resource-group $RESOURCE_GROUP_NAME \
--name $ACA_NAME \
--image $TODO_QUARKUS_IMAGE_TAG \
--environment $ACA_ENV \
--registry-server $LOGIN_SERVER \
--registry-identity system \
--target-port 8080 \
--ingress 'external' \
--min-replicas 1
成功的输出是一个包含属性 "type": "Microsoft.App/containerApps"
的 JSON 对象。
然后,使用服务连接器将 Azure Database for PostgreSQL 灵活服务器实例连接到容器应用,请执行以下步骤:
使用以下命令安装 Azure CLI 的服务连接器无密码扩展:
az extension add --name serviceconnector-passwordless --upgrade --allow-preview true
使用以下命令将数据库连接到具有系统分配的托管标识的容器应用:
az containerapp connection create postgres-flexible \ --resource-group $RESOURCE_GROUP_NAME \ --name $ACA_NAME \ --target-resource-group $RESOURCE_GROUP_NAME \ --server $DB_SERVER_NAME \ --database $DB_NAME \ --system-identity \ --container $ACA_NAME
成功的输出是一个包含属性
"type": "microsoft.servicelinker/linkers"
的 JSON 对象。
使用以下命令获取完全限定的 URL 以访问 Todo 应用程序:
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
为 ${QUARKUS_URL}
打开新的 Web 浏览器。 如果网页未正确呈现,请等待一段时间并刷新页面。
然后,添加一个文本内容为 Deployed the Todo app to Container Apps
的新待办事项。 选择此项目以将其标记为已完成。
访问 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
}
]
验证数据库是否已更新
运行以下命令,验证数据库是否已使用新的待办事项进行更新:
export ACCESS_TOKEN=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken)
az postgres flexible-server execute \
--admin-user $ENTRA_ADMIN_NAME \
--admin-password $ACCESS_TOKEN \
--name $DB_SERVER_NAME \
--database-name $DB_NAME \
--querytext "select * from todo;"
如果系统要求你安装扩展,请回答 Y。
输出应类似于以下示例,并且应该在前面所示的 Todo 应用 GUI 中包含相同的项:
Successfully connected to <DB_SERVER_NAME>.
Ran Database Query: 'select * from todo;'
Retrieving first 30 rows of query output, if applicable.
Closed the connection to <DB_SERVER_NAME>
[
{
"completed": true,
"id": 1,
"ordering": 1,
"title": "Deployed the Todo app to Container Apps",
"url": null
}
]
完成后,请使用以下命令删除允许本地 IP 地址访问 Azure Database for PostgreSQL 灵活服务器实例的防火墙规则:
az postgres flexible-server firewall-rule delete \
--resource-group $RESOURCE_GROUP_NAME \
--name $DB_SERVER_NAME \
--rule-name $DB_SERVER_NAME-database-allow-local-ip \
--yes
清理资源
若要避免 Azure 费用,应清除不需要的资源。 如果不再需要群集,请使用 az group delete 命令来删除资源组、容器服务、容器注册表和所有的相关资源。
git reset --hard
docker rmi ${TODO_QUARKUS_IMAGE_TAG}
az group delete --name $RESOURCE_GROUP_NAME --yes --no-wait
你可能还想使用 docker rmi
删除 Quarkus 开发模式生成的 postgres
和 testcontainers
容器映像。