Skip to content

Tomcat 部署

Tomcat 是一个 Servlet 容器,用来运行 Java Web 应用。它和 Nginx 的定位不同——Nginx 处理静态文件和反向代理,Tomcat 负责加载 Java 应用、处理 Servlet 逻辑、执行 Java 代码后返回响应。

典型链路:

text
client → nginx → tomcat → java web application

一、几个核心对象

对象做什么运维关注什么
JDK/JREJava 运行环境版本、路径、JVM 内存参数
TomcatServlet 容器端口、线程、日志、部署目录
WARJava Web 应用包发布包路径、解压目录、回滚版本
ConnectorTomcat 接收 HTTP 请求的入口HTTP 端口、线程池、超时
Context应用的访问路径//app 这类路径映射

部署可以拆成两层看:机器上先有稳定的 Java 和 Tomcat 基础环境,再把业务 WAR 放进去。两层混在一起,发布和回滚会互相牵连。

二、安装 JDK

bash
# RHEL / Rocky / CentOS
yum install -y java-17-openjdk java-17-openjdk-devel
java -version

# Debian / Ubuntu
apt update
apt install -y openjdk-17-jdk
java -version

多版本 Java 并存时确认实际使用的是哪个:

bash
readlink -f "$(which java)"    # 看 java 命令最终指向的路径

Tomcat 和 JDK 版本要匹配。Tomcat 10 使用了 Jakarta EE 命名空间(jakarta.servlet),如果应用还停留在 javax.servlet,直接迁到 Tomcat 10 会启动失败——这类应用通常停在 Tomcat 9。

三、安装 Tomcat

创建运行用户,用软链接管理版本:

bash
useradd -r -s /sbin/nologin tomcat
tar -xf apache-tomcat-9.0.x.tar.gz -C /opt/
ln -s /opt/apache-tomcat-9.0.x /opt/tomcat   # 软链接便于后续升级切换
chown -R tomcat:tomcat /opt/apache-tomcat-9.0.x

目录结构:

