将 Tomcat 应用程序迁移到 Azure Kubernetes 服务 上的容器(AKS)
本指南介绍在需要迁移现有 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.xml 和 context.xml。 还可以在应用程序中查找包含密码或凭据的配置文件。 其中可能包括 META-INF/context.xml。对于 Spring Boot 应用程序,可能包括 application.properties 或 application.yml 文件。
确定是否使用以及如何使用文件系统
使用应用程序服务器上的文件系统需要重新配置,在极少数情况下需要体系结构更改。 可以识别下面的部分或所有情况。
只读静态内容
如果应用程序当前提供静态内容,则需为其提供一个备用位置。 可能需要考虑将静态内容移到 Azure Blob 存储,并添加 Azure CDN,方便用户在全球范围内快速下载。 有关详细信息,请参阅 Azure 存储中的静态网站托管和快速入门:将 Azure 存储帐户与 Azure CDN 集成。
动态发布的静态内容
如果应用程序允许那些通过应用程序上传/生成但在创建后不可变的静态内容,则可将上述 Azure Blob 存储和 Azure CDN 与 Azure 函数配合使用,以便处理上传和 CDN 刷新操作。 我们提供了一个示例实现,用于通过 Azure Functions 进行静态内容的上传和 CDN 预加载操作。
动态或内部内容
对于经常由应用程序写入和读取的文件(如临时数据文件),或者仅对应用程序可见的静态文件,可以将 Azure 存储共享作为持久卷进行装载。 有关详细信息,请参阅在 Azure Kubernetes 服务 (AKS) 中通过 Azure 文件存储创建并使用卷。
识别会话持久性机制
若要确定正在使用的会话持久性管理器,请在应用程序和 Tomcat 配置中检查 context.xml 文件。 请查找 <Manager>
元素,然后记下 className
属性的值。
Tomcat 的内置 PersistentManager 实现(如 StandardManager 或 FileStore)不适用于分布式缩放平台,如 Kubernetes。 AKS 可能会在多个 Pod 之间进行负载均衡,并随时以透明方式重启任何 Pod。不建议将可变状态持久保存到文件系统。
如果需要会话持久性,则需要使用会将内容写入外部数据存储的备用 PersistentManager
实现,例如使用 Redis 缓存的 VMware Tanzu 会话管理器。 有关详细信息,请参阅将 Redis 作为会话缓存与 Tomcat 配合使用。
特殊情况
某些生产方案可能需要进行其他更改或施加额外的限制。 虽然这种情况可能不怎么出现,但务必确保它们不适用于应用程序,或者在适用于应用程序的情况下已得到正确解决。
确定应用程序是否依赖于计划的作业
计划的作业(如 Quartz 计划程序任务或 cron 作业)不能与容器化 Tomcat 部署配合使用。 如果应用程序横向扩展,则一个计划的作业可能会按照计划期间运行多次。 这种情况可能会导致意外的后果。
清点应用程序服务器内外部所有计划的作业。
确定应用程序是否包含特定于 OS 的代码
如果应用程序包含任何要适应运行应用程序的 OS 的代码,则需对应用程序进行重构,使之不依赖于基础 OS。 例如,可能需要将文件系统路径中使用的 /
或 \
替换为 File.Separator
或 Path.get
。
确定是否使用了 MemoryRealm
MemoryRealm 需要一个持久的 XML 文件。 在 Kubernetes 上,需将此文件添加到容器映像中,或将其上传到可供容器使用的共享存储。 pathName
参数必须相应地进行修改。
若要确定当前是否在使用 MemoryRealm
,请检查 server.xml 和 context.xml 文件,并搜索已在其中将 className
属性设置为 org.apache.catalina.realm.MemoryRealm
的 <Realm>
元素。
确定是否使用了 SSL 会话跟踪
在容器化部署中,SSL 会话通常在应用程序容器外部由入口控制器卸载。 如果应用程序需要 SSL 会话跟踪,请确保 SSL 流量直接传递到应用程序容器。
确定是否使用了 AccessLogValve
如果使用 AccessLogValve,则应将 directory
参数设置为 已装载的 Azure 文件共享或其子目录之一。
就地测试
在创建容器映像之前,请将应用程序迁移到要在 AKS 上使用的 JDK 和 Tomcat。 全面测试应用程序,确保兼容性和性能。
将配置参数化
在预迁移中,你可能已在 server.xml 和 context.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}"
/>
迁移
建议你分别为要迁移的每个应用程序(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 以添加在迁移前步骤中准备的资源,如数据源,如下例所示:
注意
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) 供 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 中生成和存储容器映像的学习模块。
预配公共 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),将证书导入容器的本地密钥存储中。
注意
Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。
迁移计划的作业
若要在 AKS 群集上执行计划的作业,请按需定义 Cron 作业。
迁移后
将应用程序迁移到 AKS 后,即应验证其运行是否符合预期。 完成此操作后,可以参考我们提供的一些建议,使应用程序的云原生性更好。
考虑向分配给入口控制器或应用程序负载均衡器的 IP 地址添加 DNS 名称。 有关详细信息,请参阅将 TLS 用于 Azure Kubernetes 服务 (AKS) 上的入口控制器。
考虑为应用程序添加 HELM 图表。 可以通过 Helm 图表将应用程序部署参数化,供更多样化的客户使用和自定义。
设计和实施 DevOps 策略。 若要在提高开发速度的同时保持可靠性,请考虑通过 Azure Pipelines 自动进行部署和测试。
启用针对群集的 Azure 监视,以便收集容器日志、跟踪利用率等。
考虑通过 Prometheus 公开特定于应用程序的指标。 Prometheus 是在 Kubernetes 社区中广泛采用的开源指标框架。 可以配置 Azure Monitor 中的 Prometheus 指标抓取,而不是托管你自己的 Prometheus 服务器,以便进行应用程序指标聚合,并自动响应或升级异常情况。
设计和实施业务连续性和灾难恢复策略。 对于关键应用程序,请考虑多区域部署体系结构。
查看 Kubernetes 版本支持策略。 你有责任持续更新 AKS 群集,确保其始终运行受支持的版本。
让所有负责群集管理和应用程序开发的团队成员查看相关的 AKS 最佳做法。
评估 logging.properties 文件中的项。 考虑消除或减少某些日志记录输出以提高性能。
考虑监视代码缓存大小并向 Dockerfile 中的
JAVA_OPTS
变量添加参数-XX:InitialCodeCacheSize
和-XX:ReservedCodeCacheSize
,进一步优化性能。