将 Tomcat 应用程序迁移到 Azure 容器应用

本指南介绍在需要迁移现有 Tomcat 应用程序以使之在 Azure 容器应用 (ACA) 上运行时应注意的事项。

预迁移

若要确保迁移成功,请在开始之前完成以下各节中所述的评估和清点步骤。

清点外部资源

外部资源(如数据源、JMS 消息代理等)通过 Java 命名和目录接口 (JNDI) 注入。 某些此类资源可能需要迁移或重新配置。

在应用程序中

检查 META-INF/context.xml 文件。 查找 <Context> 元素中的 <Resource> 元素。

在应用程序服务器上

检查 $CATALINA_BASE/conf/context.xml$CATALINA_BASE/conf/server.xml 文件,以及在 $CATALINA_BASE/conf/[engine-name]/[host-name] 目录中发现的 .xml 文件。

context.xml 文件中,JNDI 资源将由顶级 <Context> 元素中的 <Resource> 元素描述。

server.xml 文件中,JNDI 资源将由 <GlobalNamingResources> 元素中的 <Resource> 元素描述。

Datasources

数据源是 JNDI 资源,其 type 属性设置为 javax.sql.DataSource。 对于每个数据源,请记录以下信息:

  • 数据源名称是什么?
  • 连接池配置是什么?
  • 在哪里可以找到 JDBC 驱动程序 JAR 文件?

有关详细信息,请参阅 Tomcat 文档中的 JNDI Datasource HOW-TO(JNDI 数据源操作方法)。

所有其他的外部资源

在本指南中,不可能记录每个可能的外部依赖项。 你的团队负责验证你是否可以在迁移之后满足应用程序的所有外部依赖项的要求。

清点机密

密码和安全字符串

检查生产服务器上的所有属性和配置文件中是否有机密字符串和密码。 务必检查 $CATALINA_BASE/conf 中的 server.xmlcontext.xml。 还可以在应用程序中查找包含密码或凭据的配置文件。 其中可能包括 META-INF/context.xml。对于 Spring Boot 应用程序,可能包括 application.propertiesapplication.yml 文件。

确定是否使用以及如何使用文件系统

使用应用程序服务器上的文件系统需要重新配置,在极少数情况下需要体系结构更改。 可以识别下面的部分或所有情况。

只读静态内容

如果应用程序当前提供静态内容,则需为其提供一个备用位置。 可能需要考虑将静态内容移到 Azure Blob 存储,并添加 Azure CDN,方便用户在全球范围内快速下载。 有关详细信息,请参阅 Azure 存储中的静态网站托管快速入门:将 Azure 存储帐户与 Azure CDN 集成

动态发布的静态内容

如果应用程序允许那些通过应用程序上传/生成但在创建后不可变的静态内容,则可将上述 Azure Blob 存储和 Azure CDN 与 Azure 函数配合使用,以便处理上传和 CDN 刷新操作。 我们提供了一个示例实现,用于通过 Azure Functions 进行静态内容的上传和 CDN 预加载操作

识别会话持久性机制

若要确定正在使用的会话持久性管理器,请在应用程序和 Tomcat 配置中检查 context.xml 文件。 请查找 <Manager> 元素,然后记下 className 属性的值。

Tomcat 的内置 PersistentManager 实现(如 StandardManagerFileStore)不适用于分布式缩放平台,如 ACA。 ACA 可能会在多个实例之间进行负载均衡,并随时以透明方式重启任何实例,因此不建议将可变状态持久保存到文件系统。

如果需要会话持久性,则需要使用会将内容写入外部数据存储的备用 PersistentManager 实现,例如使用 Redis 缓存的 VMware Tanzu 会话管理器。

特殊情况

某些生产方案可能需要进行更多改动或施加更多限制。 虽然这种情况可能不怎么出现,但务必确保它们不适用于应用程序,或者在适用于应用程序的情况下已得到正确解决。

确定应用程序是否依赖于计划的作业

计划的作业(如 Quartz 计划程序任务或 cron 作业)不能与容器化 Tomcat 部署配合使用。 如果应用程序横向扩展,则一个计划的作业可能会按照计划期间运行多次。 这种情况可能会导致意外的后果。

清点应用程序服务器内外部所有计划的作业。

确定应用程序是否包含特定于 OS 的代码

如果应用程序包含的代码有主机 OS 的依赖项,则需重构该代码,删除那些依赖项。 例如,如果应用程序在 Windows 上运行,则可能需要将文件系统路径中的 /\ 替换为 File.SeparatorPaths.get

确定是否使用了 MemoryRealm

MemoryRealm 需要一个持久的 XML 文件。 在 ACA 上,需要将此文件添加到容器映像中,或上传到容器可用的共享存储中。 (有关详细信息,请参阅识别会话持久性机制部分。)pathName 参数必须作相应修改。

若要确定当前是否在使用 MemoryRealm,请检查 server.xmlcontext.xml 文件,并搜索已在其中将 className 属性设置为 org.apache.catalina.realm.MemoryRealm<Realm> 元素。

就地测试

在创建容器映像之前,请将应用程序迁移到要在 ACA 上使用的 JDK 和 Tomcat。 全面测试应用程序,确保兼容性和性能。

将配置参数化

在预迁移中,你可能已在 server.xmlcontext.xml 文件中标识了机密和外部依赖项(如数据源)。 对于这样标识的每个项,请将任何用户名、密码、连接字符串或 URL 替换为环境变量。

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

