练习 - 将 Jakarta EE 应用程序部署到 Azure 应用服务上的 JBoss EAP

已完成

在本单元中,你将一个 Jakarta EE 应用程序部署到 Azure 应用服务上的 Red Hat JBoss Enterprise 应用程序平台(JBoss EAP)。 使用适用于 Azure 应用服务的 Maven 插件来配置项目、编译和部署应用程序,以及配置数据源。

配置应用

使用适用于 Azure 应用服务的 Maven 插件配置应用,请执行以下步骤:

  1. 使用以下命令以交互方式运行 Azure 插件的配置目标:

    ./mvnw com.microsoft.azure:azure-webapp-maven-plugin:2.13.0:config
    

    重要

    如果更改 MySQL 服务器的区域,则应将该区域与 Jakarta EE 应用程序服务器的区域匹配,以最大程度地减少延迟延迟。

  2. 使用下表中的值回答交互式提示:

    输入元素 价值
    Create new run configuration (Y/N) [Y]: Y
    Define value for OS [Linux]: Linux
    Define value for javaVersion [Java 17]: 1: Java 17
    Define value for runtimeStack: 3: Jbosseap 7
    Define value for pricingTier [P1v3]: P1v3
    Confirm (Y/N) [Y]: Y

    以下输出是典型的:

    [INFO] Saving configuration to pom.
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  03:00 min
    [INFO] Finished at: 2025-02-21T06:24:11+09:00
    [INFO] ------------------------------------------------------------------------
    

    使用 Maven 命令后,以下示例是 Maven pom.xml 文件的典型补充:

    <build>
      <finalName>ROOT</finalName>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
        <plugin>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-webapp-maven-plugin</artifactId>
            <version>2.13.0</version>
            <configuration>
                <schemaVersion>v2</schemaVersion>
                <resourceGroup>jakartaee-app-on-jboss-rg</resourceGroup>
                <appName>jakartaee-app-on-jboss</appName>
                <pricingTier>P1v3</pricingTier>
                <region>centralus</region>
                <runtime>
                    <os>Linux</os>
                    <javaVersion>Java 17</javaVersion>
                    <webContainer>Jbosseap 7</webContainer>
                </runtime>
                <deployment>
                    <resources>
                        <resource>
                            <directory>${project.basedir}/target</directory>
                            <includes>
                                <include>*.war</include>
                            </includes>
                        </resource>
                    </resources>
                </deployment>
            </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. 检查 <region> 文件中的元素。 如果其值与 MySQL 的安装位置不匹配,请将其更改为同一位置。

  4. 使用以下示例在 Azure 应用服务上的 JBoss EAP 8 环境中,将 webContainer 文件中的 值修改为 Jbosseap 8

    小窍门

    截至 2025 年 2 月,JBoss EAP 的最新可用版本为 8.0 Update 4.1。

    <runtime>
        <os>Linux</os>
        <javaVersion>Java 17</javaVersion>
        <webContainer>Jbosseap 8</webContainer> <!-- Change this value -->
    </runtime>
    
  5. 将以下 XML 添加到 <resources> 文件的 元素。 此配置用于部署启动文件,稍后在本单元中更新该文件。

    <resource>
      <type>startup</type>
      <directory>${project.basedir}/src/main/webapp/WEB-INF/</directory>
      <includes>
        <include>createMySQLDataSource.sh</include>
      </includes>
    </resource>
    

    资源 <type> 值为 startup 时,会将指定的脚本部署为 startup.sh 文件(适用于 Linux)或 startup.cmd 文件(适用于 Windows)。 部署位置为 /home/site/scripts/

    注释

    可以通过以下方法之一指定 type 部署选项和部署位置:

    • 如果未指定type=war,则将 WAR 文件部署到 path
    • type=war&path=webapps/<appname> 将 WAR 文件部署到 /home/site/wwwroot/webapps/<appname>
    • type=jar 将 WAR 文件部署到 /home/site/wwwroot/app.jar。 忽略 path 参数。
    • type=ear 将 WAR 文件部署到 /home/site/wwwroot/app.ear。 忽略 path 参数。
    • type=lib 将 JAR 部署到 /home/site/libs。 必须指定 path 参数。
    • type=static 将脚本部署到 /home/site/scripts。 必须指定 path 参数。
    • type=startup 在 Linux 上部署脚本为 startup.sh,或在 Windows 上部署为 startup.cmd。 该脚本部署到 /home/site/scripts/。 忽略 path 参数。
    • type=zip.zip 文件解压缩到 /home/site/wwwrootpath 参数是可选的。
  6. 请检查resourceGroupappName 元素在 pom.xml 文件中的值。

  7. 使用以下命令将 resourceGroupappName 的值分配给环境变量:

    export RESOURCE_GROUP_NAME=<resource-group>
    export WEB_APP_NAME=<app-name>
    

