ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux服务器部署多Tomcat实例:端口配置、资源隔离与生产环境实践

Linux服务器部署多Tomcat实例:端口配置、资源隔离与生产环境实践 1. 从单兵作战到多线部署为什么需要一台服务器跑多个Tomcat刚接触服务器部署的新手或者是从开发转向运维的朋友常常会有一个疑问我买一台云服务器难道不是装一个Tomcat然后把所有项目都扔进去就行了吗理论上一个Tomcat确实可以部署多个Web应用通过不同的上下文路径Context Path来区分比如http://服务器IP:8080/app1和http://服务器IP:8080/app2。这在早期或者小型内部系统中很常见。但为什么我们还要大费周章地在一台机器上部署多个Tomcat实例呢这背后其实是生产环境对隔离性、稳定性和灵活性的硬性要求。想象一下你有一个对外提供服务的电商主站Tomcat A还有一个内部使用的后台管理系统Tomcat B以及一个定时跑批处理任务的Java应用Tomcat C。如果它们都挤在同一个Tomcat容器里会是什么局面首先最直接的风险是“一损俱损”。Tomcat A的应用因为一个内存泄漏或者死循环把整个JVM搞崩了那么B和C也会跟着一起挂掉所有服务瞬间中断。其次资源无法隔离。A应用是个“内存大户”疯狂吃资源B和C这两个“小应用”就可能因为资源不足而响应缓慢甚至崩溃。再者升级和维护变得异常棘手。你想给后台管理系统B升级一下Spring框架版本但这个新版本可能和主站A依赖的旧版本冲突在同一个Tomcat里根本无法共存。最后日志、配置、启停管理都会混杂在一起排查问题如同大海捞针。所以在生产环境中为不同的应用、甚至同一应用的不同环境如测试、预发布部署独立的Tomcat实例是更专业和稳妥的做法。它实现了应用级别的资源与故障隔离让每个应用都有自己的“独立套房”互不干扰。升级、重启、调优都可以针对单个实例进行不影响其他服务。这就像在一栋大楼里给每个租户分配独立的房间和水电表而不是让大家共用一个大厅。理解了“为什么”我们再来看看“怎么做”。核心思路很简单复制多份Tomcat的安装目录然后为每一份配置不同的运行参数主要是端口号和环境变量让它们能和平共处。听起来容易但实操中会遇到各种“坑”比如端口冲突、环境变量覆盖、启动脚本修改、以及如何优雅地管理这些实例。接下来我就结合自己多次在Linux服务器以CentOS 7为例上实操的经验手把手带你走一遍最稳妥的部署流程并分享那些文档里不会写的细节和避坑指南。2. 战前准备理清思路与备齐弹药在开始动手之前我们必须把整个部署的蓝图和需要的“弹药”准备好。盲目操作只会导致混乱和反复重来。2.1 核心部署逻辑拆解部署多个Tomcat的核心在于解决两个根本冲突网络端口冲突和系统资源标识冲突。网络端口冲突Tomcat默认使用8080端口HTTP、8005端口SHUTDOWN、8009端口AJP等。如果两个Tomcat实例使用相同的端口后启动的那个一定会报错“Address already in use”。所以我们必须为每个实例分配一套唯一的端口号。系统资源标识冲突这主要指CATALINA_HOME和CATALINA_BASE环境变量。CATALINA_HOME指向Tomcat的安装目录包含公共的lib、bin等。CATALINA_BASE指向一个实例的工作目录包含该实例私有的conf、logs、webapps、work、temp等。传统单实例部署时两者通常指向同一个目录。但在多实例部署中我们通过设置不同的CATALINA_BASE让多个实例共享一份CATALINA_HOME即Tomcat二进制文件但拥有各自独立的配置、日志和应用部署目录。这是官方推荐的做法便于统一升级Tomcat版本。基于此我们的部署方案有两种方案A目录完全复制直接复制完整的Tomcat安装目录如tomcat-01,tomcat-02。每个目录都是独立的互不干扰。你需要分别修改每个目录下conf/server.xml中的端口号。这种方式简单粗暴适合实例不多、且对Tomcat版本可能有差异化需求的场景。方案B共享Home分离Base只安装一份Tomcat作为CATALINA_HOME。然后为每个实例创建独立的CATALINA_BASE目录里面包含conf,logs,webapps,work,temp等子目录并从CATALINA_HOME的conf目录复制初始配置文件过来修改。这种方式更优雅资源利用更高效是运维的标准做法。为了更直观地理解我将以方案A为例进行演示因为它更易于初学者理解和操作且能更清晰地展现每个实例的独立性。掌握了方案A过渡到方案B会非常容易。2.2 环境与工具清单假设我们有一台全新的CentOS 7服务器我们需要部署两个Tomcat实例分别运行两个不同的Java Web项目。服务器CentOS 7.x 最小化安装。必备软件JDK必须提前安装好。Tomcat运行在JVM之上。建议使用JDK 8或JDK 11这些长期支持版本。可以通过java -version命令验证。Tomcat我们将使用Tomcat 9.0.x版本。去Apache官网下载Core版本的tar.gz压缩包例如apache-tomcat-9.0.xx.tar.gz。工具通过SSH连接服务器进行操作。确保你拥有root权限或可以通过sudo执行特权命令。在开始前请确保JDK已正确安装且环境变量JAVA_HOME已配置。你可以通过以下命令检查java -version echo $JAVA_HOME如果JAVA_HOME没有输出或指向错误需要先配置。一个常见的配置方法是编辑/etc/profile文件在末尾添加export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.xxx.x86_64 # 请替换为你的实际JDK路径 export PATH$JAVA_HOME/bin:$PATH然后执行source /etc/profile使配置生效。注意这里配置的是系统环境变量它对所有用户和所有Tomcat实例都生效是Tomcat能找到Java运行时的基础。3. 实战部署一步步构建两个独立的Tomcat王国现在让我们进入实战环节。我会以部署两个实例为例演示从安装、配置到启动的全过程。3.1 第一步基础Tomcat的安装与解压首先我们登录服务器在一个合适的目录下操作比如/opt。上传与解压cd /opt # 假设你已经通过FTP或SCP将 apache-tomcat-9.0.xx.tar.gz 上传到了 /opt 目录 tar -zxvf apache-tomcat-9.0.xx.tar.gz # 解压后得到一个目录例如 apache-tomcat-9.0.xx为了便于区分我们先将其重命名为一个“模板”目录或第一个实例的目录。mv apache-tomcat-9.0.xx tomcat-template # 或者 mv apache-tomcat-9.0.xx tomcat-8080创建实例目录现在我们基于这个“模板”复制出两个实例目录。cp -r tomcat-template tomcat-8080 cp -r tomcat-template tomcat-8081这样我们就有了tomcat-8080和tomcat-8081两个完全独立的Tomcat目录。用端口号命名目录是一个好习惯一目了然。3.2 第二步攻克核心难关——端口配置这是最关键的一步。我们需要修改每个实例conf/server.xml文件中的三个关键端口确保它们互不冲突。端口号规划表实例名称HTTP/1.1 连接器端口 (默认8080)关闭端口 (默认8005)AJP/1.3 连接器端口 (默认8009)tomcat-80808080(保持不变)8005(保持不变)8009(保持不变)tomcat-8081808180068010注意关闭端口Shutdown Port是Tomcat监听关闭命令的端口每个实例必须唯一否则无法独立关闭。AJP端口通常用于与Web服务器如Apache HTTPD集成如果你不用可以注释掉整个Connector protocolAJP/1.3 ...节点但修改端口是更规范的做法。开始修改修改 tomcat-8081 的端口vi /opt/tomcat-8081/conf/server.xml使用vi编辑器或你喜欢的nano找到以下三处并进行修改关闭端口搜索Server port 大概在第22行左右。Server port8005 shutdownSHUTDOWN !-- 修改为 8006 --HTTP连接器端口搜索Connector port8080大概在第69行左右。Connector port8080 protocolHTTP/1.1 ... / !-- 修改为 8081 --AJP连接器端口搜索Connector port8009 protocolAJP/1.3大概在第116行左右。Connector port8009 protocolAJP/1.3 ... / !-- 修改为 8010 --修改完成后保存退出:wq。检查 tomcat-8080 的端口理论上第一个实例我们可以用默认端口但为了保险起见最好也打开server.xml确认一下确保没有被其他程序占用。如果8080端口已被占用比如已有其他Tomcat在运行你也需要为tomcat-8080分配另一组端口。一个重要的实操心得在修改端口时我强烈建议你一次性修改完一组端口并记录在案。我曾经因为只改了HTTP端口忘了改关闭端口导致第二个实例启动后第一个实例的关闭命令失效排查了半天。你可以写一个简单的脚本来批量修改或者用sed命令但对于新手手动修改并仔细核对是最稳妥的。3.3 第三步启动脚本的“个性化”改造默认情况下Tomcat的启动脚本bin/startup.sh和关闭脚本bin/shutdown.sh会读取CATALINA_HOME和CATALINA_BASE环境变量。在我们这种“目录完全复制”的方案中这两个变量默认都会指向当前脚本所在的目录这通常没问题。但是当我们需要为每个实例配置特定的JVM参数如内存大小、GC策略时就需要对脚本进行改造。更常见的做法是我们为每个实例创建独立的启动/停止脚本或者通过修改bin/catalina.sh来传递参数。这里介绍一种简单有效的方法修改每个实例的bin/setenv.sh文件如果不存在就创建。这个文件是Tomcat启动时自动加载的用于设置该实例特有的环境变量。为 tomcat-8080 创建 setenv.shvi /opt/tomcat-8080/bin/setenv.sh添加内容例如设置JVM堆内存#!/bin/sh # 设置此Tomcat实例的JVM参数 export JAVA_OPTS-server -Xms512m -Xmx1024m -XX:MetaspaceSize128m -XX:MaxMetaspaceSize256m # 你也可以在这里设置其他实例特有的变量比如日志路径前缀 export CATALINA_OUT/opt/tomcat-8080/logs/catalina.out保存退出并赋予执行权限chmod x /opt/tomcat-8080/bin/setenv.sh为 tomcat-8081 创建 setenv.sh 同样操作你可以设置不同的内存参数。vi /opt/tomcat-8081/bin/setenv.sh#!/bin/sh export JAVA_OPTS-server -Xms256m -Xmx512m -XX:MetaspaceSize64m -XX:MaxMetaspaceSize128m export CATALINA_OUT/opt/tomcat-8081/logs/catalina.outchmod x /opt/tomcat-8081/bin/setenv.sh通过setenv.sh我们实现了每个实例JVM参数的隔离配置。这是生产环境调优的必备步骤。3.4 第四步部署项目与首次启动现在我们可以将我们的Web项目通常是WAR包部署到对应的实例中。部署项目将你的project-a.war放到/opt/tomcat-8080/webapps/目录下。将project-b.war放到/opt/tomcat-8081/webapps/目录下。Tomcat启动时会自动解压WAR包。启动实例# 启动第一个实例 /opt/tomcat-8080/bin/startup.sh # 启动第二个实例 /opt/tomcat-8081/bin/startup.sh验证启动查看日志这是最可靠的验证方式。tail -f /opt/tomcat-8080/logs/catalina.out tail -f /opt/tomcat-8081/logs/catalina.out在日志中寻找Server startup in [xxxx] milliseconds这样的信息表示启动成功。检查进程ps -ef | grep tomcat你应该能看到两个Java进程分别对应两个Tomcat实例它们的启动命令路径是不同的。端口监听netstat -tlnp | grep java你应该能看到8080、8005、8009第一个实例和8081、8006、8010第二个实例端口处于LISTEN状态。浏览器访问在本地浏览器输入http://你的服务器IP:8080和http://你的服务器IP:8081应该能看到Tomcat的默认主页或者你部署的应用。如果一切顺利恭喜你两个Tomcat实例已经成功运行在同一台服务器上了4. 进阶管理与深度避坑指南把服务跑起来只是第一步如何管理好它们才是体现运维水平的地方。下面分享一些进阶配置和那些容易踩的“坑”。4.1 权限、用户与安全加固默认的Tomcat安装存在一些安全隐患尤其是在多实例环境下需要逐一加固。创建专用系统用户绝对不要使用root用户直接运行Tomcat这会给系统带来巨大风险。我们应该为Tomcat服务创建专用的、权限受限的系统用户。# 创建一个名为‘tomcat’的系统用户且不创建家目录禁止登录shell useradd -r -m -d /opt/tomcat -s /bin/false tomcat然后将我们所有的Tomcat实例目录的所有权赋予这个用户假设实例目录都在/opt下chown -R tomcat:tomcat /opt/tomcat-8080 chown -R tomcat:tomcat /opt/tomcat-8081以后启动Tomcat应该切换到这个用户来执行或者在启动脚本中通过su - tomcat -c来切换。管理后台密码每个实例的conf/tomcat-users.xml文件是独立的。你需要为每个实例单独配置管理员用户并且务必使用强密码。同时考虑是否真的需要Tomcat自带的管理后台manager和host-manager应用如果不需要可以直接删除webapps目录下的manager和host-manager文件夹这是最安全的方式。关闭不必要的连接器和功能在生产环境如果不需要AJP协议比如前面没有Apache HTTPD建议在server.xml中注释掉AJP连接器。同样检查conf/web.xml中是否启用了不必要的内置Servlet。4.2 日志管理让排查有迹可循多实例的日志管理至关重要。默认日志都在各自实例的logs目录下但我们可以做得更好。日志切割Tomcat默认的catalina.out不会自动切割会无限增长。我们需要借助logrotate工具。创建配置文件/etc/logrotate.d/tomcat-8080/opt/tomcat-8080/logs/catalina.out { daily rotate 30 copytruncate missingok compress delaycompress notifempty create 644 tomcat tomcat }同样为tomcat-8081创建一份。这样日志会按天切割并压缩保留30天。日志级别与输出可以在conf/logging.properties中调整每个实例的日志级别将不必要的DEBUG信息关闭减少日志量聚焦错误(SEVERE)和警告(WARNING)。4.3 服务化与开机自启通过命令行startup.sh启动在SSH断开后进程可能会终止。我们需要将Tomcat实例注册为系统服务实现后台运行和开机自启。这里以systemd为例CentOS 7及以上为 tomcat-8080 创建服务单元文件vi /etc/systemd/system/tomcat-8080.service写入以下内容注意根据你的实际路径修改[Unit] DescriptionApache Tomcat 9 Instance for Port 8080 Afternetwork.target [Service] Typeforking Usertomcat Grouptomcat # 设置环境变量非常重要 EnvironmentJAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk EnvironmentCATALINA_PID/opt/tomcat-8080/temp/tomcat.pid EnvironmentCATALINA_HOME/opt/tomcat-8080 EnvironmentCATALINA_BASE/opt/tomcat-8080 ExecStart/opt/tomcat-8080/bin/startup.sh ExecStop/opt/tomcat-8080/bin/shutdown.sh Restarton-failure RestartSec10 [Install] WantedBymulti-user.target为 tomcat-8081 创建服务单元文件同理创建/etc/systemd/system/tomcat-8081.service修改其中的端口、PID文件路径和目录路径。启用并启动服务systemctl daemon-reload systemctl enable tomcat-8080 systemctl enable tomcat-8081 systemctl start tomcat-8080 systemctl start tomcat-8081现在你可以使用systemctl status tomcat-8080来查看服务状态并且服务器重启后这两个Tomcat实例也会自动启动。4.4 那些年我踩过的“坑”与解决方案端口冲突的幽灵明明改了server.xml启动时还是报“Address already in use”。除了检查其他Tomcat实例还要用netstat -tlnp看看是不是有其他程序比如旧的Java进程、Nginx、某个开发工具占用了你规划的端口。一个习惯在分配端口前先用netstat命令扫一遍。环境变量的“陷阱”在systemd的service文件中JAVA_HOME必须显式设置。即使你在/etc/profile中设置了systemd服务在启动时也读取不到用户环境变量。这是最常见的启动失败原因之一错误信息通常是“Neither the JAVA_HOME nor the JRE_HOME environment variable is defined”。务必在service文件的[Service]段用Environment指令设置。权限不足导致启动失败当你用tomcat用户启动时务必确保该用户对Tomcat目录下的logs,temp,work,webapps等目录有写权限。启动失败时首先去logs/catalina.out里看错误日志权限问题通常会有“Permission denied”的提示。关闭端口被误杀shutdown.sh脚本是通过向关闭端口发送SHUTDOWN命令来停止Tomcat的。如果这个端口配置错误或者被防火墙拦截shutdown.sh就会失效你只能通过kill -9来强制结束进程。确保每个实例的关闭端口唯一且可访问。内存溢出OOM连环计在多实例环境下物理内存是共享的。如果你给每个实例都配置了过大的-Xmx最大堆内存总和超过了物理内存就会导致频繁的Swap交换系统变得巨卡甚至触发OOM Killer杀掉其他进程。务必根据服务器总内存和实例数量合理分配每个实例的堆内存要预留一部分给系统和其他进程。5. 从多实例到容器化另一种更现代的思考在一台服务器上部署多个Tomcat实例是传统虚拟主机或物理服务器时代的经典做法。但随着云原生和微服务架构的普及Docker容器化成为了更主流、更优雅的解决方案。你可以为每个Tomcat应用创建一个Docker镜像然后在一台服务器上运行多个Docker容器。每个容器都有自己完全隔离的文件系统、网络命名空间和进程空间。端口映射、资源限制CPU、内存、环境变量配置都可以通过Docker命令或docker-compose.yml文件轻松管理。例如一个简单的docker-compose.yml可以定义两个Tomcat服务version: 3 services: app1: image: tomcat:9.0-jdk8 container_name: tomcat-app1 ports: - 8080:8080 volumes: - ./app1/webapps:/usr/local/tomcat/webapps - ./app1/logs:/usr/local/tomcat/logs environment: - JAVA_OPTS-Xms512m -Xmx1024m app2: image: tomcat:9.0-jdk8 container_name: tomcat-app2 ports: - 8081:8080 volumes: - ./app2/webapps:/usr/local/tomcat/webapps - ./app2/logs:/usr/local/tomcat/logs environment: - JAVA_OPTS-Xms256m -Xmx512m一行命令docker-compose up -d就能启动两个完全隔离的Tomcat环境。这种方式在部署速度、环境一致性、资源调度和水平扩展上都有巨大优势。所以当你熟练掌握了手动部署多Tomcat实例后不妨将学习Docker提上日程。它并不是要取代你刚学会的技能而是为你提供了在更复杂场景下比如混合部署、持续集成/持续部署更强大的工具。理解手动部署的每一个细节会让你在使用容器化工具时更加得心应手因为容器化的本质正是对这些底层隔离和配置机制的一种标准化和自动化封装。
返回列表