前言

image-20250817215422535

应粉丝盆友的要求,写一篇关于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目录

img

这是应用部署的地方,默认会有几个示例应用:

  • 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

image-20250817214904835

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服务时踩过几个坑:

  1. JAVA_HOME路径错误:一定要用绝对路径,相对路径可能找不到
  2. 权限问题:tomcat用户要有执行bin目录下脚本的权限
  3. PID文件路径:temp目录要有写权限,不然创建不了pid文件
  4. 环境变量: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连接监控。

生产环境部署注意事项

安全配置

生产环境一定要关闭不必要的功能:

  1. 删除webapps下的默认应用(ROOT、examples、docs、manager)
  2. 修改默认端口
  3. 配置访问控制

在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版本不兼容
  • 配置文件语法错误
  • 权限不足

应用无法访问

检查几个方面:

  1. 防火墙是否开放端口
  2. 应用是否正确部署
  3. 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较劲的兄弟们。部署这活儿,经验很重要,多踩几个坑就熟练了。

如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!

公众号:运维躬行录

个人博客:躬行笔记

标签: none