编译并生成 Jakarta EE 应用

配置 Azure 应用服务部署设置后,使用以下命令编译和打包源代码:

./mvnw clean package

以下输出是典型的:

[INFO] --- war:3.4.0:war (default-war) @ jakartaee-app-on-jboss ---
[INFO] Packaging webapp
[INFO] Assembling webapp [jakartaee-app-on-jboss] in [/private/tmp/mslearn-jakarta-ee-azure/target/ROOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/private/tmp/mslearn-jakarta-ee-azure/src/main/webapp]
[INFO] Building war: /private/tmp/mslearn-jakarta-ee-azure/target/ROOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.881 s
[INFO] Finished at: 2025-02-21T06:32:30+09:00
[INFO] ------------------------------------------------------------------------

将 Jakarta EE 应用部署到 Azure 应用服务上的 JBoss EAP

编译并打包代码后,使用以下命令部署应用程序:

./mvnw azure-webapp:deploy

应会看到包含成功消息和已部署应用程序的 URL 的输出。 请务必将 URL 保存为以后使用。

配置数据库连接

示例应用程序连接到 MySQL 数据库并显示数据。 pom.xml 文件中的 Maven 项目配置指定 MySQL JDBC 驱动程序,如以下示例所示:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>${mysql-jdbc-driver}</version>
</dependency>

因此,JBoss EAP 会自动将 JDBC 驱动程序 ROOT.war_com.mysql.cj.jdbc.Driver_9_2 安装到部署包 ROOT.war

在 JBoss EAP 中创建 MySQL DataSource 对象

若要访问 Azure Database for MySQL,需要在 JBoss EAP 中配置 DataSource 对象,并在源代码中指定 Java 命名和目录接口(JNDI)名称。 若要在 JBoss EAP 中创建 MySQL DataSource 对象,请使用 /WEB-INF/createMySQLDataSource.sh 启动 shell 脚本。 以下示例演示 Azure 应用服务中已存在脚本的未配置版本:

#!/bin/bash
# In order to use the variables in CLI scripts
# https://access.redhat.com/solutions/321513
sed -i -e "s|.*<resolve-parameter-values.*|<resolve-parameter-values>true</resolve-parameter-values>|g" /opt/eap/bin/jboss-cli.xml
/opt/eap/bin/jboss-cli.sh --connect <<EOF
data-source add --name=JPAWorldDataSourceDS \
--jndi-name=java:jboss/datasources/JPAWorldDataSource \
--connection-url=${AZURE_MYSQL_CONNECTIONSTRING}&characterEncoding=utf8&sslMode=REQUIRED&serverTimezone=UTC&authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin \
--driver-name=ROOT.war_com.mysql.cj.jdbc.Driver_9_2 \
--min-pool-size=5 \
--max-pool-size=20 \
--blocking-timeout-wait-millis=5000 \
--enabled=true \
--driver-class=com.mysql.cj.jdbc.Driver \
--jta=true \
--use-java-context=true \
--valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker \
--exception-sorter-class-name=com.mysql.cj.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
exit
EOF

注释

创建数据源时,不指定 MySQL 连接的密码。 环境变量AZURE_MYSQL_CONNECTIONSTRING在参数--connection-url中指定。 稍后创建服务连接时,会自动设置此环境变量。

服务连接值设置为 jdbc:mysql://$MYSQL_SERVER_INSTANCE.mysql.database.azure.com:3306/world?serverTimezone=UTC&sslmode=required&user=aad_jbossapp,使用用户名而不使用 aad_jbossapp 密码。 通过追加 &authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin 到此 URL,Microsoft为用户启用 aad_jbossapp Entra ID 身份验证。

使用以下命令配置应用服务实例以调用启动脚本:

az webapp config set \
    --resource-group ${RESOURCE_GROUP_NAME} \
    --name ${WEB_APP_NAME} \
    --startup-file '/home/site/scripts/startup.sh'

脚本运行后,应用程序服务器会在每次重新启动应用程序服务器时调用它。

注释

如果部署项目不是 ROOT.war,则还更改 --driver-name=YOUR_ARTIFACT.war_com.mysql.cj.jdbc.Driver_9_2 值。

为 MySQL 灵活服务器配置服务连接

