1. 项目概述:为什么需要在一台服务器上跑多个Tomcat实例?
“tomcat多实例---简单部署jpress网站”这个标题,表面看是个运维小技巧,但背后藏着的是中小团队最真实、最频繁的生产困境:资源有限,需求多样,又不能随便加机器。我接触过的某高校实验室、某初创内容平台、某本地化SaaS服务商,几乎都踩过同一个坑——一开始只装一个Tomcat,把所有Java Web应用(CMS、后台管理、API服务、测试Demo)全塞进去,结果改个jpress主题要重启整个容器,一重启,正在跑的数据报表服务就断了;或者测试新版本jpress时,不小心把生产环境的session配置覆盖了,用户集体掉登录。这种“一锅炖”式部署,不是省事,是埋雷。
所谓“Tomcat多实例”,本质是用同一套Tomcat二进制程序,启动多个相互隔离的运行环境。它不等于开多个虚拟机或Docker容器(那成本高、管理重),也不等于用Spring Boot内嵌Tomcat(jpress是传统WAR包架构,强依赖Servlet容器)。它的核心价值在于:零额外安装、低内存占用、配置级隔离、故障域收窄。你只需要一份apache-tomcat-9.0.x解压目录,就能同时跑出dev-jpress、test-jpress、prod-jpress三个完全独立的站点,端口、日志、webapps、JVM参数互不干扰。jpress作为基于JFinal框架的轻量级开源博客系统,对Tomcat版本要求明确(推荐8.5+或9.0+),又依赖JDBC数据源和文件上传路径,恰好是验证多实例部署逻辑的绝佳样本——它不复杂,但足够典型。
这个方案适合三类人:一是运维刚接手一批老旧Java项目的中小公司IT支持,没预算买专业中间件许可证;二是开发者想在单台开发机上并行调试多个jpress站点(比如对比不同主题、测试插件兼容性);三是教学场景中,导师需要为A同学配独立数据库+独立域名+独立日志,又不想每人配一台云服务器。它解决的不是“高并发”问题,而是“可维护性”和“环境确定性”问题。接下来我会从设计思路、目录结构、配置细节、jpress适配、实操避坑五个维度,带你手把手搭出稳定可用的多实例环境,所有步骤均基于Linux CentOS 7/8与Tomcat 9.0.83实测验证,Windows路径差异我会单独标注。
2. 整体设计思路:为什么选“共享bin + 独立实例目录”而非其他方案?
很多人第一次听说多实例,第一反应是“复制整个Tomcat文件夹”。这确实可行,但很快会陷入维护噩梦:Tomcat安全补丁来了,你要手动更新5个副本;JVM调优参数改了,得逐个打开catalina.sh去改;某个实例的server.xml被误删,恢复时还得翻备份。我们采用的是Tomcat官方文档明确推荐的**“CATALINA_BASE分离模式”**——即所有实例共用同一份$CATALINA_HOME(二进制程序),每个实例拥有独立的$CATALINA_BASE(配置、日志、webapps、work)。这是最轻量、最可控、升级最省心的方案。
2.1 目录结构设计:清晰到能一眼看出谁是谁
我坚持用语义化命名,拒绝tomcat1/tomcat2这种编号。以jpress为例,我的生产环境目录树长这样:
/opt/tomcat/ # CATALINA_HOME,只放bin/、lib/、LICENSE等不变文件 ├── bin/ # 所有实例共用的启动脚本(catalina.sh, startup.sh等) ├── lib/ └── ... /opt/tomcat-instances/ # 所有实例的根目录(自定义,非必须,但强烈建议) ├── jpress-prod/ # 生产实例:端口8080,连接MySQL主库 │ ├── conf/ # server.xml, web.xml, context.xml等 │ ├── logs/ # catalina.out, localhost.log等 │ ├── webapps/ # 只放jpress.war,不放ROOT或其他应用 │ ├── work/ # JSP编译缓存,每次启动自动清空 │ └── temp/ # 临时文件 ├── jpress-test/ # 测试实例:端口8081,连接测试库 │ ├── conf/ │ ├── logs/ │ ├── webapps/ │ ├── work/ │ └── temp/ └── jpress-dev/ # 开发实例:端口8082,H2嵌入式数据库 ├── conf/ ├── logs/ ├── webapps/ ├── work/ └── temp/提示:
/opt/tomcat-instances/这个父目录不是Tomcat默认路径,完全由你定义。它的存在让所有实例配置集中管理,避免散落在/home或/var下难以追踪。Windows用户请将/opt替换为C:\tomcat,路径分隔符用反斜杠。
2.2 为什么不用Docker?为什么不用systemd服务?
有人会问:“现在都2024年了,为啥不用Docker?”——因为jpress的部署场景往往不在云原生环境。某地市级政务信息平台曾让我协助迁移,他们服务器是十年前采购的物理机,内核不支持Docker,运维团队只会service httpd start。强行上容器,等于把简单问题复杂化。而systemd服务虽好,但systemctl start tomcat@prod这种写法,对新手来说,.service文件里Environment="CATALINA_BASE=/opt/tomcat-instances/jpress-prod"这种变量传递,容易配错且报错晦涩。我们选择最原始、最透明的方式:直接调用catalina.sh脚本,显式传入-Dcatalina.base参数。这样每一步都看得见、摸得着,出问题立刻能定位是脚本、参数还是权限。
2.3 实例间的关键隔离点:哪些必须独立?哪些可以共享?
不是所有文件夹都需物理隔离。根据Tomcat生命周期,我划出三条铁律:
- 必须独立:
conf/(配置)、logs/(日志)、webapps/(应用)、work/(JSP编译)、temp/(临时文件)。其中conf/server.xml里的<Server port>、<Connector port>、<Engine name>必须全局唯一,否则端口冲突或日志混杂。 - 可以共享:
bin/(启动脚本)、lib/(核心jar)、endorsed/(覆盖JDK类)。bin/catalina.sh本身不保存状态,它只是个“指挥官”,真正干活的是$CATALINA_BASE/conf/下的配置。 - 谨慎共享:
shared/lib/(共享库)。jpress本身不依赖特殊第三方jar,所以我不建这个目录。若未来要集成统一认证SDK,才考虑在此放auth-sdk.jar,避免每个实例重复拷贝。
这个设计的底层逻辑是:让变化的部分独立,让不变的部分复用。Tomcat程序版本升级,只需替换/opt/tomcat/bin/和/opt/tomcat/lib/;jpress版本升级,只需替换对应webapps/jpress.war;数据库切换,只需改conf/context.xml里的<Resource>标签。职责清晰,修改点明确。
3. 核心配置详解:从server.xml到context.xml的逐行解析
配置是多实例的灵魂,也是最容易出错的地方。我不会罗列所有XML标签,只聚焦jpress部署最关键的四个文件,结合实际场景解释每一行为什么这么写。
3.1 server.xml:端口、服务名与引擎的精准控制
这是每个实例conf/下最核心的文件。以jpress-prod/conf/server.xml为例,关键段落如下:
<Server port="8005" shutdown="SHUTDOWN"> <!-- 8005是shutdown端口,必须每个实例不同 --> <!-- prod用8005,test用8006,dev用8007 --> </Server> <Service name="jpress-prod"> <!-- Service名必须唯一,用于区分不同实例的日志和监控 --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" /> <!-- HTTP端口:prod用8080,test用8081,dev用8082 --> <!-- URIEncoding="UTF-8" 必加!否则jpress后台中文菜单乱码 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <!-- AJP端口:prod用8009,test用8010,dev用8011 --> <!-- 若前端用Nginx反向代理,此端口可禁用,注释掉即可 --> <Engine name="jpress-prod" defaultHost="localhost"> <!-- Engine名必须与Service名一致,且全局唯一 --> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <!-- appBase="webapps" 是相对路径,指向 $CATALINA_BASE/webapps --> <!-- 不要写成绝对路径!否则跨实例会混乱 --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> <!-- 日志路径是相对的,自动落到 $CATALINA_BASE/logs/ 下 --> </Host> </Engine> </Service>注意:
<Server port>、<Connector port>、<Engine name>、<Service name>这四处是“四重锁”,缺一不可。我曾帮某公司排查,他们只改了HTTP端口,忘了改<Server port>,结果两个实例抢8005端口,后启动的那个直接报Address already in use,但错误日志藏在catalina.out里,不仔细看根本找不到。
3.2 context.xml:jpress数据库与文件存储的生命线
jpress所有数据都靠这个文件打通。conf/context.xml不是Tomcat默认创建的,必须手动新建。内容极简,但字字关键:
<?xml version="1.0" encoding="UTF-8"?> <Context> <!-- 配置jpress使用的数据库 --> <Resource name="jdbc/jpress" auth="Container" type="javax.sql.DataSource" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/jpress_prod?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false" username="jpress_user" password="StrongPass123!" maxActive="20" minIdle="5" initialSize="5" validationQuery="SELECT 1" testOnBorrow="true" removeAbandonedOnBorrow="true" removeAbandonedTimeout="60" logAbandonedOnBorrow="true" /> <!-- 配置jpress附件上传路径 --> <Environment name="jpress.upload.path" value="/data/jpress-prod/uploads" type="java.lang.String" override="false" /> <!-- 此路径必须提前创建,并赋予tomcat用户读写权限 --> <!-- prod用 /data/jpress-prod/uploads,test用 /data/jpress-test/uploads --> </Context>这里有两个易错点:第一,url中的&必须写成&,XML语法强制要求,否则Tomcat启动直接失败,报cvc-complex-type.2.4.a: Invalid content was found starting with element 'url';第二,jpress.upload.path的值必须是绝对路径,且不能放在$CATALINA_BASE下(否则实例重启时work/目录清空可能误删上传文件)。我习惯统一放在/data/分区,既独立于系统盘,又方便做定时备份。
3.3 catalina.properties:JVM与类加载的隐形开关
这个文件控制Tomcat底层行为。jpress-prod/conf/catalina.properties中,我必改的三处:
# 指定jpress实例的JVM参数,不写在这里,启动时无法生效 JAVA_OPTS="-server -Xms1024m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -Dfile.encoding=UTF-8" # 关键!告诉Tomcat:你的配置在哪?日志写哪?应用放哪? catalina.base=/opt/tomcat-instances/jpress-prod catalina.home=/opt/tomcat java.io.tmpdir=/opt/tomcat-instances/jpress-prod/temp # 类加载优化:避免jpress的logback与Tomcat自带log4j冲突 common.loader="${catalina.home}/lib/","${catalina.home}/lib/*.jar","${catalina.base}/lib/","${catalina.base}/lib/*.jar"注意:
JAVA_OPTS在这里设置,比在bin/setenv.sh里更可靠。因为catalina.sh会优先读取此文件中的JAVA_OPTS,再与脚本里定义的合并。common.loader路径必须包含"${catalina.base}/lib/",否则你把jpress需要的mysql-connector-java-8.0.33.jar放到实例自己的lib/下,Tomcat根本加载不到。
3.4 web.xml(可选):当jpress需要定制化时
标准jpress WAR包自带web.xml,一般无需修改。但若你启用了HTTPS,或需要强制跳转www域名,就得在$CATALINA_BASE/conf/web.xml里加filter:
<filter> <filter-name>HttpsRedirectFilter</filter-name> <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class> <init-param> <param-name>hstsEnabled</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>HttpsRedirectFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>这个filter会自动给所有响应头加上Strict-Transport-Security,提升jpress站点安全性。但它只对当前实例生效,prod加了,test没加,互不影响——这正是多实例的价值:策略按需启用,不搞一刀切。
4. jpress部署实操:从war包解压到首页可访问的完整链路
理论讲完,现在进入真刀真枪环节。以下步骤在CentOS 7.9上全程实测,耗时约12分钟。Windows用户注意路径和命令差异,我会特别标注。
4.1 环境准备:JDK、Tomcat二进制包与jpress WAR包
首先确认JDK版本。jpress 4.0+要求JDK 8u202+或JDK 11+:
# 查看已安装JDK java -version # 输出应为 openjdk version "11.0.22" 或类似 # 若未安装,用yum安装OpenJDK 11(CentOS) sudo yum install -y java-11-openjdk-devel # 设置JAVA_HOME(写入 /etc/profile) echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk' | sudo tee -a /etc/profile echo 'export PATH=$JAVA_HOME/bin:$PATH' | sudo tee -a /etc/profile source /etc/profile下载Tomcat 9.0.83(官网最新稳定版):
cd /tmp wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.83/bin/apache-tomcat-9.0.83.tar.gz tar -xzf apache-tomcat-9.0.83.tar.gz -C /opt/ sudo mv /opt/apache-tomcat-9.0.83 /opt/tomcat # 设置属主,避免后续权限问题 sudo chown -R root:root /opt/tomcat下载jpress 4.2.0 WAR包(以GitHub Release为准):
wget https://github.com/JPressProjects/jpress/releases/download/v4.2.0/jpress-4.2.0.war # 重命名为标准名,方便识别 mv jpress-4.2.0.war jpress.warWindows提示:下载Tomcat用ZIP包,解压到
C:\tomcat;JDK安装后,在“系统属性→高级→环境变量”里添加JAVA_HOME和PATH;jpress.war直接下载保存即可。
4.2 创建实例目录并初始化配置
这是最关键的一步,决定后续是否顺利:
# 创建实例根目录 sudo mkdir -p /opt/tomcat-instances/{jpress-prod,jpress-test,jpress-dev} # 复制基础配置(只复制conf/骨架,不复制server.xml等具体内容) sudo cp -r /opt/tomcat/conf/ /opt/tomcat-instances/jpress-prod/ sudo cp -r /opt/tomcat/conf/ /opt/tomcat-instances/jpress-test/ sudo cp -r /opt/tomcat/conf/ /opt/tomcat-instances/jpress-dev/ # 创建webapps、logs、work、temp目录(必须存在,否则启动报错) for dir in webapps logs work temp; do sudo mkdir -p /opt/tomcat-instances/jpress-prod/$dir sudo mkdir -p /opt/tomcat-instances/jpress-test/$dir sudo mkdir -p /opt/tomcat-instances/jpress-dev/$dir done # 赋予tomcat用户(假设为tomcat)完全控制权 # 先创建tomcat用户(若不存在) sudo useradd -r -s /bin/false tomcat # 修改目录属主 sudo chown -R tomcat:tomcat /opt/tomcat-instances/提示:
webapps/目录必须为空或只放jpress.war,不要放ROOT/或其他WAR包。logs/目录必须存在且可写,否则catalina.out无法生成,你将看不到任何启动日志。
4.3 部署jpress WAR包并启动实例
将jpress.war放入对应实例的webapps/目录:
# 放入生产实例 sudo cp jpress.war /opt/tomcat-instances/jpress-prod/webapps/ # 放入测试实例 sudo cp jpress.war /opt/tomcat-instances/jpress-test/webapps/ # 放入开发实例 sudo cp jpress.war /opt/tomcat-instances/jpress-dev/webapps/ # 切换到tomcat用户,启动prod实例(显式指定CATALINA_BASE) sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-prod # 启动test实例 sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-test # 启动dev实例 sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-devWindows操作:以管理员身份打开CMD,执行:
set CATALINA_BASE=C:\tomcat-instances\jpress-prod set CATALINA_HOME=C:\tomcat cd /d C:\tomcat\bin catalina.bat start
4.4 验证与首次访问:检查端口、日志与jpress安装向导
启动后,立刻检查:
# 查看进程是否起来 ps aux | grep tomcat | grep jpress-prod # 应看到类似:tomcat 12345 ... /opt/tomcat/bin/bootstrap.jar ... # 检查端口监听 sudo netstat -tuln | grep ':8080\|:8081\|:8082' # 应显示 0.0.0.0:8080, 0.0.0.0:8081, 0.0.0.0:8082 均在LISTEN状态 # 查看prod实例日志(实时跟踪) sudo tail -f /opt/tomcat-instances/jpress-prod/logs/catalina.out # 启动成功标志:最后几行出现 "Server startup in [xxx] milliseconds"打开浏览器,依次访问:
http://your-server-ip:8080/→ 应看到jpress安装向导页面(第一步:数据库配置)http://your-server-ip:8081/→ 同样是安装向导,但数据库URL指向test库http://your-server-ip:8082/→ 指向dev库,可选H2嵌入式数据库
注意:首次访问时,jpress会自动解压
jpress.war到webapps/jpress/目录,并生成WEB-INF/classes/config.properties。此时webapps/下会有jpress/文件夹和jpress.war两个东西,这是正常现象。后续更新jpress,只需替换jpress.war,Tomcat会自动重新解压覆盖。
4.5 数据库初始化:为三个实例分别创建独立库
jpress安装向导要求填写数据库信息。我们必须为每个实例创建独立数据库,避免数据污染:
-- 登录MySQL mysql -u root -p -- 创建prod库(utf8mb4字符集,兼容emoji) CREATE DATABASE jpress_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建test库 CREATE DATABASE jpress_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建dev库(H2用户可跳过) CREATE DATABASE jpress_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建专用用户(最小权限原则) CREATE USER 'jpress_prod'@'localhost' IDENTIFIED BY 'StrongPass123!'; CREATE USER 'jpress_test'@'localhost' IDENTIFIED BY 'StrongPass456!'; CREATE USER 'jpress_dev'@'localhost' IDENTIFIED BY 'StrongPass789!'; -- 授权(只给对应库) GRANT ALL PRIVILEGES ON jpress_prod.* TO 'jpress_prod'@'localhost'; GRANT ALL PRIVILEGES ON jpress_test.* TO 'jpress_test'@'localhost'; GRANT ALL PRIVILEGES ON jpress_dev.* TO 'jpress_dev'@'localhost'; FLUSH PRIVILEGES;在jpress安装向导中,prod实例填jpress_prod库名和jpress_prod用户;test填jpress_test;dev填jpress_dev。这样,三个站点的数据彻底隔离,删库跑路也只影响一个实例。
5. 常见问题与排查技巧:那些让你抓狂又不得不面对的坑
实操中,90%的问题都集中在配置路径、权限、端口这三座大山。我把踩过的、同事问爆的、论坛高频问题整理成速查表,并附上独家排查心法。
5.1 启动失败:Address already in use或Permission denied
这是新手第一道坎。错误日志通常在catalina.out里,但很多人没看就重装。
| 现象 | 根本原因 | 排查命令 | 解决方案 |
|---|---|---|---|
SEVERE: Failed to initialize end point associated with ProtocolHandler ["http-nio-8080"] java.net.BindException: Address already in use | 端口被占(其他Tomcat、Nginx、甚至IDEA内置服务器) | sudo lsof -i :8080或sudo netstat -tulnp | grep :8080 | 杀掉进程sudo kill -9 <PID>,或改server.xml中端口 |
java.io.FileNotFoundException: /opt/tomcat-instances/jpress-prod/logs/catalina.out (Permission denied) | tomcat用户对logs/目录无写权限 | ls -ld /opt/tomcat-instances/jpress-prod/logs/ | sudo chown tomcat:tomcat /opt/tomcat-instances/jpress-prod/logs/ |
Cannot find /opt/tomcat/bin/setenv.sh | Tomcat找不到setenv.sh,但catalina.properties里JAVA_OPTS没生效 | cat /opt/tomcat/conf/catalina.properties | grep JAVA_OPTS | 确保catalina.properties中JAVA_OPTS行未被注释,且语法正确 |
实操心得:永远先看
catalina.out!不要凭感觉猜。用sudo -u tomcat /opt/tomcat/bin/catalina.sh run -Dcatalina.base=...前台启动,错误直接打在终端,比后台启动快十倍定位。
5.2 jpress访问异常:白页、404、数据库连不上
jpress界面出问题,90%是配置没对上。
| 现象 | 根本原因 | 关键检查点 | 解决方案 |
|---|---|---|---|
访问http://ip:8080/显示404,但http://ip:8080/jpress/能进 | jpress.war包名不是jpress.war,或server.xml里<Host appBase>指向错误 | ls /opt/tomcat-instances/jpress-prod/webapps/是否有jpress.war?appBase是否为webapps? | 重命名为jpress.war;确认appBase="webapps"(不是"."或绝对路径) |
安装向导填完数据库点“下一步”,卡住或报Connection refused | context.xml里url写错,或MySQL没开远程访问 | mysql -h 127.0.0.1 -u jpress_prod -p jpress_prod能否连?telnet 127.0.0.1 3306是否通? | 检查url中IP、端口、库名;确认MySQLbind-address = 127.0.0.1(非localhost);检查防火墙sudo firewall-cmd --list-ports |
jpress后台上传图片失败,提示No such file or directory | context.xml里jpress.upload.path路径不存在,或tomcat用户无权限 | ls -ld /data/jpress-prod/uploads | sudo mkdir -p /data/jpress-prod/uploads && sudo chown -R tomcat:tomcat /data/jpress-prod/ |
注意:jpress的
config.properties生成后,会缓存数据库连接。如果中途改了context.xml,必须删掉webapps/jpress/WEB-INF/classes/config.properties,再重启Tomcat,否则新配置不生效。
5.3 日志混乱:多个实例日志写到同一个catalina.out
这是CATALINA_BASE没设对的典型症状。catalina.out本该在每个实例自己的logs/下,但如果启动时没传-Dcatalina.base,Tomcat会默认用$CATALINA_HOME,导致所有实例往/opt/tomcat/logs/catalina.out里狂写。
终极验证法:启动后,立刻执行:
ps aux | grep catalina | grep -v grep # 正确输出应包含:-Dcatalina.base=/opt/tomcat-instances/jpress-prod # 如果没有,说明启动命令漏了-D参数!解决方案:写个启动脚本start-prod.sh,固化参数:
#!/bin/bash export CATALINA_HOME=/opt/tomcat export CATALINA_BASE=/opt/tomcat-instances/jpress-prod /opt/tomcat/bin/startup.sh然后chmod +x start-prod.sh && sudo -u tomcat ./start-prod.sh。一劳永逸。
5.4 性能瓶颈:内存溢出(OutOfMemoryError)
jpress本身不重,但多实例共用物理内存,容易OOM。catalina.properties里JAVA_OPTS是救命稻草:
# 生产实例(2G内存起步) JAVA_OPTS="-server -Xms1536m -Xmx1536m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" # 测试实例(1G够用) JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+UseG1GC" # 开发实例(512M足矣) JAVA_OPTS="-server -Xms512m -Xmx512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m"提示:
-Xms和-Xmx设为相同值,避免JVM动态扩容缩容带来的GC抖动。-XX:+UseG1GC是JDK8u20之后的推荐垃圾收集器,比-XX:+UseParallelGC更平稳。
6. 运维与扩展:让多实例真正融入日常工作流
搭好只是开始,如何让它长期稳定、便于管理、还能平滑演进?这才是资深博主的硬功夫。
6.1 一键启停脚本:告别手敲10遍catalina.sh
写三个shell脚本,放在/opt/tomcat-instances/下:
# /opt/tomcat-instances/start-all.sh #!/bin/bash echo "Starting jpress-prod..." sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-prod echo "Starting jpress-test..." sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-test echo "Starting jpress-dev..." sudo -u tomcat /opt/tomcat/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-dev # /opt/tomcat-instances/stop-all.sh #!/bin/bash echo "Stopping jpress-prod..." sudo -u tomcat /opt/tomcat/bin/catalina.sh stop -Dcatalina.base=/opt/tomcat-instances/jpress-prod echo "Stopping jpress-test..." sudo -u tomcat /opt/tomcat/bin/catalina.sh stop -Dcatalina.base=/opt/tomcat-instances/jpress-test echo "Stopping jpress-dev..." sudo -u tomcat /opt/tomcat/bin/catalina.sh stop -Dcatalina.base=/opt/tomcat-instances/jpress-dev赋予权限:sudo chmod +x /opt/tomcat-instances/start-all.sh /opt/tomcat-instances/stop-all.sh。以后运维只需sudo /opt/tomcat-instances/start-all.sh,5秒搞定。
6.2 日志轮转:防止catalina.out撑爆磁盘
Tomcat自带logging.properties,但默认不轮转。编辑/opt/tomcat-instances/jpress-prod/conf/logging.properties:
# 将ConsoleHandler改为FileHandler,并启用轮转 handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler # 配置catalina日志轮转(每天一个文件,保留30天) 1catalina.org.apache.juli.AsyncFileHandler.level = FINE 1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina. 1catalina.org.apache.juli.AsyncFileHandler.maxDays = 30 1catalina.org.apache.juli.AsyncFileHandler.bufferSize = 16384重启实例后,logs/下会出现catalina.2024-05-20.log、catalina.2024-05-19.log……自动清理旧日志。
6.3 平滑升级:Tomcat版本更新不中断jpress服务
当Tomcat发布新版本(如9.0.84),如何无缝升级?三步走:
- 停旧启新:先停
jpress-prod,再解压新Tomcat到/opt/tomcat-new,然后用新bin/catalina.sh启动,指向旧CATALINA_BASE:sudo -u tomcat /opt/tomcat-new/bin/catalina.sh start -Dcatalina.base=/opt/tomcat-instances/jpress-prod - 验证功能:访问
http://ip:8080/,确认jpress一切正常。 - 切换软链接:
sudo rm /opt/tomcat && sudo ln -s /opt/tomcat-new /opt/tomcat。
整个过程,jpress服务中断时间小于30秒。这就是CATALINA_HOME与CATALINA_BASE分离设计的最大红利。
6.4 安全加固:最小权限原则的落地实践
生产环境绝不能用root跑Tomcat。我的加固清单:
- 用户隔离:为