搞Linux运维和Java开发这些年,Tomcat算是打交道最多的中间件之一了。几乎每个Java Web项目,从开发环境到测试再到生产,都绕不开在Linux上把Tomcat装好、配好、跑起来。很多新手第一次接触这套流程,往往卡在各种细节上:JDK版本选错了、环境变量没配、端口起不来、日志看不懂。这篇文章就是把整个流程完整走一遍,从选型到部署,从配置到排错,尽量把该踩的坑都提前说出来,方便你照着操作就能顺利跑起来。
这篇内容适合刚接触Linux和Java部署的初学者,也适合需要快速搭建测试环境的开发同学,对于要上生产的老手,配置调优部分也能做个参照。
1. 安装前的环境准备与基础选型
1.1 版本搭配是第一步,别一上来就装最新的
很多人一上来就下载最新版Tomcat 11或者最新JDK 21,结果项目跑不起来,还以为是配置问题。实际上Tomcat和JDK的版本匹配是有讲究的,不同版本的Tomcat对JDK版本有硬性要求。
目前常用的搭配大概是这样:
| Tomcat版本 | 最低JDK版本 | 适用场景 |
|---|---|---|
| Tomcat 8.5 | JDK 7+ | 老项目兼容性好,很多企业仍在用 |
| Tomcat 9 | JDK 8+ | 最主流的选择,资料多,坑少 |
| Tomcat 10 | JDK 11+ | 新项目,但Servlet API包名变了,老项目迁移麻烦 |
| Tomcat 11 | JDK 17+ | 太新,除非项目明确要求,否则不建议生产直接用 |
个人建议稳妥起见选Tomcat 9 + JDK 8,这两个组合是过去十年Java Web领域最经典的搭配,遇到的任何问题几乎都能在网上找到答案。如果你用的是云镜像或者Docker方式部署,很多镜像直接内置了JDK,比如常见的镜像标签tomcat:8.5-jdk8-corretto,这种就是Amazon的Corretto JDK 8搭配Tomcat 8.5,开箱即用,省去自己配JDK的麻烦。但本文先讲标准的手工安装流程,方便你理解每一层的关系。
1.2 确认你的Linux系统环境
在动手之前,先搞清楚自己手上是什么系统,架构是什么,这决定了你下载哪个安装包。一般用这几条命令确认:
cat /etc/os-release uname -m看/etc/os-release能拿到发行版信息,是CentOS、Ubuntu还是Debian;uname -m看架构,x86_64就是常见的64位,aarch64就是ARM架构。别小看这一步,下载错了二进制包,连解压都报错,更别提运行了。
另外,确认一下系统里是否已经装了JDK。很多系统镜像自带OpenJDK,但版本可能不对。用java -version看一眼,如果没有输出或者提示command not found,那就走下一步安装JDK的流程。如果版本太新或者太老,我建议先卸掉再装合适的,避免后续出现莫名奇妙的问题。卸载用包管理器就行,CentOS系是yum remove,Ubuntu系是apt remove。
1.3 获取安装包,别从乱七八糟的网站下
Tomcat的官方下载地址是Apache Tomcat官网,但国内直连官网有时候很慢,建议用国内镜像站,比如阿里云镜像、华为云镜像,速度稳定得多。不要随便从第三方网站下载,你没法保证里面有没有被塞东西,供应链安全在真实生产环境里是非常严肃的问题。
下载的时候注意认准tar.gz包,不要下成源码包或者zip包。源码包需要自己编译,麻烦且没必要;zip包在Linux下也能解压,但文件权限、软链接等处理不如tar.gz方便。用wget或者curl下载即可,比如:
wget https://mirrors.aliyun.com/apache/tomcat/tomcat-9/v9.0.98/bin/apache-tomcat-9.0.98.tar.gz版本号以实际为准。文件不大,大概12MB左右,下载很快。顺便提一句,校验一下SHA512校验和是个好习惯,官方页面会给出对应的校验值,Linux下用sha512sum命令比对就行。
2. JDK安装与环境变量配置
2.1 用包管理器安装OpenJDK,省心又干净
JDK的安装方式有好几种,手工解压官网的JDK包、用包管理器安装、用Docker镜像自带。对于Linux服务器上部署Tomcat这个场景,我推荐用包管理器装OpenJDK,原因很简单:第一,软件源里的版本经过测试,兼容性好;第二,卸载和升级都方便,符合Linux包管理哲学。
以CentOS/RHEL系为例:
yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel这里特别注意,一定要装-devel版本。很多新手只装java-1.8.0-openjdk,结果发现只有JRE(Java Runtime Environment),没有javac编译器和完整的JDK工具链。虽然Tomcat运行本身只需要JRE,但如果你后续要编译JSP或者在服务器上做简单的代码编译测试,没有javac会很痛苦。
Ubuntu/Debian系对应的是:
apt update apt install -y openjdk-8-jdk装完之后,Java可执行文件一般会被放到/usr/lib/jvm/目录下。用ls /usr/lib/jvm/确认一下实际路径,后面配置环境变量要用。这个路径在CentOS和Ubuntu上略有差异,CentOS通常是/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64,Ubuntu通常是/usr/lib/jvm/java-8-openjdk-amd64。记住,实际以你机器上的路径为准。
2.2 JAVA_HOME环境变量配置,这是Tomcat能不能启动的关键
Tomcat启动脚本本身是用Java写的,它启动时的第一件事就是找JAVA_HOME或者JRE_HOME。如果这两个环境变量没设置好,Tomcat会直接报错退出,这是新手遇到最多的错误之一。
修改全局环境变量,编辑/etc/profile文件:
vi /etc/profile在文件末尾追加:
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarCLASSPATH很多人不写,但写上更稳妥,一些老的Java程序会依赖它。保存退出后执行:
source /etc/profile让配置立即生效。这里解释下为什么要改/etc/profile而不是只改当前用户的~/.bashrc:Tomcat经常以独立用户或者系统服务形式运行,如果你只在某个用户的环境变量里配置,切到别的用户或者服务启动时环境变量根本不会加载,很容易排查半天。改全局配置虽然稍微“重”一点,但是一次配置,所有用户和系统服务都能用。
2.3 验证JDK是否装好
配置完环境变量后,验证一下是否生效:
java -version javac -version如果两个命令都有输出,且版本号是1.8.x,说明JDK安装成功。再执行echo $JAVA_HOME,确认环境变量已经正确加载。这一步过了,JDK这块的准备工作就算完成了。
注意:如果你需要在一个系统上跑多个JDK版本,不要用全局环境变量写死,建议每个项目一个独立的Tomcat,在Tomcat的
bin/setenv.sh里单独指定JDK路径。这个文件后面会讲到,先留个印象。
3. Tomcat解压部署与目录结构盘点
3.1 解压、移动、首次启动
假设下载好了apache-tomcat-9.0.98.tar.gz,接下来把它放到你想安装的位置。习惯上,我一般放在/opt/tomcat或者/usr/local/tomcat下,这样路径清晰,一眼就知道这个目录是干嘛的。
mkdir -p /opt/tomcat tar -zxvf apache-tomcat-9.0.98.tar.gz -C /opt/tomcat mv /opt/tomcat/apache-tomcat-9.0.98/* /opt/tomcat/把内容直接放到/opt/tomcat根下,省得目录层级太深。解压之后,看一眼目录结构:
ls -lh /opt/tomcat启动Tomcat很简单:
/opt/tomcat/bin/startup.sh看到类似Tomcat started.的输出,基本就算起来了。然后确认一下进程和端口:
ps -ef | grep tomcat ss -tlnp | grep 8080ss输出里能看到8080端口处于LISTEN状态,说明Tomcat正在监听。这时候用浏览器访问http://服务器IP:8080,如果能打开默认页面,恭喜你,第一步就成功了。
3.2 关键目录结构,搞懂它们能省很多事
Tomcat解压后的目录结构很有规律,我在这里做一个对照说明:
| 目录 | 作用 |
|---|---|
| bin | 存放启动和停止脚本,如startup.sh、shutdown.sh |
| conf | 核心配置文件目录,server.xml、web.xml、tomcat-users.xml都在这里 |
| lib | Tomcat运行依赖的jar包,不要随便动 |
| logs | 日志目录,catalina.out、localhost.log都在这里,排查问题先看这里 |
| webapps | Web应用部署目录,放war包或者解压后的项目目录 |
| work | JSP编译后的临时文件目录,清理后Tomcat会重新编译 |
| temp | 临时文件目录 |
logs目录是重点中的重点。Tomcat启动的详细日志都写在catalina.out里,控制台输出只是其中一部分。很多新手只看startup.sh的终端输出,发现报错信息不全,不知道去哪里看完整日志。记住,判断Tomcat是否真正常启动,永远以catalina.out里的Server startup in [xxx] milliseconds为准。
webapps目录也非常关键。默认情况下这个目录下有ROOT、docs、examples等几个很基础的Web应用,如果不需要可以直接删掉,只保留ROOT用来放默认首页。实际部署项目时,把你的war包丢进这个目录,Tomcat启动时会自动解压并部署。
3.3 注册为系统服务,避免“重启服务器之后Tomcat没了”
开发环境手动启动无所谓,但生产环境里,服务器重启后手动去启动Tomcat就太原始了。建议将Tomcat注册成systemd服务,让系统来管理它的生命周期。
创建服务文件:
vi /etc/systemd/system/tomcat.service内容如下:
[Unit] Description=Apache Tomcat Web Application Container After=network.target [Service] Type=forking Environment=JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.x86_64 Environment=CATALINA_HOME=/opt/tomcat Environment=CATALINA_BASE=/opt/tomcat ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/opt/tomcat/bin/shutdown.sh User=root Group=root Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.targetType=forking很关键,因为Tomcat的startup.sh启动后会立即返回,真正的Java进程是后台进程,所以要告诉systemd这个特性。配置完后执行:
systemctl daemon-reload systemctl enable tomcat systemctl start tomcat systemctl status tomcat这样Tomcat就变成系统服务了,开机自启、异常自动重启都交给systemd处理,同时你还可以用journalctl -u tomcat查看服务日志。这一步从开发环境养成的习惯,到了生产环境会给你省下很多麻烦。
4. 核心配置调优与项目部署实操
4.1 server.xml里必须要懂的三个端口
Tomcat的conf/server.xml是最核心的配置文件,里面定义了三个默认端口,一定要搞清楚各自的作用:
| 端口 | 作用 | 默认值 |
|---|---|---|
| Shutdown端口 | 用于接收关闭命令 | 8005 |
| HTTP连接器端口 | 对外提供Web服务的端口 | 8080 |
| AJP连接器端口 | 用于与Apache/Nginx等Web服务器协作 | 8009 |
HTTP端口就是浏览器访问的端口,生产环境通常要改成80端口,这样访问的时候不用带端口号。改法很简单,在server.xml中找到:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />把8080改成80即可。但这里要注意一个前提:Linux的1024以下端口需要root权限才能监听。如果你用systemd服务方式运行且User=root,那没问题;如果是普通用户启动Tomcat,监听80端口会报Permission denied。这时候要么用普通用户+反向代理转发,要么通过setcap给Java进程授权,不过最省事的还是让Tomcat继续用8080,前面挡一个Nginx或Caddy做转发。
8005端口,也就是Shutdown端口,server.xml里配置的字符串是SHUTDOWN。如果外部可以访问到这个端口,理论上就能通过发送这个字符串来关闭Tomcat,这是安全隐患。生产环境建议把8005端口修改为其他自定义值,或者干脆用防火墙把这个端口屏蔽掉。
AJP端口8009,如果你没有在前面配Apache或者Nginx通过AJP协议转发请求,可以直接注释掉或者删掉这个Connector。减少开放端口就是减少攻击面,这个道理在安全上永远成立。
4.2 内存参数调整,不调迟早会遇到OutOfMemoryError
Tomcat默认的JVM内存参数偏保守,尤其是生产环境,并发一上来,很容易出现java.lang.OutOfMemoryError。这个错误大多数情况是堆内存不够,少数情况是Metaspace不够。
调整方式是在Tomcat的bin目录下新建一个setenv.sh文件。这个文件会被catalina.sh自动加载,专门用来设置环境变量,比直接改catalina.sh更优雅,升级Tomcat时不会把自定义配置覆盖掉。
vi /opt/tomcat/bin/setenv.sh内容示例:
JAVA_OPTS="-server -Xms512m -Xmx1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"这里解释一下参数含义:
-Xms:JVM初始堆内存大小-Xmx:JVM最大堆内存大小-XX:MetaspaceSize:元空间初始大小-XX:MaxMetaspaceSize:元空间最大大小
-Xms和-Xmx设置成相同值是个常见做法,因为启动时直接分配到位,避免运行期频繁扩容和收缩带来的性能损耗。对于一台4G内存的服务器,给Tomcat分配1G堆内存是比较合理的起步值;8G内存的服务器可以给到2G到4G,但别把所有内存都塞给JVM,操作系统本身和文件缓存也需要内存。
修改完setenv.sh后,需要重启Tomcat才能生效。验证是否生效,可以看看启动日志里的-Xms和-Xmx参数是否出现了你设置的值。
4.3 部署Java Web项目,war包丢进去就行
部署项目最常见的两种方式:直接复制war包到webapps目录,或者通过Manager管理界面热部署。
直接用命令行复制是最简单也最稳妥的方式:
cp my-webapp.war /opt/tomcat/webapps/然后Tomcat会自动检测到war包,自动解压、部署。等待几秒钟后,访问http://服务器IP:8080/my-webapp/就能打开项目首页。
这里有个坑要提醒:如果更新版本时,服务器上已经存在解压后的同名目录,Tomcat不会自动覆盖。正确做法是先把旧的war包和对应的解压目录删掉,再复制新的war包进去,否则可能出现旧代码混着新配置的诡异问题。
停掉Tomcat再部署是最安全的方式,可以避免文件锁和部署过程中的各种竞态问题。推荐流程是:
systemctl stop tomcat rm -rf /opt/tomcat/webapps/my-webapp* cp my-webapp.war /opt/tomcat/webapps/ systemctl start tomcat4.4 Manager管理界面的访问授权
Tomcat自带一个Manager管理界面,默认是禁止访问的,需要配置conf/tomcat-users.xml增加用户才能用。这个管理界面能让你通过浏览器上传war包、启停应用,很方便,但生产环境建议关掉或者限制访问IP,否则被扫描器扫到会有爆破风险。
编辑tomcat-users.xml,在<tomcat-users>标签内添加:
<role rolename="manager-gui"/> <user username="admin" password="你的密码" roles="manager-gui"/>保存后重启Tomcat,访问http://服务器IP:8080/manager/html,输入用户名密码就能进入管理界面。如果你需要上传war包,还需要manager-script角色。尽量用强密码,并且不要在生产环境的公网IP上开放这个路径,建议只允许内网访问。
5. 常见问题与排查技巧实录
5.1 启动报错:JAVA_HOME环境变量未定义
这是出现频率最高的问题,启动Tomcat时报这样的错:
Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is needed to run this program原因就是JAVA_HOME没设置或者设置不对。解决思路:
- 确认JDK确实装好了:
java -version - 确认
/etc/profile里的路径正确:echo $JAVA_HOME - 如果是手工解压JDK安装的,检查路径里是否有
bin/java这个文件 - 如果改了配置文件,记得
source /etc/profile或重新登录服务器
这个问题的本质是Tomcat的启动脚本找不到Java运行时。有些时候你手动敲java -version是有输出的,但Tomcat还是报错,这是因为systemd服务环境和你手动登录的shell环境不同,需要用Environment=JAVA_HOME=...在服务文件里显式指定。
5.2 端口被占用导致启动失败
启动Tomcat时日志里出现:
java.net.BindException: Address already in use: JVM_Bind <null>:8080这就意味着8080端口已经被别的进程占了。排查手段:
ss -tlnp | grep 8080看看是什么进程占用了端口,确认是否是你之前的Tomcat没关干净,还是其他服务占了端口。如果是旧Tomcat进程残留,杀掉即可:
ps -ef | grep tomcat kill -9 进程号如果端口确实需要给别的服务用,那就改Tomcat的8080为其他端口,改法前面已经讲过。
5.3 部署后访问项目出现404
war包放进去了,Tomcat也启动了,访问项目却404,这种问题通常有几个原因:
第一,访问路径不对。Tomcat部署war包后的默认访问路径是http://IP:8080/war包名/,如果你的war包叫myapp.war,访问的是http://IP:8080/myapp/,不是http://IP:8080/。
第二,项目没有成功解压部署。去webapps目录下看看是否存在对应的解压目录,如果war包没有解压,可能是Tomcat没检测到或者部署失败。查看logs/catalina.out里有没有异常信息。
第三,项目本身有问题。比如web.xml配置错误、缺少依赖jar包等,这些都会在日志里体现。排查的原则永远是先看日志,日志会告诉你90%的真相。
5.4 内存溢出OutOfMemoryError
内存溢出的报错在日志里长这样:
java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: MetaspaceJava heap space是堆内存不够,Metaspace是元空间不够。解决的思路就是调整JVM参数,就是之前说的setenv.sh里的-Xmx和-XX:MaxMetaspaceSize。
但这里有个务实的提醒:内存调大只是缓解症状,如果项目本身有内存泄漏,内存再大也只是推迟崩溃时间。调优之后如果还频繁溢出,就得认真查代码了,用jmap、jstat这些Java自带的工具去分析堆内存使用情况,找到疑似泄漏的对象。
5.5 防火墙把8080端口挡了
很多人在服务器上装好了Tomcat,本地访问没问题,但外网就是打不开。这个问题在云服务器和本地虚拟机上都常见,原因一般是防火墙拦截。
CentOS系默认防火墙是firewalld,查看和开放端口的方法:
firewall-cmd --permanent --add-port=8080/tcp firewall-cmd --reload如果用的是Ubuntu的ufw:
ufw allow 8080/tcp同时要注意,云服务器的话,安全组规则也要放行端口。这个属于“锅在防火墙,不是Tomcat”的典型问题,排查的时候先把本地curl http://localhost:8080跑一下,本地通、外部不通,基本就是防火墙或者安全组的锅。
最后再分享一个经验习惯:装Tomcat最容易出问题的阶段其实不是安装本身,而是第一次启动后没有养成看日志的习惯。我现在不管在什么环境部署,启动完成第一步永远是tail -f /opt/tomcat/logs/catalina.out,观察几行启动日志,确认没有异常才继续下一步。你把这个习惯养成了,后面绝大部分排障工作都会顺很多。不同的Linux发行版、不同的JDK版本、不同的Tomcat版本组合起来,细节上可能会有差异,但排查思路是通用的:先看日志、再看端口、最后看配置,按这个顺序排查,基本没有解决不了的问题。