将 Tomcat 应用程序迁移到 Azure Kubernetes 服务上的容器

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

预迁移

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

清点外部资源

外部资源(如数据源、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 静态文件

动态或内部内容

对于经常由应用程序写入和读取的文件(如临时数据文件),或者仅对应用程序可见的静态文件,可以将 Azure 存储共享作为持久卷进行装载。 有关详细信息,请参阅在 Azure Kubernetes 服务中动态创建永久性卷并将其用于 Azure 文件存储

识别会话持久性机制

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

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

如果需要会话持久性,则需要使用会将内容写入外部数据存储的备用 PersistentManager 实现,例如使用 Redis 缓存的 VMware Tanzu 会话管理器。 有关详细信息,请参阅将 Redis 作为会话缓存与 Tomcat 配合使用

特殊情况

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

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

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

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

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

如果应用程序包含任何要适应运行应用程序的 OS 的代码,则需对应用程序进行重构,使之不依赖于基础 OS。 例如,可能需要将文件系统路径中使用的 /\ 替换为 File.SeparatorPath.get

确定是否使用了 MemoryRealm

MemoryRealm 需要一个持久的 XML 文件。 在 Kubernetes 上,需将此文件添加到容器映像中,或将其上传到可供容器使用的共享存储pathName 参数必须相应地进行修改。

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

确定是否使用了 SSL 会话跟踪

在容器化部署中,SSL 会话通常在应用程序容器外部由入口控制器卸载。 如果应用程序需要 SSL 会话跟踪,请确保 SSL 流量直接传递到应用程序容器。

确定是否使用了 AccessLogValve

如果使用 AccessLogValve,则应将 directory 参数设置为 已装载的 Azure 文件共享或其子目录之一。

就地测试

在创建容器映像之前,请将应用程序迁移到要在 AKS 上使用的 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}"
/>

迁移

建议你分别为要迁移的每个应用程序(WAR 文件)执行以下步骤,第一步(“预配容器注册表和 AKS”)除外。

注意

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

预配容器注册表和 AKS

在注册表中创建其服务主体具有“读取者”角色的容器注册表和 Azure Kubernetes 群集。 确保根据群集的网络要求选择相应的网络模型

az group create \
    --resource-group $resourceGroup \
    --location eastus
az acr create \
    --resource-group $resourceGroup \
    --name $acrName \
    --sku Standard
az aks create \
    --resource-group $resourceGroup \
    --name $aksName \
    --attach-acr $acrName \
    --network-plugin azure

准备部署项目

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

根据需要打开用于聚类分析的端口

若要在 AKS 上使用 Tomcat 聚类分析,请确保在 Dockerfile 中公开必要的端口范围。 若要在 server.xml 中指定服务器 IP 地址,请确保使用在容器启动时初始化为 Pod 的 IP 地址的变量中的值。

也可将会话状态持久保存到备用位置,使之可以跨副本使用。

若要确定应用程序是否使用聚类分析,请在 server.xml 文件中查找 <Host><Engine> 元素内的 <Cluster> 元素。

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

生成并推送映像

若要生成映像并将其上传到 Azure 容器注册表 (ACR) 供 AKS 使用,最简单的方法是使用 az acr build 命令。 该命令不需要在计算机上安装 Docker。 例如,如果当前目录中有上述 Dockerfile 和应用程序包 petclinic.war,则可通过一个步骤生成 ACR 中的容器映像:

az acr build \
    --image "${acrName}.azurecr.io/petclinic:{{.Run.ID}}" \
    --registry $acrName \
    --build-arg APP_FILE=petclinic.war \
    --build-arg=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"

# Your application can now be accessed with a browser at http://localhost:8080.

# Log into ACR
sudo az acr login --name $acrName

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

有关详细信息,请参阅用于 在 Azure 中生成和存储容器映像的 Learn 模块。

预配公共 IP 地址

如果想要跳出内部网络或虚拟网络来访问应用程序,则需要公共静态 IP 地址。 应在群集的节点资源组内预配此 IP 地址。

export nodeResourceGroup=$(az aks show \
    --resource-group $resourceGroup \
    --name $aksName \
    --query 'nodeResourceGroup' \
    --output tsv)
export publicIp=$(az network public-ip create \
    --resource-group $nodeResourceGroup \
    --name applicationIp \
    --sku Standard \
    --allocation-method Static \
    --query 'publicIp.ipAddress' \
    --output tsv)
echo "Your public IP address is ${publicIp}."

部署到 AKS

创建并应用 Kubernetes YAML 文件。 如果要创建外部负载均衡器(不管是针对应用程序,还是针对入口控制器),请确保提供在上一部分以 LoadBalancerIP 形式预配的 IP 地址。

包括环境变量形式的外部化参数。 不要包括机密(如密码、API 密钥和 JDBC 连接字符串)。 配置 KeyVault FlexVolume 部分介绍了机密。

配置持久性存储

如果应用程序需要非易失性存储,请配置一个或多个持久性卷

可能需要使用 Azure 文件存储创建持久性卷,该卷装载到 Tomcat 日志目录 (/tomcat_logs),目的是集中保留日志。 有关详细信息,请参阅在 Azure Kubernetes 服务 (AKS) 中动态创建永久性卷并将其用于 Azure 文件存储

配置 KeyVault FlexVolume

创建 Azure KeyVault 并填充所有必要的机密。 然后,配置 KeyVault FlexVolume,使这些机密可供 Pod 访问。

需要修改启动脚本(容器中的 Tomcat GitHub 存储库中的 startup.sh),将证书导入容器的本地密钥存储中。

迁移计划的作业

若要在 AKS 群集上执行计划的作业,请按需定义 Cron 作业

迁移后

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