以前帮人排查Tomcat启动问题,聊到最后发现大多数麻烦在下载环节就已经埋下了。要么是版本跟JDK搭不上,要么下载的包不完整解压失败,更有甚者把tar.gz和zip搞混,解压出来一堆乱目录。Linux服务器上部署Java Web应用,Tomcat几乎是个绕不开的基础组件,“下载Tomcat”这个动作看着简单,实际做的时候却有不少细节值得讲清楚。这篇文章我会把Linux下下载Tomcat的完整流程走一遍,从版本选型、下载源选择,到校验、解压、启动验证,最后再列几个真实会踩的坑。适合刚接触Linux运维、或者第一次在服务器上搭Java环境的朋友,老手也可以重点看看校验和systemd管理那两段,有些操作平时容易忽略。
1. 下载前必须想清楚的三件事
1.1 你手里的Java环境是什么版本
Tomcat本质上是Java写的Servlet容器,它对JDK版本有硬性要求,这不是“建议”而是“门槛”。比如Tomcat 10.1要求JDK 11以上才能跑,Tomcat 9要求JDK 8以上,Tomcat 8.5则要求JDK 7以上。如果你的服务器上JDK版本太低,启动脚本执行的时候会直接给出不匹配的提示,进程起不来。
所以下载Tomcat之前,第一件事不是打开官网,而是先在Linux终端里敲一下:
java -version我遇到过不少朋友,Tomcat倒是下好了,解压了,配置文件也改了,最后卡在UnsupportedClassVersionError这类报错上,回头一查,JDK还是1.6的。如果服务器上还没有Java,那先把JDK装好、把JAVA_HOME配好,再回来看Tomcat的下载问题。这里我把常见版本搭配整理成了表格,下载前对着选就不会乱:
| Tomcat版本 | 最低JDK要求 | 生产环境建议 |
|---|---|---|
| 8.5.x | JDK 7+ | JDK 8 |
| 9.0.x | JDK 8+ | JDK 8 / 11 |
| 10.0.x | JDK 11+ | JDK 11 |
| 10.1.x | JDK 11+ | JDK 11 / 17 |
这里有一个特别容易忽略的坑:Tomcat 10开始,Servlet API的包名从javax.*换成了jakarta.*,如果你的老项目代码里到处是javax.servlet.http.HttpServlet,直接搬到Tomcat 10上会有一堆类找不到。判断方法很简单,看项目的依赖或者war包里有没有javax.servlet字样,有的话就老老实实继续用Tomcat 9。
1.2 Tomcat大版本怎么挑
版本选择上,我的原则是“生产环境别追新,也别太守旧”。
- 8.5.x:属于维护期版本,适合还在用JDK 8跑老项目的环境,能稳定运行但也别指望新特性。
- 9.0.x:目前兼容性最广的版本之一,JDK 8到17都能搭配,很多老war包直接扔进去就能跑,是“省心”路线。
- 10.0.x:过渡版本,存在的意义不大,没必要选。
- 10.1.x:新项目比较推荐,配合JDK 11或17,Servlet规范是当前最新的,长期支持也更好。
有一种情况不需要额外下载Tomcat:你用的是Spring Boot内嵌容器。Spirng Boot 2.x默认内嵌Tomcat 9,Spring Boot 3.x默认内嵌Tomcat 10.1,这些由框架自身管理,不需要自己下载部署。反过来,如果你是要把war包部署到独立Tomcat里,那就按项目要求的Servlet规范来选。
1.3 下载源选哪个
Tomcat的官方下载页是最权威的,但有一个实际问题:官方CDN节点在境外,国内服务器直接拉取时速度经常感人。我个人在实际部署中会这样分情况:
- 服务器在国外或者网络情况很好,直接用官方CDN链接,省心且版本最新。
- 服务器在国内,优先考虑国内镜像站,速度能快一个量级。
- 本地有浏览器环境的,也可以先在本地下载,再通过scp或rz上传到服务器。
另外要注意,Tomcat官方会把最新版本放在动态CDN上,历史版本放在archive目录里。所以你在网上查到某个教程里的版本号,打开官网却发现已经更新到更新版本时,不用慌,按自己的需求选版本就好,不一定非要用最新的。
2. 三种下载方式,按场景选一种就行
2.1 本地浏览器下载再传服务器:适合不熟悉命令行的朋友
如果你的服务器上没装什么下载工具,或者你对命令行还不算熟,最稳的办法就是本地浏览器下载,然后传上去。
操作流程大概是:
- 浏览器打开Tomcat官方下载页,找到对应大版本入口。
- 在Binary Distributions区找到
tar.gz结尾的包。注意不是zip,zip是Windows用的,Linux下解压tar.gz最省事。 - 下载完成后用scp传到服务器临时目录:
scp apache-tomcat-9.0.80.tar.gz user@服务器地址:/tmp/或者用rz命令也可以,但前提是服务器上装了lrzsz。这个方式的优点是能看到文件大小、下载完整度,缺点是多了一步传输动作。
这里提醒一句:Windows本地下载的文件,有时候会被浏览器重命名,或者下载工具自动加一串后缀。传到Linux后会遇到“文件是下来了,但文件名不对,解压命令照着敲就是报错”的情况。上传前先ls -lh看一眼再动手。
2.2 服务器上wget直连:最常用也最推荐
在已经能SSH登录服务器的情况下,直接在服务器上用wget拉取是最快的。先找到官方下载页里对应版本的tar.gz直链,然后执行:
cd /tmp wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.80/bin/apache-tomcat-9.0.80.tar.gz这里要提一下,官方CDN的链接结构是/tomcat/tomcat-<大版本>/v<具体版本>/bin/...,所以如果你想追某个小版本,直接改URL里的版本号就行。不过要注意,dlcdn这个地址是动态CDN,只保留当前推荐版本,历史版本需要去archive目录下找。
下载完成后第一件事不是解压,而是看文件大小:
ls -lh apache-tomcat-9.0.80.tar.gz正常情况这个包应该有10MB以上(具体版本不同有差异)。如果只有几百KB甚至几KB,那说明你拉下来的很可能是一个404页面或者重定向说明,文件名虽然一样,但内容不是正经压缩包。遇到这种情况就要删掉重来。
2.3 镜像站下载:网络差、官方链接慢时的救急方案
如果你在国内服务器上直接拉官方CDN,速度能慢到让你怀疑人生。这时候换成国内镜像站是更务实的做法。镜像站的目录结构和官方基本一致,找到apache/tomcat对应的版本目录,再进bin目录拿tar.gz直链:
wget https://mirrors.example.com/apache/tomcat/tomcat-9/v9.0.80/bin/apache-tomcat-9.0.80.tar.gz上面链接里的mirrors.example.com是占位写法,实际使用时换成你所在地区能访问的镜像域名即可。使用镜像站有一个小缺点:版本同步存在延迟,官方刚发的某个小版本镜像站可能还没有。遇到这种情况,要么等镜像站同步,要么用官方archive里提供的旧版本曲线救国。
还有一个需要注意的点:镜像站偶发少文件或者文件不完整的情况,所以不管从哪下载,拿到手之后都建议做一次基于sha512的完整性校验,具体操作下一章讲。
2.4 小技巧:wget断点续传与后台下载
下载Tomcat包虽然不算特别大,但网络不稳定的时候也容易中断。wget有两个参数在实际下载中非常实用。
一个是断点续传:
wget -c https://.../apache-tomcat-9.0.80.tar.gz-c能让wget从上次中断的位置继续下载,不需要重新拉一遍。另一个是后台下载:
wget -b https://.../apache-tomcat-9.0.80.tar.gz加了-b之后,wget会立即返回,下载在后台执行,日志写到当前目录下一个叫wget-log的文件里。你可以用tail -f wget-log实时看下载进度。在大文件比较多或者网络慢的环境下,这两个参数能帮你省下不少等待时间。
3. 下载完别急着解压,先校验再部署
3.1 SHA512校验:这一步能省很多事
很多人下载完直接解压,等解压报错“gzip: stdin: not in gzip format”才回头想是不是文件有问题。其实官网上在tar.gz旁边就放了对应的sha512校验文件,解压前花十几秒做一次校验,能挡掉绝大多数文件损坏问题。
把tar.gz和对应的.sha512文件放在同一个目录下,然后执行:
sha512sum -c apache-tomcat-9.0.80.tar.gz.sha512如果输出OK,说明文件完整性没问题。如果你没有下载.sha512文件,也可以直接把官方页面显示的哈希值和本地计算结果比对:
sha512sum apache-tomcat-9.0.80.tar.gz输出的是一串128个十六进制字符的哈希值,和官网显示的一致才说明文件没问题。这里多嘴一句,校验环节在你手动下载文件、尤其是从镜像站下载时更重要,因为镜像站偶尔会同步出问题,导致文件不完整。我之前就遇到过一次,镜像站上看起来一切正常,下载完解压才发现少文件,最后追查下来是镜像同步不完整。所以校验这一步值得养成习惯。
3.2 规划目录结构与软链
解压前先想清楚目录规划。Tomcat通常安装在/opt或者/usr/local下,我个人习惯放在/opt/tomcat,然后用一个软链指向当前使用的版本。
mkdir -p /opt/tomcat tar -xzf apache-tomcat-9.0.80.tar.gz -C /opt/tomcat ln -s /opt/tomcat/apache-tomcat-9.0.80 /opt/tomcat/current解压完成后,可以看到Tomcat自带的几个核心目录:
bin:启动、关闭脚本,以及供Windows使用的bat文件。conf:核心配置文件,比如server.xml、web.xml。lib:Tomcat运行依赖的jar包。logs:运行日志默认输出位置。webapps:放置部署war包或解压后的Web应用。work:JSP编译后的临时文件。
软链的好处在于升级。以后你下载了新版本的Tomcat,只需要解压到/opt/tomcat下,然后把current软链指向新目录,环境变量和systemd配置都不用动。这个习惯在维护多版本或者经常升级的环境里非常省心。
3.3 配置JAVA_HOME与CATALINA_HOME
Tomcat启动脚本会去找两个环境变量:JAVA_HOME和CATALINA_HOME。如果你JDK已经装好,JAVA_HOME应该已经配过了。可以用下面的命令确认:
echo $JAVA_HOME如果输出为空,需要配置一下。编辑/etc/profile,在文件末尾追加:
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk export CATALINA_HOME=/opt/tomcat/current export PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin配置完成后执行:
source /etc/profile这里有个细节,source只对当前会话生效,新开的SSH窗口会自动加载/etc/profile,但如果你是用某个受限用户启动Tomcat,可能需要重新登录一次才能读到新环境变量。所以启动Tomcat前,先在那个用户的shell里执行echo $CATALINA_HOME确认能输出路径,这一步检查能避免很多“脚本看起来没问题但就是起不来”的困惑。
3.4 授权与启动
生产环境不建议用root用户直接跑Tomcat,原因很简单:Tomcat作为Web容器,如果被攻破,root权限意味着很大风险。创建一个低权限专用用户是更稳的做法。
useradd -r -s /sbin/nologin tomcat chown -R tomcat:tomcat /opt/tomcat然后通过sudo切换到tomcat用户执行启动:
sudo -u tomcat /opt/tomcat/current/bin/startup.sh启动脚本会输出Tomcat started字样,但这句话不代表一定成功。很多启动失败会发生在JVM初始化阶段,出错信息不会显示在终端,而是写入logs/catalina.out。如果启动后进程没起来,第一时间去看这个文件。
4. 启动验证与开机自启
4.1 验证Tomcat是否正常起来
Tomcat启动后,建议按下面的顺序验证一遍,而不是看了终端输出就认为万事大吉。
先看进程是否存在:
ps -ef | grep tomcat再看端口是否在监听:
curl -I http://localhost:8080如果返回HTTP/1.1 200表示正常。如果返回404也算正常,因为默认根路径下有没有部署的应用,Tomcat本身的欢迎页和应用路径是分开的。最怕的是connection refused,那说明进程可能挂了或者端口没监听。这时候去看日志:
tail -n 100 /opt/tomcat/current/logs/catalina.out日志里会有明确的启动成功或失败原因。我个人习惯是把这行命令记在笔记里,因为在排查Tomcat问题时,catalina.out基本是第一个需要打开的文件。
4.2 防火墙、安全组与端口修改
本机curl通了,但外部浏览器打不开,这通常不是Tomcat的问题,而是防火墙或云服务器安全组没放行端口。
如果你的系统用了firewalld,执行:
firewall-cmd --permanent --add-port=8080/tcp firewall-cmd --reload如果服务器是云主机,还得去控制台的安全组里放行8080端口。这一步很容易被遗漏——本机怎么测都是通的,外部一访问就卡住。
如果你想换掉默认的8080端口,修改conf/server.xml里的一行:
<Connector port="8080" protocol="HTTP/1.1" ... />把port="8080"改成你需要的端口。改之前先确认新端口没有被其他服务占掉:
ss -tlnp | grep 8081改完端口要重启Tomcat才会生效。这里补充一个点:Tomcat不只监听一个端口,除了HTTP连接器,还有AJP连接器(默认8009),以及shutdown端口(默认8005)。生产环境如果不需要AJP协议,建议直接注释掉,减少攻击面。
4.3 systemd管理Tomcat进程
用startup.sh和shutdown.sh管理Tomcat进程虽然简单,但不够“正规”。进程生命周期不受systemd管控,开机自启、意外退出自动拉起这些能力都没有。更推荐的方案是写一个systemd service文件。
新建/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_HOME=/opt/tomcat/current Environment=CATALINA_BASE=/opt/tomcat/current ExecStart=/opt/tomcat/current/bin/startup.sh ExecStop=/opt/tomcat/current/bin/shutdown.sh Restart=on-failure [Install] WantedBy=multi-user.target然后执行:
systemctl daemon-reload systemctl enable --now tomcat这样Tomcat会随系统开机自启,挂了之后如果条件满足也会自动重启。注意这里Type=forking很关键,因为startup.sh会fork出子进程后立即返回,systemd要靠这个类型判断启动是否完成。另外,JAVA_HOME、CATALINA_HOME要在service文件里显式声明,不要依赖shell环境变量,因为systemd启动的进程环境很干净,之前遇到过环境变量没传进去导致启动失败的情况。
5. 真实踩过的坑:下载与部署的常见问题速查
5.1 下载很慢或者直接超时
这是在国内服务器上下载Tomcat时最常见的问题。wget走官方CDN,速度可能只有几十KB,甚至到最后直接超时。解决思路按优先级排列:
- 换国内镜像站,速度通常能提升数倍;
- 用
wget -c断点续传,避免从头再来; - 本地浏览器下载,然后scp传上去。
我自己的经验是,如果你所在服务器访问官方源持续很慢,干脆一开始就用镜像站,别在官方CDN上浪费时间。下载这类固定版本的开源软件,速度比“权威性”重要,反正只要做了sha512校验,来自镜像站的文件一样可信。
5.2 下载了个“假文件”
现象是文件下载完,ls -lh一看只有几百KB,解压时直接报gzip: stdin: not in gzip format。
原因很简单:wget在下载过程中遇到了重定向或者404,但文件名被保留了下来,实际写入的是HTML页面内容。尤其是官网CDN偶尔会做链接跳转,wget不加-L参数时可能拿到的不是最终文件。
对策:删掉这个假文件,用curl -L代替:
curl -LO https://.../apache-tomcat-9.0.80.tar.gz大写-L是跟随重定向,-O是保存为远程文件名。下载完成后顺手看一眼文件大小,再走一遍sha512校验,基本能避开这个坑。
5.3 启动提示Neither the JAVA_HOME nor the CATALINA_HOME environment variable has been defined
这是Tomcat启动脚本在找不到环境变量时给出的经典提示。原因一般有三种:
- 服务器上压根没装JDK,或者装了但没配置JAVA_HOME;
- 你在一个shell里source了环境变量,但切换到另一个用户(比如sudo su到tomcat用户)后环境变量不继承;
- 用systemd启动但service文件里没有声明Environment。
对策也分场景:如果是手动命令行启动,确认tomcat用户能读到环境变量;如果用systemd,直接在tomcat.service里写好Environment=项,不要依赖全局环境变量。另外,也可以把JAVA_HOME直接写进catalina.sh脚本开头,一劳永逸,但这样升级版本时容易漏改,我还是更倾向于在systemd配置里写清楚。
5.4 8080端口被占用
Tomcat启动时如果8080已经被别的进程占用,会出现一个很迷惑的现象:startup.sh执行时没抛异常,进程列表里看似有Tomcat进程,但访问端口就是不通。
实际情况是Tomcat的启动脚本fork出了一个新进程,但这个进程在初始化时发现端口冲突,很快就退出了,只在catalina.out里留了句Address already in use。
排查命令:
ss -tlnp | grep 8080 lsof -i :8080对策有两种:换Tomcat的HTTP端口,或者处理占用端口的进程。处理占用进程时要注意,生产环境先确认那个进程是不是别的业务的,别手起刀落误杀。
5.5 javax和jakarta包名不兼容
把老项目的war包丢进Tomcat 10.1,启动日志里出现一堆ClassNotFoundException,大部分是javax.servlet.*路径下的类。这是Tomcat 10大版本升级导致的兼容性变化,不是配置问题。
判断和处理方式:
| 现象 | 处理方式 |
|---|---|
| war包依赖javax.servlet | 继续用Tomcat 9.0.x |
| war包依赖jakarta.servlet | 用Tomcat 10.1.x |
| 不知道war包用哪种 | 解压看WEB-INF/lib下的jar包名,或者直接看代码 import |
如果项目是从Tomcat 9迁移到10,代码层面需要把javax.servlet.*替换成jakarta.servlet.*,同时Maven或Gradle里的依赖坐标也要同步改。这不是简单改一行配置就能过的,所以我对老项目的建议一直是:别盲目升级大版本。
最后说个我自己的习惯:每台服务器的/opt/tomcat目录下,我都会放一个versions.txt,记录当前使用的Tomcat版本、对应JDK路径、下载来源、sha512校验值。下次升级或排查问题时,照着记录核对环境,能省掉很多重复摸索的时间。下载这个环节看着基础,但版本选错、文件损坏带来的连锁问题,往往比安装本身更耗时间。如果你第一次部署时也在这些地方卡过壳,照上面这些步骤和排查思路走一遍,应该能顺畅不少。