目录用途
bin/启停脚本(startup.shshutdown.shcatalina.sh
conf/配置文件(server.xmlweb.xml 等)
logs/Tomcat 自身日志和应用控制台输出
webapps/默认的应用部署目录
work/JSP 编译产生的临时文件
temp/临时文件
lib/Tomcat 和应用共享的 jar 库

业务应用包和 Tomcat 程序目录分开管理更清晰——WAR 放在独立的数据目录,通过 Context 指向。直接把所有 WAR 堆在 webapps 里能跑,但多版本回滚、权限控制和清理都比较乱。

四、server.xml —— Connector 配置

/opt/tomcat/conf/server.xml 是核心配置文件。HTTP Connector 示例:

xml
<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxThreads="200"
           maxConnections="8192"
           acceptCount="100" />

字段含义:

字段做什么
portTomcat 监听端口
protocolHTTP 协议处理实现
connectionTimeout连接建立后等待请求的超时(毫秒)
maxThreads最大工作线程数——同时处理多少个请求
maxConnections允许同时保持的最大连接数(含处理中和排队的)
acceptCount连接数达到 maxConnections 后,OS 层面还能排多少连接
redirectPort需要 HTTPS 跳转时指向的端口

这三个参数不是同一层东西:maxConnections 控制"能进多少连接",maxThreads 控制"有多少线程在真正处理请求",acceptCount 是"满了之后还能在门外排多少人"。如果前面有 Nginx,Tomcat 通常只监听本机:

xml
<Connector port="8080"
           address="127.0.0.1"
           protocol="HTTP/1.1"
           ... />

五、systemd 管理

ini
# /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat
After=network.target

[Service]
Type=forking
User=tomcat
Group=tomcat

Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_PID=/run/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512m -Xmx512m"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

启动:

bash
systemctl daemon-reload
systemctl enable --now tomcat
systemctl status tomcat --no-pager
ss -lntp | grep ':8080'
curl -I http://127.0.0.1:8080/

JAVA_HOME 路径在不同发行版差异很大。unit 写好后先看 systemctl statusjournalctl 里的 Java 路径错误,比直接翻 catalina.out 更快定位到启动失败原因。

CATALINA_HOMECATALINA_BASE 的区别:

变量指向什么典型内容
CATALINA_HOMETomcat 程序目录bin/lib/ 等静态程序文件
CATALINA_BASE实例运行目录conf/logs/webapps/temp/

单实例部署两者指向同一个目录最省事。多实例部署时,多个实例共享同一个 CATALINA_HOME(同一份 Tomcat 程序),各自用独立的 CATALINA_BASE(端口、日志、应用包不互相混)。

六、部署 WAR 包

最简单的部署是把 WAR 丢进 webapps

bash
cp app.war /opt/tomcat/webapps/app.war
chown tomcat:tomcat /opt/tomcat/webapps/app.war

Tomcat 默认会自动解压并部署。访问路径由 WAR 文件名决定:

WAR 文件访问路径
app.war/app
ROOT.war/(根路径)

生产环境更推荐分离部署——WAR 放在独立的数据目录,通过 Context 文件指向:

bash
mkdir -p /data/apps/app/releases/20260522-001
cp app.war /data/apps/app/releases/20260522-001/app.war
ln -sfn /data/apps/app/releases/20260522-001/app.war /data/apps/app/current.war

Context 配置(/opt/tomcat/conf/Catalina/localhost/app.xml):

xml
<Context docBase="/data/apps/app/current.war"
         reloadable="false" />

reloadable="false" 在生产环境是必要的——设为 true 时 Tomcat 会持续监控类文件变化,增加额外开销。发布应该交给明确的部署流程,不是靠 Tomcat 自动检测。

七、日志

文件内容
logs/catalina.out控制台输出,很多应用日志也落在这里
logs/catalina.YYYY-MM-DD.logTomcat 自身的 Catalina 引擎日志
logs/localhost.YYYY-MM-DD.log应用上下文相关的日志
logs/localhost_access_log.*.txtTomcat 的访问日志

查看:

bash
tail -f /opt/tomcat/logs/catalina.out
tail -f /opt/tomcat/logs/localhost_access_log.*.txt
journalctl -u tomcat -n 100 --no-pager

catalina.out 很容易变得很大——它不自动切割。如果应用已经用 logback/log4j 输出了独立日志文件,控制台输出要控制好,避免 catalina.out 长期不切割占满磁盘。

八、Nginx 代理 Tomcat

nginx
upstream tomcat_app {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name app.example.local;

    location / {
        proxy_pass http://tomcat_app;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

如果应用部署在 /app 路径下,Nginx 代理的路径要和 Context 对上:

nginx
location /app/ {
    proxy_pass http://tomcat_app/app/;
}

路径错位时,Tomcat 可能返回 404,或者页面能打开但 CSS/JS 全 404——因为静态资源的路径拼出来的 URL 不符合实际路由。排查时看 Nginx access log 里的 URI、Tomcat access log 里的请求路径、以及浏览器开发者工具的 Network 面板,三条线对一下。

九、常见部署问题

现象常见原因排查入口
Tomcat 启动失败Java 路径错、端口被占用、XML 配置语法错journalctl -u tomcatcatalina.out
WAR 不解压权限不足、磁盘满、WAR 包损坏logs/webapps/ 目录权限
访问 404Context 路径不对、Nginx 代理路径不匹配Nginx access log + Tomcat access log
访问 502Nginx 连不上 Tomcatss -lntp 确认 8080 在监听
启动很慢应用初始化慢、数据库连接池初始化慢应用日志、网络连通性
改配置不生效没 reload/restart、改错了 CATALINA_BASE 目录systemctl status、启动参数确认

每次发布记录中保留:Java 版本、Tomcat 版本、WAR 包构建号、配置变更内容、JVM 启动参数和回滚包的路径。Java 应用出问题时,这些信息经常比"服务启动了还是没启动"更关键。