配置启动脚本后,使用以下步骤将应用服务配置为使用 Service Connector 进行 MySQL 灵活服务器连接:

  1. 使用以下命令设置环境变量:

    export PASSWORDLESS_USER_NAME_SUFFIX=jbossapp
    export SOURCE_WEB_APP_ID=$(az webapp list \
        --resource-group  $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    export MYSQL_ID=$(az mysql flexible-server list \
        --resource-group $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    export TARGET_MYSQL_ID=$MYSQL_ID/databases/world
    export MANAGED_ID=$(az identity list \
        --resource-group $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    

    环境变量用于以下目的:

    • PASSWORDLESS_USER_NAME_SUFFIX 是用于连接到 MySQL 灵活服务器的用户名的后缀。 创建的用户名具有前缀 aad_ ,后跟指定的后缀。
    • SOURCE_WEB_APP_ID 是用于连接到 MySQL 灵活服务器的 Azure 应用服务实例的 ID。
    • MYSQL_ID 是 MySQL 灵活服务器的 ID。
    • TARGET_MYSQL_ID 指定数据库名称 $MYSQL_ID/databases/world ,以便与有权访问 world 数据库的用户建立连接。
    • MANAGED_ID 是用于连接到 MySQL 灵活服务器的托管标识。
  2. 使用以下命令添加扩展 serviceconnector-passwordless 并创建服务连接:

    az extension add \
        --name serviceconnector-passwordless \
        --upgrade
    az webapp connection create mysql-flexible \
        --resource-group ${RESOURCE_GROUP_NAME} \
        --connection $PASSWORDLESS_USER_NAME_SUFFIX \
        --source-id $SOURCE_WEB_APP_ID \
        --target-id $TARGET_MYSQL_ID \
        --client-type java \
        --system-identity mysql-identity-id=$MANAGED_ID
    

    注释

    如果收到类似 Resource '********-****-****-****-************' does not exist or one of its queried reference-property objects are not present.错误消息,请在几秒钟后重新运行该命令。

  3. 在 SQL 提示符下,使用以下查询检查在 MySQL 中注册的用户列表:

    SELECT user, host, plugin FROM mysql.user;
    

    以下输出是典型的:

    +----------------------------------+-----------+-----------------------+
    | user                             | host      | plugin                |
    +----------------------------------+-----------+-----------------------+
    | aad_jbossapp                     | %         | aad_auth              |
    | azureuser                        | %         | mysql_native_password |
    | $CURRENT_AZ_LOGIN_USER_NAME#EXT#@| %         | aad_auth              |
    | azure_superuser                  | 127.0.0.1 | mysql_native_password |
    | azure_superuser                  | localhost | mysql_native_password |
    | mysql.infoschema                 | localhost | caching_sha2_password |
    | mysql.session                    | localhost | caching_sha2_password |
    | mysql.sys                        | localhost | caching_sha2_password |
    +----------------------------------+-----------+-----------------------+
    8 rows in set (2.06 sec)
    

    应该会看到使用 aad_jbossapp 插件的 aad_auth 用户。 在 Azure 上部署的 JBoss EAP 中,可以使用用户名(无需密码)连接到 MySQL 灵活服务器 aad_jbossapp

确认代码中的 DataSource 引用

若要从应用程序访问 MySQL 数据库,需要在应用程序项目中配置数据源引用。

数据库访问代码是使用 Java 持久性 API (JPA) 实现的。 引用的配置 DataSource 位于 JPA 配置文件 persistence.xml中。

使用以下步骤确认 DataSource 引用:

  1. 打开 src/main/resources/META-INF/persistence.xml 文件,并检查名称是否 DataSource 与配置中使用的名称匹配。 启动脚本已创建 java:jboss/datasources/JPAWorldDataSourceJNDI 名称,如以下示例所示:

    <persistence-unit name="JPAWorldDatasourcePU" transaction-type="JTA">
      <jta-data-source>java:jboss/datasources/JPAWorldDataSource</jta-data-source>
      <exclude-unlisted-classes>false</exclude-unlisted-classes>
      <properties>
        <property name="hibernate.generate_statistics" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
      </properties>
    </persistence-unit>
    
  2. 访问单元名称中的 PersistenceContext MySQL 数据库,如以下示例所示:

    @Transactional(REQUIRED)
    @RequestScoped
    public class CityService {
    
        @PersistenceContext(unitName = "JPAWorldDatasourcePU")
        EntityManager em;
    

访问应用程序

示例应用程序实现三个 REST 终结点。 若要访问应用程序和检索数据,请使用以下步骤:

  1. 使用浏览器导航到部署应用程序时输出中显示的应用程序 URL。

  2. 若要以 JSON 格式获取所有大洲信息,请使用 GET 终结点上的 area 方法。

    区域终结点的屏幕截图。

  3. 若要获取指定大洲中的所有国家和地区,请使用 GET 终结点上的 area 方法并指定 continent 路径参数。

    包含大陆路径参数的区域终结点的屏幕截图。

  4. 为获取指定国家或地区内人口超过 100 万的所有城市,请在GET终结点上使用countries方法,并指定countrycode路径参数。

    国家/地区终结点的屏幕截图,其中包含国家/地区代码路径参数。

练习摘要

在本单元中,你验证了应用程序 REST 终结点,并确认应用程序可以从 MySQL 数据库获取数据。 在下一个单元中,你将检查服务器日志。