
1. 项目概述Linux上跑Tomcat到底在解决什么问题1.1 先搞清楚Tomcat到底是干嘛的很多刚接触后端开发的朋友上来就在Windows上把Tomcat解压、双击startup.bat看到“Server startup”就以为完事了。等真正上了生产环境面对一台只有命令行的Linux服务器瞬间就懵了没有图形界面没有批处理脚本连服务怎么启动都要重新学一遍。这篇文章就是围绕“Linux系统安装部署Tomcat”这件事把从零开始到生产可用的完整路径讲清楚。Tomcat的本质是Servlet容器和轻量级Web服务器。你写的Java Web应用打成WAR包之后并不是直接跑在操作系统上的而是需要一个符合Servlet规范的容器来加载、管理、运行这些组件。Tomcat就是最常见的选择之一。它负责监听端口、接收HTTP请求、把请求交给对应的Servlet或JSP处理再把结果返回给客户端。说得通俗一点Tomcat就是Java Web程序住的“房子”你写的代码是住户HTTP协议是门铃Tomcat负责接客、安排房间、送客。Linux上部署Tomcat和应用部署Windows的区别在于Linux下更强调命令行操作、脚本管理、权限控制和守护进程化。你在Windows上双击就能跑的Tomcat到了Linux上需要手写启动脚本、配环境变量、设置开机自启还需要考虑系统服务管理方式。这套流程一旦走通你才算真正理解Web应用是如何被托管的。1.2 为什么偏偏选Linux作为部署环境选择Linux不是因为它“高级”而是因为它确实更适合做服务器。先从成本说起大多数Linux发行版免费没有Windows Server那种授权费用一台云主机买完系统就是白来的。再从资源占用看纯命令行Linux空载内存占用可以压到几百MB左右省下来的资源都能留给Java应用和Tomcat。这一点在生产环境很关键特别是服务器配置不高、同时跑多个Java服务的时候Windows的图形界面和后台服务开销就非常不划算。稳定性方面Linux的进程管理、文件权限、日志轮转机制都非常成熟。同时远程维护优势也很大你只需要SSH连接就能完成全部部署操作不需要物理接触服务器也不用为远程桌面卡顿发愁。这也是为什么很多运维招聘要求里都写着“熟悉Linux常用命令”因为大部分服务器软件都跑在Linux上Tomcat只是其中一个典型代表。还有一个现实原因大部分企业云主机默认都是CentOS、Ubuntu或者Debian这些Linux发行版。你如果只会Windows上的Tomcat部署到生产环境依然寸步难行。所以这篇博文直接以Linux为主线把“Linux系统安装部署Tomcat”做成一个可以照着抄的标准操作流程。1.3 方案选型JDK版本、Tomcat版本、发行版怎么定动手之前先把版本选型这个最关键的问题解决。Tomcat本身是用Java写的所以必须先装JDK而且Tomcat和JDK之间有严格的兼容关系选错版本会出现启动异常或者ClassFormatError这类莫名其妙的问题。当前主流搭配是Tomcat 9或Tomcat 10对应JDK 8及以上版本。如果你用的是Jakarta EE 9的新规范那就选Tomcat 10如果你的应用是传统javax命名空间的WAR包Tomcat 9会更省事因为Tomcat 10已经把包名从javax.servlet改成jakarta.servlet老项目直接扔进去可能缺包报错。如果只是快速搭个环境跑旧项目Tomcat 8.5配JDK 8的组合非常稳网上大量教程也都基于这个组合问题排查资料最多。Linux发行版选型上新手建议用Ubuntu LTS或CentOS Stream这类资料丰富的系统。我自己在多个环境都部署过Ubuntu下apt装软件方便CentOS下systemd配置资料多Debian最省资源。核心是搞清楚自己用的包管理器是谁Ubuntu/Debian用aptCentOS/RHEL系用dnf或yum。下面所有操作流程我会以通用性为主同时标注两类系统的差异方便你照做。2. 环境准备与安装包选择2.1 安装JDKOpenJDK还是Oracle JDKTomcat运行需要JDK或JRE生产环境我推荐OpenJDK一是免费二是没有Oracle JDK那种商业授权限制。用系统包管理器装OpenJDK是最省心的方法Ubuntu系执行sudo apt update sudo apt install -y openjdk-11-jdkCentOS系执行sudo yum install -y java-11-openjdk-devel安装完成后验证版本java -version能正常输出版本号说明JDK已经可用。这里有个小细节如果系统里同时装了多个JDK版本建议用update-alternatives命令统一管理比如CentOS系可以执行sudo update-alternatives --config java来选择默认版本。Ubuntu系也有类似的update-alternatives机制。我自己一般在生产环境用JDK 11配合Tomcat 9原因很简单JDK 8虽然老当益壮但JDK 11在性能、ZGC垃圾回收、TLS协议支持上都更好同时也没有JDK 17那么挑Tomcat版本。如果你是在内网跑旧系统JDK 8继续用也没问题但新项目建议直接上11或17。2.2 创建专用用户与目录规划很多人图省事直接用root启动Tomcat这在实验环境可以生产环境千万别这么干。Tomcat启动后会监听网络端口如果进程权限过高一旦Web应用被上传了恶意脚本攻击者就直接拿到root权限了。正确做法是创建一个专用系统用户只给它碰Tomcat目录的能力。创建用户的命令sudo useradd -r -m -s /bin/bash tomcat参数说明-r表示创建系统用户-m表示同时创建家目录-s指定登录shell。然后规划目录我习惯把Tomcat放在/opt/tomcat应用日志放在/opt/tomcat/logsWeb应用默认部署在/opt/tomcat/webapps。目录和日志分开有个好处后续做日志采集和备份时不用在Tomcat内部目录里翻找。创建并授权目录sudo mkdir -p /opt/tomcat sudo chown -R tomcat:tomcat /opt/tomcat为什么要单独授权而不是直接chmod 777因为Tomcat目录里如果有应用配置文件任何用户都能改就废了。用tomcat用户运行、tomcat组管理是最小权限原则的标准操作。2.3 下载并解压Tomcat下载Tomcat最靠谱的途径是官方站点也可以从国内镜像拉速度更快。先到Apache Tomcat官网确认你想用的版本号然后直接在服务器上用wget下载cd /tmp wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.98/bin/apache-tomcat-9.0.98.tar.gz如果服务器访问外网受限就先在本地下载tar.gz包再通过scp或rz命令上传。解压命令sudo mkdir -p /opt/tomcat sudo tar -xzf apache-tomcat-9.0.98.tar.gz -C /opt/tomcat sudo mv /opt/tomcat/apache-tomcat-9.0.98 /opt/tomcat/latest这里我把解压后的目录重命名为latest方便以后升级时切换软链接。你可能会问为什么不直接把目录就叫tomcat因为Tomcat的目录里通常会带版本号升级时新旧版本目录并存用latest指向当前版本回滚时改软链接就行非常方便。3. 核心配置与启动验证3.1 配置环境变量与目录权限解压完成后第一件事是配置环境变量。虽然Tomcat启动脚本能自动找JAVA_HOME但有多个JDK版本或特殊安装路径时显式指定更可靠。编辑/etc/profile.d/tomcat.shsudo vi /etc/profile.d/tomcat.sh写入export CATALINA_HOME/opt/tomcat/latest export JAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64注意JAVA_HOME的路径要以实际安装位置为准可以用readlink -f $(which java)来解析真实路径。设置完执行source /etc/profile使其生效然后验证echo $CATALINA_HOME接下来确认目录权限确保tomcat用户能读写整个/opt/tomcat/latest目录尤其是logs、temp、work三个子目录必须有写权限sudo chown -R tomcat:tomcat /opt/tomcat/latest这一步经常被忽略结果就是启动时报“Cannot create directory”或者日志文件根本写不进去。Tomcat启动时会在temp和work目录生成临时文件和编译文件logs目录要写日志权限不对必然出问题。3.2 修改server.xml端口、字符集、主机名Tomcat的核心配置文件是/opt/tomcat/latest/conf/server.xml。默认端口是8080如果你要改成80或其他端口找这一行改port属性即可Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /改端口时要注意两件事一是1024以下端口比如80启动用户需要有相应权限普通用户绑定80端口可能会失败需要改为tomcat用户用setcap授权或用systemd处理这个我后面再细说。二是如果本机已有Nginx或Apache占用80端口直接把Tomcat改到8080反而最省事让Nginx后面反代。生产环境我还会给Connector加上URIEncoding配置避免中文请求参数出现乱码。虽然Tomcat 8以上默认URI编码是UTF-8但加上更保险Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 /Host配置那块默认的主机名是localhost对应应用访问地址是http://服务器IP:8080。如果你有域名可以加一个host但前期没必要直接改默认Host的name属性即可。修改完server.xml后记得重启Tomcat才能生效。3.3 首次启动与日志验证现在可以启动Tomcat了。切换成tomcat用户再启动避免root权限残留sudo -u tomcat /opt/tomcat/latest/bin/startup.sh启动后立刻查看日志tail -f /opt/tomcat/latest/logs/catalina.out看到INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxx] milliseconds说明启动成功。接着验证端口ss -tlnp | grep 8080能听到LISTEN状态就说明Tomcat正在监听。然后用curl测一下首页curl -I http://127.0.0.1:8080返回HTTP/1.1 200就基本OK了。这里分享一个我踩过的坑用startup.sh启动后如果前面环境变量没配好脚本会报“Cannot find /usr/lib/jvm/java-11-openjdk-amd64/bin/java”听起来很蠢但真的很常见。解决办法是检查JAVA_HOME是否指向了真实存在的JDK路径或者干脆在setenv.sh里写死这个后面会讲。4. 生产环境深化配置4.1 内存参数调优Tomcat默认运行内存取决于JVM通常不够用。默认情况下JVM堆内存可能只分配物理内存的1/4对高并发应用来说很容易出现OutOfMemoryError。修改内存参数不建议直接改catalina.sh应该创建setenv.sh这样升级Tomcat时不会覆盖你的配置。创建/opt/tomcat/latest/bin/setenv.shexport CATALINA_OPTS-Xms512m -Xmx1024m -XX:MetaspaceSize256m -XX:MaxMetaspaceSize512m参数说明-Xms是初始堆大小-Xmx是最大堆大小这两个值一般建议设成一样减少运行时堆扩容的性能抖动。MetaspaceSize是元空间初始值Java 8以后方法区在这个区域。还有一个容易被忽略的参数export JAVA_OPTS-Xms512m -Xmx1024m -Djava.security.egdfile:/dev/./urandom-Djava.security.egdfile:/dev/./urandom可以加快Tomcat启动速度因为Java的SecureRandom在某些Linux环境下会阻塞等待熵源换成urandom后启动明显变快。这个技巧我在很多服务器上实测有效强烈建议加上。设置完记得给setenv.sh加可执行权限chmod x /opt/tomcat/latest/bin/setenv.sh重启Tomcat后用jmap或jstat验证参数是否生效jmap -heap $(pgrep -f catalina.startup.Bootstrap | head -1)堆信息里能看到MinHeapFreeRatio、MaxHeapSize这些值确认内存配置没白改。4.2 注册为systemd服务实现开机自启虽然手动执行startup.sh也能跑起来但服务器一重启Tomcat就没了得人肉去启动这在生产环境不可接受。正确做法是注册成systemd服务。创建服务文件/etc/systemd/system/tomcat.service[Unit] DescriptionApache Tomcat 9 Afternetwork.target [Service] Typeforking Usertomcat Grouptomcat EnvironmentJAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64 EnvironmentCATALINA_HOME/opt/tomcat/latest EnvironmentCATALINA_BASE/opt/tomcat/latest ExecStart/opt/tomcat/latest/bin/startup.sh ExecStop/opt/tomcat/latest/bin/shutdown.sh SuccessExitEventStatus0 Restarton-failure RestartSec5 [Install] WantedBymulti-user.target关键点在于Typeforking因为Tomcat的startup.sh会启动一个独立进程然后立即返回systemd需要知道哪个进程是真正的Tomcat服务进程。Tomcat默认会把PID写到/opt/tomcat/latest/tmp下Systemd通过catlina启动脚本自动识别PID所以不需要额外配置PIDFile。配置完成后执行sudo systemctl daemon-reload sudo systemctl start tomcat sudo systemctl enable tomcat sudo systemctl status tomcatenable就是设置开机自启。以后服务管理都用systemctl start/stop/restart tomcat比直接调脚本规范得多。我实测过这样配置后Tomcat异常挂掉会在5秒后自动拉起比裸跑脚本靠谱太多。4.3 防火墙与反向代理对接Tomcat启动后外部能不能访问还取决于防火墙。很多云服务器自带安全组规则同时Linux本机也可能开了firewalld或ufw。Ubuntu上用ufw的话执行sudo ufw allow 8080/tcpCentOS上如果用的是firewalldsudo firewall-cmd --permanent --add-port8080/tcp sudo firewall-cmd --reload如果服务器有云安全组还要去云控制台把8080端口加入入方向规则否则本机防火墙放行一样白搭。对于已经跑着Nginx的环境我不建议直接暴露Tomcat端口而是用Nginx做反向代理。Nginx监听80端口把请求转发给Tomcat的8080端口。Nginx配置示例server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这样做的好处是静态资源由Nginx处理动态请求交给Tomcat同时Nginx还能做负载均衡和HTTPS终止。生产环境基本都是这个架构单纯让Tomcat暴露8080只适合内网或实验环境。5. 常见问题与排查实录5.1 端口占用与启动失败Tomcat启动失败最常见的原因就是端口被占用。比如你之前已经启动了一个Tomcat再执行startup.sh日志里会出现SEVERE [main] org.apache.catalina.util.LifecycleBase.handleSubClassException Failed to initialize component [Connector[HTTP/1.1-8080]]排查步骤很简单ss -tlnp | grep 8080 lsof -i:8080找到占用端口的进程确认是不是残留的Tomcat进程如果是就kill掉再启动。注意用shutdown.sh关掉旧Tomcat别直接kill -9否则可能造成未完成请求丢失。如果端口真的被其他服务占用那就改server.xml里的端口号。另外还有个隐藏原因Tomcat启动到一半报端口被占但ss命令又看不到很可能是IPv6和IPv4绑定的问题。Tomcat默认监听:::8080如果系统同时绑定了IPv6和IPv4偶尔会有冲突可以尝试把Connector的address属性改成127.0.0.1或0.0.0.0试试。5.2 启动脚本找不到JDK日志里出现Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is required to run this program这是最典型的初始化错误原因就是JAVA_HOME没有设置或者设置成了不存在的路径。解决办法有三层第一层在/etc/profile.d/tomcat.sh里配置全局环境变量第二层在setenv.sh里写JAVA_HOME第三层在catalina.sh头部手动指定。我一般建议用第二层因为只影响Tomcat不影响系统其他进程。检查JAVA_HOME路径是否正确的方法是执行ls $JAVA_HOME/bin/java能列出文件说明路径正确。如果你用apt安装OpenJDK路径类似/usr/lib/jvm/java-11-openjdk-amd64如果你是手动解压JDK到/opt/jdk路径就是/opt/jdk不要带后面的bin目录。5.3 访问出现500/404/403一个常见场景部署WAR包后访问应用首页出现404原因是WAR包没有解压成功或应用上下文路径不对。检查/opt/tomcat/latest/webapps目录正常情况下Tomcat会自动解压war包生成同名目录。如果只看到war文件没有目录说明部署失败去看logs目录下的localhost.日期.log通常能找到线索。403则多是指定了目录但没配置欢迎文件或者访问的是/manager/html这类管理界面但tomcat用户没有权限。Tomcat 9以后默认禁止远程访问manager和host-manager这是为了保护管理接口。如果你确实需要用管理页面远程部署应在conf/context.xml里调整或改conf/tomcat-users.xml但我不建议暴露管理端口给外网除非你能加IP白名单。500错误就比较复杂了可能是应用本身异常、JDBC驱动没加载、依赖缺失。优先看catalina.out和localhost.日期.log大部分堆栈都会直接打出来。我遇到最多的坑是Web应用里的一个Servlet在Linux下类名大小写问题Windows下不区分大小写掩盖了错误到Linux上一跑就500。5.4 日志乱码与字符集问题Linux上跑Tomcat中文日志或中文页面出现乱码是高频问题。原因通常是系统默认字符集不是UTF-8或者Tomcat启动时没有指定文件编码。解决办法是在setenv.sh里增加export JAVA_OPTS$JAVA_OPTS -Dfile.encodingUTF-8同时确保系统locale是UTF-8locale如果显示不是en_US.UTF-8或zh_CN.UTF-8可以执行export LANGen_US.UTF-8临时设置或者修改/etc/default/locale配置文件。注意Tomcat日志文件本身的编码也需保持一致catalina.out如果已经产生乱码单纯改配置不会自动修正旧日志后续新日志才会正常。应用层面的请求参数乱码除了前面说的server.xml里配URIEncoding还要检查应用自己有没有设置request.setCharacterEncoding(UTF-8)。很多情况下Tomcat已经给了UTF-8但应用的Filter把编码重置成了GBK造成前后端互相打架。5.5 内存溢出与线程耗尽的排查跑一段时间后日志出现java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Metaspace堆空间不足的应对思路确认setenv.sh里的-Xmx是否够用用jstat监控堆使用率jstat -gcutil $(pgrep -f catalina.startup.Bootstrap | head -1) 1000如果Old区长时间接近100%说明存在内存泄漏或者对象持有时间过长加大-Xmx只能缓解不能根治。重点检查是否有全局静态集合类只加不减、JDBC连接是否关闭、ThreadLocal是否清理。线程耗尽可能表现为SEVERE: All threads (150) are currently busyTomcat默认最大线程数是200根据版本略有差异连接池线程耗尽通常意味着某个请求阻塞太久。处理方法调大maxThreads属性Connector port8080 protocolHTTP/1.1 maxThreads400 connectionTimeout20000 /但更值得做的是找后台慢SQL或死锁。我曾经遇到过一个问题数据库连接池被耗尽Tomcat的每个请求都卡在等待连接表面看线程耗尽是表象真正原因是数据库连接没释放。6. 我个人最推荐的一套部署组合与收尾技巧说了这么多最后分享一下我个人实际使用后最顺手的一套组合Ubuntu 22.04 LTS OpenJDK 11 Tomcat 9.0.x systemd托管。这套组合的好处是三者的兼容性踩坑最少OpenJDK用apt安装方便systemd脚本网上模板多Tomcat 9兼容绝大多数传统Javax应用适合大多数业务场景。安装时我习惯写一个简单的部署脚本以后新服务器直接执行#!/bin/bash # 简易Tomcat部署脚本Ubuntu/Debian适用 sudo apt update sudo apt install -y openjdk-11-jdk sudo groupadd --system tomcat || true sudo useradd -r -m -d /opt/tomcat -s /bin/bash -g tomcat tomcat || true sudo mkdir -p /opt/tomcat sudo tar -xzf apache-tomcat-9.0.98.tar.gz -C /opt/tomcat sudo ln -sfn /opt/tomcat/apache-tomcat-9.0.98 /opt/tomcat/latest sudo chown -R tomcat:tomcat /opt/tomcat然后重启服务、检查端口、看日志整个流程不超过五分钟。配合systemd服务后日常运维只需要一条命令systemctl restart tomcat。最后再提醒一个很多人会忽略的细节修改Tomcat配置前一定先备份server.xml和setenv.sh改完用configtest.sh脚本验证配置是否有语法错误/opt/tomcat/latest/bin/configtest.sh返回OK再重启能省掉无数因为手误把XML标签写错导致的启动失败。这算是我踩了多次坑之后总结出的血泪教训希望你能直接用上。