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

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

预迁移

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

清点外部资源

外部资源(如数据源、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 Spring Apps Enterprise 计划中的应用。 有关详细信息,请参阅 部署 Web 静态文件

动态发布的静态内容

如果应用程序允许那些通过应用程序上传/生成但在创建后不可变的静态内容,则可将上述 Azure Blob 存储和 Azure CDN 与 Azure 函数配合使用,以便处理上传和 CDN 刷新操作。 我们提供了一个示例实现,用于通过 Azure Functions 进行静态内容的上传和 CDN 预加载操作。 还可以直接将静态内容部署到 Azure Spring Apps Enterprise 计划中的应用。 有关详细信息,请参阅 部署 Web 静态文件

识别会话持久性机制

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

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

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

特殊情况

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

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

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

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

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

如果应用程序包含的代码有主机 OS 的依赖项,则需重构该代码,删除那些依赖项。 例如,可能需要将文件系统路径中使用的 /\ 替换为 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 替换为环境变量。

例如,假设 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="t00secure2gue$$"
/>

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

<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 以添加在迁移前步骤(如数据源)中准备的资源,如以下示例所示:

<!-- 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 数据源操作方法)的以下部分:

生成并推送映像

生成映像并将其上传到 ACA 要使用的Azure 容器注册表(ACR)的最简单方法是使用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 服务(AKS)中业务连续性和灾难恢复的最佳做法。

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

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

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

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

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