在 Azure 容器应用中使用 Quarkus 部署 Java 应用程序

本文介绍如何使用简单的 CRUD 应用程序在 Microsoft Azure 容器应用中快速部署 Red Hat Quarkus。 该应用程序是具有 JavaScript 前端和 REST 终结点的“待办事项列表”。 Azure Database for PostgreSQL 为应用提供持久性层。 本文介绍如何在本地测试应用并将其部署到容器应用。

先决条件

  • 如果还没有 Azure 订阅,可以在开始前创建一个免费帐户
  • Azure Cloud Shell 预先安装了所有的先决条件。 更多内容请参阅《Azure Cloud Shell 快速入门》。
  • 如果在本地(而不是使用 Azure Cloud Shell)中运行本指南中的命令,请完成以下步骤:
    • 准备一台安装了类似于 Unix 的操作系统(例如 Ubuntu、macOS 或适用于 Linux 的 Windows 子系统)的本地计算机。
    • 安装 Java 标准版实现版本 17 或更高版本(例如 OpenJDK 的 Microsoft 内部版本)。
    • 安装 Maven 3.5.0 或更高版本。
    • 安装适用于 OS 的 DockerPodman
    • 安装 jq
    • 安装 cURL
    • 安装 Quarkus CLI 3.4.1 或更高版本。
  • 适用于 Unix 类环境的 Azure CLI。 本文仅需要 Azure CLI 的 Bash 环境。
    • 安装 Azure CLI 并使用 az login 命令以交互方式登录,以在代码中使用DefaultAzureCredential之前登录到 Azure。
      az login
      
    • 本文需要的 Azure CLI 最低版本为 2.31.0。 如果你使用的是 Azure Cloud Shell,则表示已安装最新版本。

创建应用项目

使用以下命令克隆本文的示例 Java 项目。 该示例位于 GitHub 上。

git clone https://github.com/Azure-Samples/quarkus-azure
cd quarkus-azure
git checkout 2023-09-13
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。

Screenshot of the Todo sample app.

请尝试在待办事项列表中选择待办事项。 UI 用删除线文本样式提示选择。 还可以通过键入“验证待办事项应用”并按 ENTER 键将新的待办事项添加到待办事项列表,如以下屏幕截图所示:

Screenshot of the Todo sample app with new items added.

访问 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 示例应用:

  • Microsoft Azure Database for PostgreSQL
  • Microsoft Azure 容器注册表
  • 容器应用

其中一些资源必须在 Azure 订阅范围内具有唯一的名称。 若要确保这种唯一性,可以使用首字母缩写、序列、日期、后缀模式。 若要应用此模式,请通过首字母缩写、序列号、当日日期和特定资源后缀来命名资源,例如,rg 表示“资源组”。 使用以下命令定义环境变量,以便稍后使用:

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_PASSWORD=Secret123456
export ACA_ENV=${UNIQUE_VALUE}env
export ACA_NAME=${UNIQUE_VALUE}aca

创建用于 PostgreSQL 的 Azure 数据库

Azure Database for PostgreSQL 是一种托管服务,可在 Azure 云中运行、管理和缩放具有高可用性的 PostgreSQL 数据库。 本部分导向单独的快速入门,介绍如何创建单个 Azure Database for PostgreSQL 服务器并与其连接。 但是,按照快速入门中的步骤操作时,需要使用下表中的设置来自定义示例 Quarkus 应用的数据库部署。 填写 Azure 门户中的字段时,请将环境变量替换为实际值。