例如,假设 context.xml 文件包含以下元素:

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="jdbc:postgresql://postgresdb.contoso.com/wickedsecret?ssl=true"
    driverClassName="org.postgresql.Driver"
    username="postgres"
    password="{password}"
/>

在这种情况下,可以对其进行更改,如以下示例所示:

<Resource
    name="jdbc/dbconnection"
    type="javax.sql.DataSource"
    url="${postgresdb.connectionString}"
    driverClassName="org.postgresql.Driver"
    username="${postgresdb.username}"
    password="${postgresdb.password}"
/>

迁移

注意

某些 Tomcat 部署可能会在单个 Tomcat 服务器上运行多个应用程序。 如果部署中出现这种情况,强烈建议在单独的 Pod 中运行每个应用程序。 这样可以优化每个应用程序的资源利用率,同时最大程度地降低复杂性和耦合度。

准备部署项目

克隆容器中的 Tomcat 快速入门 GitHub 存储库。 该存储库中包含一个 Dockerfile 和 Tomcat 配置文件,其中有许多推荐的优化措施。 在下面的步骤中,我们将在生成容器映像并将其部署到 ACA 之前,概述可能需要对这些文件进行的修改。

添加 JNDI 资源

编辑 server.xml 以添加在迁移前步骤中准备的资源,如数据源,如下例所示:

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

<!-- Global JNDI resources
      Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml"
               />

    <!-- Migrated datasources here: -->
    <Resource
        name="jdbc/dbconnection"
        type="javax.sql.DataSource"
        url="${postgresdb.connectionString}"
        driverClassName="org.postgresql.Driver"
        username="${postgresdb.username}"
        password="${postgresdb.password}"
    />
    <!-- End of migrated datasources -->
</GlobalNamingResources>

如需更多的数据源说明,请参阅 Tomcat 文档中 JNDI Datasource How-To(JNDI 数据源操作方法)的以下部分:

生成并推送映像

若要生成映像并将其上传到 Azure 容器注册表 (ACR) 供 ACA 使用,最简单的方法是使用 az acr build 命令。 该命令不需要在计算机上安装 Docker。 例如,如果当前目录中有 tomcat-container-quickstart 存储库中的 Dockerfile 和 petclinic.war 应用程序包,则可以使用以下命令在 ACR 中构建容器映像:

az acr build \
    --registry $acrName \
    --image "${acrName}.azurecr.io/petclinic:{{.Run.ID}}" 
    --build-arg APP_FILE=petclinic.war \
    --build-arg SERVER_XML=prod.server.xml .

如果 WAR 文件的名称为 ROOT.war,则可省略 --build-arg APP_FILE... 参数。 如果服务器 XML 文件的名称为 server.xml,则可省略 --build-arg SERVER_XML... 参数。 这两个文件必须位于 Dockerfile 所在的目录中。

或者,也可以使用 Docker CLI,通过以下命令在本地生成镜像。 此方法可以简化在一开始部署到 ACR 之前对映像进行的测试和优化。 但是,它要求安装 Docker CLI 且 Docker 守护程序处于运行状态。

# Build the image locally.
sudo docker build . --build-arg APP_FILE=petclinic.war -t "${acrName}.azurecr.io/petclinic:1"

# Run the image locally.
sudo docker run -d -p 8080:8080 "${acrName}.azurecr.io/petclinic:1"

# You can now access your application with a browser at http://localhost:8080.

# Sign in to ACR.
sudo az acr login --name $acrName

# Push the image to ACR.
sudo docker push "${acrName}.azurecr.io/petclinic:1"

有关详细信息,请参阅使用 Azure 容器注册表生成和存储容器映像

部署到 Azure 容器应用

以下命令展示的是一个示例部署:

az containerapp create \
    --resource-group <RESOURCE_GROUP> \
    --name <APP_NAME> \
    --environment <ENVIRONMENT_NAME> \
    --image <IMAGE_NAME> \
    --target-port 8080 \
    --ingress 'external' \
    --registry-server <REGISTRY_SERVER> \
    --min-replicas 1

有关更深入的快速入门,请参阅快速入门:部署第一个容器应用

迁移后

将应用程序迁移到 ACA 后,即应验证其运行是否符合预期。 完成此操作后,可以参考我们提供的一些建议,使应用程序的云原生性更好。

建议

  • 设计和实施业务连续性和灾难恢复策略。 对于关键应用程序,请考虑多区域部署体系结构。 有关详细信息,请参阅 Azure Kubernetes Service (AKS) 中的业务连续性和灾难恢复的最佳做法

  • 评估 logging.properties 文件中的项。 考虑消除或减少某些日志记录输出以提高性能。

  • 考虑监视代码缓存大小并向 Dockerfile 中的 JAVA_OPTS 变量添加参数 -XX:InitialCodeCacheSize-XX:ReservedCodeCacheSize,进一步优化性能。 有关详细信息,请参阅 Oracle 文档中的 Codecache Tuning(Codecache 优化)。

  • 请考虑添加 Azure Monitor 预警规则和操作组,以快速检测并解决异常情况。

  • 请考虑在另一个区域复制 Azure 容器应用部署,以降低延迟、提高可靠性和容错能力。 使用 Azure 流量管理器在部署之间实现负载均衡,或使用 Azure Front Door 添加 SSL 卸载和具有 DDoS 防护的 Web 应用程序防火墙。

  • 如果不需要异地复制,请考虑添加 Azure 应用程序网关,以便添加 SSL 卸载和具有 DDoS 防护的 Web 应用程序防火墙。