踩坑无数!Tomcat部署项目的那些年,我总结出这套保命指南
前言
应粉丝盆友的要求,写一篇关于tomcat部署的文章。希望能够帮助到大家!!!说起Tomcat部署项目,我想每个搞Java开发和运维的兄弟都有一肚子话要说。记得接触那会儿,看着那个小猫咪的logo还觉得挺可爱,结果第一次部署项目就被它折腾得死去活来。
这些年下来,从Tomcat 6到现在的Tomcat 11,各种版本都碰过,踩的坑能绕地球三圈。今天就跟大家聊聊Tomcat部署项目的那些事儿,希望能帮后来的兄弟们少走点弯路。
Tomcat到底是个啥玩意儿
可能有些刚入门的朋友还不太清楚Tomcat是干什么的,我简单说说。
Tomcat其实就是一个Web服务器,更准确地说是一个Servlet容器。什么意思呢?就是专门用来跑Java Web应用的。你写的那些JSP、Servlet,打包成war文件,就是丢给Tomcat来运行的。
它是Apache软件基金会开发的开源项目,完全免费。相比其他的应用服务器比如WebLogic、WebSphere这些商业产品,Tomcat轻量级很多,配置也相对简单。这也是为什么这么多公司选择它的原因。
Tomcat的架构其实挺有意思的,它有几个核心组件:
- Catalina:这是Servlet容器的实现,负责处理Servlet和JSP
- Coyote:HTTP连接器,处理HTTP请求
- Jasper:JSP引擎,把JSP编译成Servlet
说白了,当用户在浏览器里访问你的网站时,请求先到Coyote,然后交给Catalina处理,如果是JSP页面就用Jasper编译。整个流程就是这样。
我记得刚开始学的时候,总是搞不清楚Tomcat和Apache HTTP Server的区别。简单说,Apache HTTP Server主要处理静态内容,而Tomcat专门处理动态的Java内容。当然,Tomcat也能处理静态文件,但性能没有Apache HTTP Server好。所以生产环境经常是Apache + Tomcat的组合,Apache在前面做反向代理。
现在市面上还有其他选择,比如Jetty、Undertow这些,但Tomcat的市场占有率还是最高的。主要是生态成熟,文档齐全,遇到问题容易找到解决方案。
Tomcat目录结构,这些你得搞清楚
解压完Tomcat后,看到一堆目录可能会懵,我来给大家介绍一下各个目录的作用。了解这些对后面的配置和部署很重要。
apache-tomcat-9.0.80/
├── bin/ # 可执行文件目录
├── conf/ # 配置文件目录
├── lib/ # 类库目录
├── logs/ # 日志文件目录
├── temp/ # 临时文件目录
├── webapps/ # Web应用部署目录
└── work/ # 工作目录
bin目录
这里放的都是可执行文件,最常用的几个:
- startup.sh/startup.bat:启动脚本
- shutdown.sh/shutdown.bat:关闭脚本
- catalina.sh/catalina.bat:核心启动脚本,startup其实就是调用它
- version.sh/version.bat:查看版本信息
我经常用到的还有setenv.sh,这个文件默认不存在,需要自己创建。在里面可以设置JVM参数,比startup.sh里设置更规范:
#!/bin/bash
export JAVA_OPTS="-Xms2048m -Xmx4096m -server"
export CATALINA_OPTS="-Dfile.encoding=UTF-8"
conf目录
配置文件都在这里,核心的几个文件:
- server.xml:主配置文件,定义端口、连接器等
- web.xml:全局web应用配置
- context.xml:全局Context配置
- tomcat-users.xml:用户权限配置
- logging.properties:日志配置
还有个Catalina目录,里面是各个虚拟主机的配置。默认有个localhost目录,我们经常在里面放Context配置文件。
webapps目录
这是应用部署的地方,默认会有几个示例应用:
- ROOT:根应用,访问http://localhost:8080就是它
- examples:示例应用,有各种Servlet和JSP例子
- docs:文档应用
- manager:管理应用,可以通过web界面管理其他应用
- host-manager:虚拟主机管理
生产环境建议把除了ROOT以外的都删掉,安全考虑。
logs目录
日志文件存放位置,主要的几个:
- catalina.out:标准输出日志,最重要的一个
- catalina.YYYY-MM-DD.log:Catalina引擎日志
- localhost.YYYY-MM-DD.log:localhost虚拟主机日志
- manager.YYYY-MM-DD.log:manager应用日志
- access_log.YYYY-MM-DD.txt:访问日志(需要配置才有)
排查问题时,catalina.out是必看的,应用启动失败的信息基本都在里面。
work目录
这是Tomcat的工作目录,主要存放:
- JSP编译后的Servlet类文件
- Session序列化文件
- 其他临时文件
一般不需要手动操作这个目录,但有时候JSP修改后不生效,可以删除对应的编译文件强制重新编译。
lib目录
存放Tomcat运行需要的jar包,包括:
- Servlet API
- JSP API
- Tomcat核心类库
如果有全局的第三方jar包,也可以放在这里,但不建议。最好还是放在应用的WEB-INF/lib下。
我之前遇到过一个坑,项目用了某个jar包的新版本,但lib目录里有旧版本,结果类冲突各种奇怪问题。所以lib目录里的东西,没事别乱动。
temp目录
临时文件目录,Tomcat运行时可能会在这里创建临时文件。一般也不需要关注,但要确保有写权限。
了解了这些目录结构,部署和配置就心里有数了。特别是webapps、conf、logs这三个,是经常要打交道的。
环境准备这块,真的不能马虎
部署之前,环境搭建是第一步。别小看这个步骤,我见过太多因为环境问题导致后面一堆麻烦的案例。
JDK版本选择
Tomcat对JDK版本是有要求的,这个兼容性表格我建议大家收藏:
- Tomcat 10.x 需要 Java 11+
- Tomcat 9.x 支持 Java 8+
- Tomcat 8.x 支持 Java 7+
我之前就遇到过一个哭笑不得的情况,项目用的Tomcat 10,结果服务器上装的是Java 8,启动的时候各种报错。当时还以为是配置问题,折腾了半天才发现是版本不匹配。
检查JDK版本很简单:
java -version
javac -version
记住,这两个版本号要一致,不然可能会有奇怪的问题。
java环境安装教程:https://blog.csdn.net/Coin_Collecter/article/details/136825041
Tomcat下载和解压
去Apache官网下载Tomcat,建议下载tar.gz格式的,zip有时候在Linux下会有权限问题。
官网地址:https://tomcat.apache.org/index.html
wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.108/bin/apache-tomcat-9.0.108.tar.gz
tar -xzf apache-tomcat-9.0.108.tar.gz
mv apache-tomcat-9.0.108 /opt/tomcat
解压完记得检查一下目录结构,确保bin、conf、webapps这些关键目录都在。
配置systemd服务,一键启停真香
手动启停Tomcat太麻烦了,每次都要cd到bin目录执行脚本。更要命的是服务器重启后还得手动启动。配置成systemd服务就方便多了,开机自启、一键重启,运维必备技能。
创建tomcat用户
首先创建专门的tomcat用户,安全考虑不要用root跑服务:
# 创建系统用户
sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
# 设置目录权限
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh
这里用-s /bin/false是为了安全,这个用户不能登录shell。
创建systemd服务文件
在/etc/systemd/system/目录下创建tomcat.service文件:
sudo vim /etc/systemd/system/tomcat.service
内容如下:
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_OPTS=-Xms2048M -Xmx4096M -server -XX:+UseParallelGC"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
这里几个关键配置解释一下:
- Type=forking:因为startup.sh会fork出子进程
- CATALINA_PID:指定pid文件位置,方便管理
- RestartSec=10:服务异常退出后10秒重启
- Restart=always:总是重启,除非手动停止
环境变量配置
JAVA_HOME路径要根据实际情况调整,可以用这个命令查看:
sudo update-alternatives --config java
如果有多个Java版本,选择对应的路径。
重载并启动服务
配置完成后重载systemd配置:
# 重载systemd配置
sudo systemctl daemon-reload
# 启动tomcat服务
sudo systemctl start tomcat
# 查看服务状态
sudo systemctl status tomcat
# 设置开机自启
sudo systemctl enable tomcat
如果启动失败,用journalctl查看详细日志:
sudo journalctl -u tomcat.service -f
常用管理命令
配置好后,管理Tomcat就方便多了:
# 启动服务
sudo systemctl start tomcat
# 停止服务
sudo systemctl stop tomcat
# 重启服务
sudo systemctl restart tomcat
# 重新加载配置(不重启进程)
sudo systemctl reload tomcat
# 查看服务状态
sudo systemctl status tomcat
# 查看服务日志
sudo journalctl -u tomcat.service
# 开机自启
sudo systemctl enable tomcat
# 禁用开机自启
sudo systemctl disable tomcat
配置过程中的坑
我在配置systemd服务时踩过几个坑:
- JAVA_HOME路径错误:一定要用绝对路径,相对路径可能找不到
- 权限问题:tomcat用户要有执行bin目录下脚本的权限
- PID文件路径:temp目录要有写权限,不然创建不了pid文件
- 环境变量:JVM参数最好在这里设置,不要在catalina.sh里改
我把JAVA_HOME设置成了/usr/bin/java,结果一直启动失败。后来才知道要设置到JDK的根目录,不是可执行文件路径。
高级配置
如果需要更精细的控制,可以加一些高级配置:
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
Wants=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_OPTS=-Xms2048M -Xmx4096M -server -XX:+UseG1GC"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Dfile.encoding=UTF-8"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
ExecReload=/bin/kill -s HUP $MAINPID
# 超时设置
TimeoutStartSec=300
TimeoutStopSec=30
# 重启策略
RestartSec=10
Restart=on-failure
# 资源限制
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.target
这样配置后,Tomcat就完全融入到systemd管理体系中了。服务器重启自动启动,异常退出自动重启,再也不用担心服务挂掉了。
基础配置调优,这些参数得改
默认的Tomcat配置基本上只能跑个Hello World,生产环境肯定不够用。
server.xml核心配置
打开conf/server.xml,这个文件是Tomcat的心脏。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="200"
minSpareThreads="10"
maxSpareThreads="100"
acceptCount="100"
redirectPort="8443" />
这里面几个关键参数:
- maxThreads:最大线程数,根据你的服务器配置调整
- connectionTimeout:连接超时时间,20秒一般够用
- acceptCount:等待队列长度,超过这个数就拒绝连接
我一般会把maxThreads调到500左右,但这个要根据实际情况。记得有次为了提高并发,直接调到2000,结果服务器内存爆了,那叫一个酸爽。
JVM参数优化
在bin/catalina.sh(Windows是catalina.bat)里加上JVM参数:
export JAVA_OPTS="-Xms2048m -Xmx4096m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.awt.headless=true"
内存分配这块要注意,Xms是初始内存,Xmx是最大内存。我的经验是Xms设置为Xmx的一半到三分之二比较合适。
项目部署的几种姿势
WAR包部署
最常见的方式,把war包丢到webapps目录下就行。Tomcat会自动解压部署。
cp myproject.war /opt/tomcat/webapps/
但是有个坑,如果你的war包名字有特殊字符或者中文,可能会有问题。我建议统一用英文小写加下划线的命名方式。
部署完可以看看logs/catalina.out,确认部署成功:
tail -f /opt/tomcat/logs/catalina.out
目录部署
有时候为了方便调试,会直接把解压后的项目目录放到webapps下:
mkdir /opt/tomcat/webapps/myproject
cp -r /path/to/project/* /opt/tomcat/webapps/myproject/
这种方式的好处是可以直接修改文件,不用重新打包。但生产环境不建议这么搞。
Context配置部署
在conf/Catalina/localhost/下创建xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="/path/to/project" reloadable="true">
</Context>
这种方式比较灵活,项目可以放在任意位置。
那些年踩过的坑
端口冲突问题
默认8080端口经常被占用,特别是开发环境。检查端口占用:
netstat -tlnp | grep 8080
lsof -i:8080
如果被占用了,要么杀掉占用进程,要么改Tomcat端口。改端口在server.xml里:
<Connector port="8888" protocol="HTTP/1.1" ... />
权限问题
Linux下经常遇到权限问题,特别是日志写不进去:
chown -R tomcat:tomcat /opt/tomcat
chmod +x /opt/tomcat/bin/*.sh
我建议专门创建一个tomcat用户来运行服务,不要用root。
内存溢出
这个问题太常见了,特别是PermGen space错误。Java 8以后用MetaSpace替代了PermGen,但还是要注意:
export JAVA_OPTS="$JAVA_OPTS -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
中文乱码
这个问题困扰了我很久,主要是编码设置不统一。在server.xml的Connector里加上:
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
useBodyEncodingForURI="true" />
监控和日志管理
日志配置
Tomcat的日志配置在conf/logging.properties里。我一般会调整日志级别和输出格式:
org.apache.catalina.core.ContainerBase.[Catalina].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].handlers = java.util.logging.ConsoleHandler
性能监控
可以开启JMX监控:
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9999"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
然后用JConsole或者VisualVM连接监控。
生产环境部署注意事项
安全配置
生产环境一定要关闭不必要的功能:
- 删除webapps下的默认应用(ROOT、examples、docs、manager)
- 修改默认端口
- 配置访问控制
在server.xml里可以配置访问限制:
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="192\.168\.1\.\d+|127\.0\.0\.1" />
集群部署
如果需要集群,可以配置session复制:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
</Cluster>
不过说实话,现在更多是用Nginx做负载均衡,session用Redis存储。
自动化部署脚本
手动部署容易出错,我写了个简单的部署脚本:
#!/bin/bash
APP_NAME="myproject"
TOMCAT_HOME="/opt/tomcat"
WAR_PATH="/tmp/${APP_NAME}.war"
# 停止Tomcat
$TOMCAT_HOME/bin/shutdown.sh
# 备份旧版本
if [ -d "$TOMCAT_HOME/webapps/$APP_NAME" ]; then
mv $TOMCAT_HOME/webapps/$APP_NAME $TOMCAT_HOME/webapps/${APP_NAME}_backup_$(date +%Y%m%d_%H%M%S)
fi
# 部署新版本
cp $WAR_PATH $TOMCAT_HOME/webapps/
# 启动Tomcat
$TOMCAT_HOME/bin/startup.sh
# 检查部署状态
sleep 10
curl -I http://localhost:8080/$APP_NAME
常见问题排查
启动失败
看catalina.out日志,一般都能找到原因。常见的有:
- 端口被占用
- JDK版本不兼容
- 配置文件语法错误
- 权限不足
应用无法访问
检查几个方面:
- 防火墙是否开放端口
- 应用是否正确部署
- Context路径是否正确
# 检查防火墙
firewall-cmd --list-ports
# 或者
iptables -L
# 检查应用状态
curl -I http://localhost:8080/myproject
性能问题
用jstack和jmap分析:
# 查看线程堆栈
jstack <tomcat_pid> > thread_dump.txt
# 查看内存使用
jmap -histo <tomcat_pid>
Docker化部署
现在很多项目都容器化了,Tomcat也不例外。写个简单的Dockerfile:
FROM tomcat:9.0-jdk11
# 删除默认应用
RUN rm -rf /usr/local/tomcat/webapps/*
# 复制应用
COPY myproject.war /usr/local/tomcat/webapps/ROOT.war
# 复制配置文件
COPY server.xml /usr/local/tomcat/conf/
EXPOSE 8080
CMD ["catalina.sh", "run"]
构建和运行:
docker build -t myproject:latest .
docker run -d -p 8080:8080 --name myproject myproject:latest
容器化的好处是环境一致性,但也要注意日志和数据的持久化。
总结
Tomcat部署项目看起来简单,但要做好还是有很多细节需要注意。从环境准备到配置优化,从安全设置到性能监控,每个环节都不能马虎。
我这些年的经验告诉我,部署不是一锤子买卖,需要持续的监控和优化。特别是生产环境,一定要做好备份和回滚准备。
记住几个关键点:
- 环境兼容性检查不能省
- 配置参数要根据实际情况调整
- 安全配置在生产环境必须做
- 监控和日志要跟上
- 自动化部署能减少人为错误
希望这篇文章能帮到正在和Tomcat较劲的兄弟们。部署这活儿,经验很重要,多踩几个坑就熟练了。
如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!
公众号:运维躬行录
个人博客:躬行笔记