设置 说明
资源组 ${RESOURCE_GROUP_NAME} 选择“新建”。 部署会创建该新资源组。
服务器名称 ${DB_SERVER_NAME} 此值构成数据库服务器主机名的一部分。
位置 ${LOCATION} 从下拉列表中选择一个位置。 请记下位置。 其他创建的 Azure 资源必须使用此相同位置。
管理员用户名 quarkus 示例代码假定使用此值。
密码 ${DB_PASSWORD} 密码必须至少为 8 个字符,最多 128 个字符。 密码必须含以下字符类别中的三类 – 英文大写字母、英文小写字母、数字(0-9)及非字母数字字符(!、$、#、% 等)。 密码不能包含登录名的所有或部分。 登录名称的一部分定义为三个或多个连续字母数字字符。

脑中记得替换这些数据,同时请按照《快速入门:使用Azure 门户创建 Azure Database for PostgreSQL 服务器》中的步骤操作,直到“配置防火墙规则”部分。 然后,在“配置防火墙规则”部分中,确保为“允许访问 Azure 服务”选择“是”,然后选择“保存”。 如果忽略此操作,Quarkus 应用则无法访问数据库,并且根本无法启动。

完成“配置防火墙规则”部分的所有快速入门步骤(包括允许访问 Azure 服务的步骤)后,请返回到本文。

在 Azure Database for PostgreSQL 中创建 Todo 数据库

你在前面创建的 PostgreSQL 服务器为空。 没有任何可以与 Quarkus 应用程序配合使用的数据库。 使用以下命令创建名为 todo 的新数据库:

az postgres db create \
    --resource-group ${RESOURCE_GROUP_NAME} \
    --name todo \
    --server-name ${DB_SERVER_NAME}

必须使用 todo 作为数据库的名称,因为示例代码假定使用此数据库名称。

如果命令成功,输出将类似于以下示例:

{
  "charset": "UTF8",
  "collation": "English_United States.1252",
  "id": "/subscriptions/REDACTED/resourceGroups/ejb091223rg/providers/Microsoft.DBforPostgreSQL/servers/ejb091223db/databases/todo",
  "name": "todo",
  "resourceGroup": "ejb091223rg",
  "type": "Microsoft.DBforPostgreSQL/servers/databases"
}

创建 Microsoft Azure 容器注册表实例

由于 Quarkus 是云原生技术,因此它内置支持创建在容器应用中运行的容器。 容器应用完全依赖于容器注册表,从中查找要运行的容器映像。 容器应用内置支持 Azure 容器注册表。

使用 az acr create 命令创建容器注册表实例。 以下示例创建名为环境变量值的 ${REGISTRY_NAME}n 个容器注册表实例:

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 连接到容器注册表实例

登录到容器注册表实例。 登录后可以推送映像。 运行以下命令验证连接:

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

如果使用 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

若要验证是否已添加扩展,可以运行 git diff 检查输出。

作为一项云原生技术,Quarkus 支持配置文件概念。 Quarkus 拥有以下三个内置的配置文件:

  • dev - 在开发模式下激活。
  • test - 在运行测试时激活。
  • prod - 在开发或测试模式下未运行时的默认配置文件。

Quarkus 根据需要支持任意数量的命名配置文件。

本部分中的剩余步骤指导取消注释和自定义 src/main/resources/application.properties 文件中的值。 通过删除前导 # %prod.,确保取消所有以 # 开头行的注释。

%prod. 前缀表明在 prod 配置文件中运行时这些属性处于活动状态。 有关配置文件的详细信息,请参阅Quarkus 文档

自定义数据库配置

添加以下数据库配置变量。 分别将<DB_SERVER_NAME_VALUE>值替换为环境变量<DB_PASSWORD_VALUE>${DB_PASSWORD}和环境变量的实际值${DB_SERVER_NAME}

# Database configurations
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://<DB_SERVER_NAME_VALUE>.postgres.database.azure.com:5432/todo
%prod.quarkus.datasource.jdbc.driver=org.postgresql.Driver
%prod.quarkus.datasource.username=quarkus@<DB_SERVER_NAME_VALUE>
%prod.quarkus.datasource.password=<DB_PASSWORD_VALUE>
%prod.quarkus.hibernate-orm.database.generation=create
%prod.quarkus.hibernate-orm.sql-load-script=no-file

通常,你不希望删除数据库中保存的数据,并在生产环境中使用示例数据重新填充。 因此,可以看到指定的quarkus.hibernate-orm.database.generationcreate架构,以便应用在初始启动时仅创建架构。 此外,数据库不会预先填充任何示例数据,因为 hibernate-orm.sql-load-script 指定为 no-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 文件,并且属性值为 import.sql,则此文件中的 hibernate-orm.sql-load-script 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

生成容器映像并将其推送到容器注册表

现在,运行以下命令以生成应用程序。 此命令使用 Jib 扩展生成容器映像。

quarkus build --no-tests

输出应 BUILD SUCCESS 结尾。

可以使用命令行podman(CLI)验证容器映像是否也docker生成。 输出与以下示例类似:

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

使用以下命令将容器映像推送到容器注册表:

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

将应用映像推送到容器注册表后,请使用以下命令创建容器应用实例,在从容器注册表拉取映像后运行该应用:

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 \
    --ingress 'external'

成功的输出是一个 JSON 对象,包括属性 "type": "Microsoft.App/containerApps"

使用以下命令获取访问 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

${QUARKUS_URL} 打开新的 Web 浏览器。 然后,添加一个文本内容为 Deployed the Todo app to Container Apps 的新待办事项。 选择此项可将其标记为已完成。

Screenshot of the Todo sample app running in Container Apps.

访问 RESTful API (/api) 以获取存储在 Azure Database for PostgreSQL 中的所有待办事项,如以下示例所示:

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=quarkus@${DB_SERVER_NAME} --dbname=todo

当系统询问密码时,请使用创建数据库时使用的值。

使用以下查询获取所有待办事项:

select * from todo;

输出应类似于以下示例,并且应包含和以上所示待办事项应用 GUI 中相同的项:

Screenshot of the query output as an ASCII table.

输入 \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 删除 postgres Quarkus 开发模式生成的和 testcontainers 容器映像。

后续步骤