Linux系统安装部署Tomcat这事儿光看标题会觉得很简单不就是解压、启动、完事吗但真正在服务器上操作过的人都知道最磨人的往往不是Tomcat本身而是JDK版本、运行用户、端口占用、目录权限这些边角料。我最近在一台新装的Linux服务器上重新把整套流程走了一遍从环境准备到配置调优再到问题排查踩了不少坑决定按实际操作的顺序把这篇整理出来。内容既适合刚接触Linux部署的新手也适合想把手动部署改成规范流程的运维同学参考。1. 部署前想清楚三件事版本、目录和权限1.1 Tomcat到底是什么为什么值得单独讲Tomcat是一个Java Servlet和JSP的运行容器说人话就是替你跑Java Web应用的一个进程。很多业务系统把代码打成war包丢进Tomcat的webapps目录访问对应的端口应用就能对外提供服务。相比Nginx、Apache这类静态Web服务器Tomcat更贴近Java应用本身它负责管理Servlet生命周期、把JSP转成Java再编译成Class、维护线程池还自带管理后台。既然它这么“自包含”为什么部署还要单独写一篇因为Tomcat并不是装完就能用得舒服。它依赖JDK依赖系统权限依赖端口策略依赖你对运行参数的把控。任何一个环节不匹配启动过程可能看似正常业务却始终访问不通。部署Tomcat本质上就是把Java运行环境、系统服务和Web容器这三层串成一个闭环每一层都要搞清楚。很多新人最容易犯的错是把Tomcat当成普通软件一样“装好就行”结果要么端口被占用要么JSP页面编译报错要么随便用root启动后留下一堆危险配置。其实Tomcat的部署经验完全可以标准化只要把版本、目录、权限这三件事想清楚后面就是体力活。1.2 版本选型JDK和Tomcat别乱搭版本选型是整套部署里最容易被忽视、又最容易翻车的一步。Tomcat本身不提供Java运行时它必须跑在一个JDK之上。JDK和Tomcat的版本不是随意组合的官方每个版本都写明了最低Java版本要求。常见组合大概是这样的Tomcat版本常用JDK版本注意事项Tomcat 8.5JDK 8老项目经典组合兼容性好Tomcat 9JDK 8 / JDK 11目前最稳的组合使用面广Tomcat 10.1JDK 11 / JDK 17默认API从javax切换为jakarta命名空间Tomcat 11JDK 17较新的功能版本适合新项目如果公司里有一套历史很长的老系统代码里全是javax.servlet这类旧依赖就别轻易把运行环境升到Tomcat 10以上。因为Tomcat 10开始把Java EE的javax包切换到Jakarta EE的jakarta包同一个war包换过去之后很多类直接找不到编译报错一大片。这跟Java版本高低没关系纯粹是API命名空间变了。反过来新项目也不建议继续守着Tomcat 8和JDK 8组合除非有硬性兼容要求。新版本对HTTP/2、TLS、性能提升等都有不少改进长期维护也更有保障。我的建议是部署前先看官方文档的Support Matrix别只看“安装时能启动”就以为没问题。1.3 目录规划和运行用户安装位置是第一个要考虑的问题。有人图省事直接解压到root目录下比如/root/apache-tomcat-9.0.x这种习惯在测试环境无所谓生产环境最好别这么干。推荐把Tomcat放到/opt或/usr/local这类约定俗成的软件目录里方便管理也方便备份。另外我强烈建议用专用用户运行Tomcat不要用root。原因很简单Tomcat本身要执行Java文件Web应用又是外部传入的代码一旦应用有安全漏洞进程是什么用户攻击者就能在服务器上拿到什么用户的权限。用低权限用户运行能大幅缩小被入侵后的影响范围。我通常这样规划安装目录/opt/tomcat运行用户tomcat日志目录/opt/tomcat/logs统一软链或保留默认数据目录/data/appname与Tomcat目录尽量分开这样做的目的是把程序、日志、数据隔离。如果哪天磁盘满了你可以直接清日志目录不用在Tomcat安装目录里到处找地方。2. JDK装不对后面全白费2.1 先确认系统架构和已有Java环境动手装JDK之前先花一分钟确认系统信息和环境中是否已经有Java。命令如下cat /etc/os-release uname -m java -version这里有几个关键点。第一操作系统的架构决定了你下载JDK包的格式x86_64和aarch64对应的包完全不同下错了安装的时候直接提示cannot execute binary file。第二如果机器上已经装了Java一定要看清楚版本号和来源。有时候系统通过包管理器自带了一个旧版OpenJDK你后面手动装新版时没改好环境变量结果Tomcat启动时仍然加载了老版本就会出现各种莫名其妙的问题。遇到这种情况我习惯把系统中自带的Java彻底卸载或者完全靠环境变量隔离不让系统默认的Java路径干扰Tomcat。2.2 JDK安装方式对比包管理器还是手动解压Linux上装JDK无非两种主流方式用发行版包管理器装或者下载二进制包手动解压配置。包管理器方式命令很简短比如基于Debian的系统用apt安装openjdk。这种方式的优点是安装简单、系统自动管理依赖但缺点是版本往往偏旧而且你可能没法精确控制JDK的安装路径。Tomcat本身不挑JDK安装在哪只要JAVA_HOME指对就行所以包管理器方式对很多场景是够用的。手动解压方式更可控。下载某个固定版本的JDK解压到/usr/local/jdk下自己设置环境变量。这样做的好处是多套Java环境可以并存你切换项目时只需要改JAVA_HOME。我生产环境里更倾向于这种方式因为部署脚本和运维标准容易统一。手动解压JDK的过程其实很简单mkdir -p /usr/local/java tar -zxvf jdk-17_linux-x64_bin.tar.gz -C /usr/local/java mv /usr/local/java/jdk-17.0.x /usr/local/java/jdk17解压完JDK后紧接着配置环境变量。这里我多说一句不要直接改大范围的/etc/profile最好在/etc/profile.d目录下单独建一个java.sh文件。这样系统启动时会自动加载运维自查时也一目了然。cat /etc/profile.d/java.sh EOF export JAVA_HOME/usr/local/java/jdk17 export PATH$JAVA_HOME/bin:$PATH EOF source /etc/profile.d/java.sh配置完后执行java -version确认。如果输出里带openjdk字样并且版本号符合预期这一层就算过了。2.3 一个容易被忽略的坑Tomcat需要的是JDK而不是JRE很多资料会告诉你要装JRE因为Tomcat“运行”Java程序只需要JRE。这句话只对了一半。Tomcat在启动时会动态编译JSP文件而JSP编译需要javac也就是Java编译器。javac是JDK里才有的工具只装JRE往往会导致JSP页面首次访问时报错错误信息里经常会看到“Unable to compile class for JSP”之类的字眼。所以部署Tomcat的服务器上我从来都是直接装完整JDK不装精简JRE。哪怕你暂时觉得项目里没有JSP也保不齐将来会用到。为省那几十MB磁盘空间去埋一个后期排查成本很高的坑实在不划算。3. Tomcat本身的安装和启动解压到跑通3.1 下载安装包与校验Tomcat安装包体积不大几十MB下载很快。到官方下载页选择对应版本的tar.gz包即可。下载时我建议顺手把同目录下的sha512校验文件一起拿下来下载完校验一下避免拿到损坏或不完整的包。wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.x/bin/apache-tomcat-9.0.x.tar.gz sha512sum apache-tomcat-9.0.x.tar.gz校验码匹配后解压到指定目录mkdir -p /opt/tomcat tar -zxvf apache-tomcat-9.0.x.tar.gz -C /opt/tomcat这里建议保留Tomcat自身的版本目录然后用软链接指到标准路径比如ln -s /opt/tomcat/apache-tomcat-9.0.x /opt/tomcat/tomcat升级的时候只需要切换软链接回滚也方便比直接改目录名优雅很多。3.2 创建运行用户并调整目录权限Tomcat不能用root启动。创建专用用户这一步不要省useradd -r -s /bin/false tomcat如果用/sbin/nologin手动切到tomcat用户启动时没法登录shell虽然systemd方式也能跑但手动调试起来不方便。用/bin/false会更严格但如果你希望保留手动启动的能力可以给tomcat用户指定/bin/bash然后禁止root登录。实际生产环境里我见过两种做法都有重点是不用root。创建好用户后把Tomcat目录的属主改成tomcatchown -R tomcat:tomcat /opt/tomcat不要嫌麻烦。加上这一步之后Tomcat自身才能正常写日志、解压war包。如果忘了启动时会报Permission denied或者你手动切用户后启动半天都起不来。3.3 第一次启动和验证首次启动前先设置CATALINA_HOME和CATALINA_BASE。很多教程喜欢把这两个变量写进系统环境变量但我个人觉得没必要直接把Tomcat脚本里的默认行为跑通反而更干净。手动启动的命令是/opt/tomcat/tomcat/bin/startup.sh启动后立刻验证三件事ps -ef | grep tomcat ss -tlnp | grep 8080 tail -f /opt/tomcat/tomcat/logs/catalina.out看到Java进程存在、8080端口有监听、日志里出现“Server startup in [xxx] milliseconds”说明启动成功。然后在本机做一次HTTP探测curl -I http://127.0.0.1:8080如果返回HTTP/1.1 200Tomcat首页已经能访问了。3.4 启动失败的经典场景环境变量没生效手动启动时最经典的一个报错是Cannot find /opt/tomcat/bin/setclasspath.sh This file is needed to run this program这个错通常是JAVA_HOME没找到导致的。setclasspath.sh需要依据JAVA_HOME去定位Java相关脚本如果JAVA_HOME为空Tomcat脚本就会找不到setclasspath.sh。排查方式很简单echo $JAVA_HOME如果输出为空说明环境变量没有来源到当前会话。要么把export语句加到/etc/profile.d/java.sh要么在当前终端执行source一下。很多时候不是Tomcat的问题而是你新开的SSH会话没有重新加载系统环境变量导致的。4. 让它跑得更稳端口、JVM与自启动4.1 server.xml里的关键调整跑通之后至少要看一眼conf目录下的server.xml这里决定了Tomcat对外暴露的端口和连接器行为。默认的HTTP连接器长这样Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /生产环境建议根据实际需求改几个地方多网卡机器上指定监听地址例如address192.168.10.10避免Tomcat对全部网络接口开放。合理设置maxThreads、minSpareThreads、acceptCount。maxThreads是处理请求的最大线程数默认200如果机器配置高、应用QPS大可以提到400或500minSpareThreads保留空闲线程减少频繁创建线程的开销acceptCount是等待队列长度。如果只用HTTP可以注释掉AJP连接器。AJP是Tomcat专门和Apache httpd通信的协议现在很多部署直接用Nginx做反向代理AJP基本用不上了留着反而增加攻击面。还有一个很多人不知道的安全细节server.xml里默认有一个关闭Tomcat的shutdown端口端口是8005。这个端口如果暴露在外网理论上任何人往这个端口发一条指定字符串就能把Tomcat关闭掉。虽然现代版本对这个操作做了限制但稳妥起见可以把port设成-1直接禁用该端口Server port-1 shutdownSHUTDOWN4.2 JVM参数不能乱写用setenv.sh而不是直接改catalina.shTomcat运行时的JVM参数比如堆内存大小、垃圾回收策略、OOM时是否生成dump文件都是在启动时传给Java进程的。新手最喜欢直接改catalina.sh在那些长长的JAVA_OPTS赋值语句里手动加参数实际上这样并不优雅升级Tomcat时一覆盖就白改了。Tomcat提供了专门的扩展脚本bin/setenv.sh。只要这个文件存在catalina.sh启动时会自动读取它里面配置的JAVA_OPTS和CATALINA_OPTS。所以正确的做法是新建一个setenv.shcat /opt/tomcat/tomcat/bin/setenv.sh EOF CATALINA_OPTS-Xms512m -Xmx512m -XX:MaxMetaspaceSize256m -XX:UseG1GC -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/opt/tomcat/logs EOF chmod x /opt/tomcat/tomcat/bin/setenv.sh注意这里我推荐用CATALINA_OPTS而不是JAVA_OPTS。区别在于JAVA_OPTS不仅会传给Tomcat启动时的JVM还会传给关闭时执行的JVM。如果你把堆内存参数写在JAVA_OPTS里Tomcat执行shutdown.sh时也会带着这些参数去启动一个临时JVM虽然大多数情况下没事但有些老环境会因此出现关不掉或者关得慢的情况。CATALINA_OPTS只影响Tomcat本体运行更干净。-Xms和-Xmx建议设置成一样大避免JVM堆内存动态伸缩。动态伸缩在并发波动大的场景下会带来无谓的性能损耗还可能引起延迟抖动。至于给多大要看机器物理内存和应用本身。我见过很多部署一上来就给2G结果机器只有1G内存系统直接开始swap应用卡成幻灯片。先看free -h再看需求别拍脑袋。4.3 用systemd托管Tomcat实现开机自启手动startup.sh用着还行但服务器重启后Tomcat不会自动拉起还要人登上去执行命令这不是生产环境该有的状态。Linux的主流发行版如今都用systemd托管Tomcat很简单。新建服务文件/etc/systemd/system/tomcat.service内容可以参考[Unit] DescriptionTomcat Server Afternetwork.target [Service] Typesimple Usertomcat Grouptomcat EnvironmentJAVA_HOME/usr/local/java/jdk17 EnvironmentCATALINA_HOME/opt/tomcat/tomcat EnvironmentCATALINA_BASE/opt/tomcat/tomcat ExecStart/opt/tomcat/tomcat/bin/catalina.sh run ExecStop/opt/tomcat/tomcat/bin/catalina.sh stop Restarton-failure RestartSec5 [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable --now tomcat systemctl status tomcat这里用的是catalina.sh run它让Tomcat在前台运行由systemd统一管理进程生命周期。比用startup.sh更靠谱startup.sh会另起子进程systemd要额外追踪PID文件容易出幺蛾子。配置好之后日常启停就用systemctl start tomcat systemctl stop tomcat systemctl restart tomcat journalctl -u tomcat -f日志都进journald排查问题也方便。5. 踩坑实录端口冲突、防火墙、OOM和日志定位5.1 端口被占用时Tomcat会怎么“表演”Tomcat启动日志里如果出现这样一段SEVERE [main] org.apache.catalina.util.LifecycleBase.handleSubClassException Port 8080 was already in use.99%是端口被别的进程占了。排查命令是ss -tlnp | grep 8080看到占用进程后根据实际情况决定是停掉对方还是改Tomcat端口。这里我特别提醒一个细节改了HTTP连接器端口后还有一个redirectPort也很容易漏掉。server.xml里HTTP连接器默认写着redirectPort8443这表示当请求是HTTPS时Tomcat会把这个端口当作跳转目标。如果不一致性改到8443后面接HTTPS的时候会跳到一个奇怪的端口上。另外修改任何端口后先systemctl restart tomcat再验证。有时候你以为改生效了结果Tomcat还没重启自然还是旧端口。5.2 访问不通的排查顺序Tomcat本机curl 200但浏览器访问不到这属于最常见的部署后问题。我的排查顺序固定为四步第一步确认Tomcat监听地址是不是只绑定了内网IP。如果server.xml里配了address127.0.0.1那外网无论如何也访问不到Nginx都没法转发。第二步看服务器防火墙有没有放行端口。基于Firewalld的系统执行firewall-cmd --list-all firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload如果用iptables就检查对应INPUT链的规则。第三步检查云平台的安全组规则。很多服务器虽然在系统层面放行了端口但云平台安全组没放行流量根本到不了系统网卡。这类问题通过本机curl无法发现因为curl是绕过了外部网络的。第四步看Nginx或负载均衡配置。如果前端还有一层代理代理没有把请求转发到Tomcat对外地址也会表现为访问不通。很多人一上来就喜欢怀疑Tomcat实际上Tomcat经常是背锅的那个。5.3 内存溢出和OOM的处理Java应用跑着跑着突然挂了日志末尾出现out of memory大概率是堆内存设置不合理。有OOM问题时第一件事不是急着加内存而是看当前内存占用到底从哪里消耗。用jcmd、jstat、jmap这些JDK自带工具去分析或者提前在setenv.sh里打开JDK的堆转储开关-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/opt/tomcat/logs一旦发生OOMJVM会生成一个堆dump文件之后用内存分析工具打开就能看到到底是哪个对象占满了堆。盲调-Xmx只会让你反复试错。另外Tomcat部署多个应用时要特别留意PermGen/Metaspace不足的问题。JDK8之后永久代被Metaspace取代默认值一般够用但如果你在一个Tomcat里塞了太多war包Metaspace被撑爆的情况依然会出现。设置-XX:MaxMetaspaceSize是一个稳妥的做法给个256m或512m视应用数量而定。5.4 日志怎么看分清catalina.out和localhost.logTomcat日志目录下塞了很多文件新手经常会看晕。我简单梳理一下catalina.out标准输出和异常堆栈最常看的日志。localhost.YYYY-MM-DD.logWeb应用自身类加载和初始化日志有些启动时异常只看这个文件。manager.YYYY-MM-DD.log管理后台相关日志。host-manager.YYYY-MM-DD.log虚拟主机管理日志。部署应用后发现404优先看localhost日志进程崩溃优先看catalina.out。这两个文件不要搞混否则排查方向会走偏。还有一个经验Tomcat默认日志没有按大小滚动长时间运行catalina.out会变得很大。建议配合logrotate定时切割或者让Tomcat使用自带日志处理。不然某天磁盘被怼满你连删日志都要先腾空间非常尴尬。5.5 其他值得注意的小坑部署war包时确认war文件权限可被tomcat用户读取。看过太多人用root上传war后tomcat用户解压时权限不够结果webapps里多了一个解压到一半的目录访问起来永远是404。另外不要在Tomcat运行状态下手动去解压覆盖正在运行的war包。Tomcat不是不能热部署但生产环境最好选低峰期操作并且通过Manager应用或固定流程来做避免文件锁冲突。最后如果你长期不用Tomcat的默认管理页面建议把webapps下的docs、examples、manager、host-manager这几个目录都删掉。它们没有任何业务价值纯粹是攻击者试手的靶子。删完再重启一次Tomcat项目目录清爽很多。6. 我部署完Tomcat之后固定要做的事整套流程走完我还会再花几分钟做几件收尾的事。第一把conf下的server.xml和catalina.properties备份一份到独立位置。Tomcat升级时如果需要对比差异有个基准文件比对着改能省很多事。第二把Tomcat的状态页限制在内网访问。server.xml里那个 下配置的Manager应用最好限定来源IP比如通过防火墙规则或Valve做地址过滤。管理后台这种东西越少暴露越好。第三把端口、JAVA_HOME、JDK版本、部署目录这些关键信息写进运维文档。哪怕只是用Markdown简单记一下半年后有人接手环境时也不需要对着server.xml发呆。运维最贵的不是动作是回忆。每次做Linux系统安装部署Tomcat我都会把这套流程原样走一遍。版本确认、目录规划、专属用户、systemd托管、精简默认应用这些动作已经成了肌肉记忆。你不需要一次性学会所有优化项先把环境跑通再逐步加固就比大多数随缘部署要靠谱得多。