Tomcat 10 实战部署指南:从零到生产级 Web 容器

Tomcat 10 实战部署指南:从零到生产级 Web 容器

环境 :华为云 ECS ecs-53b4 集群 · Ubuntu 24.04.4 LTS · OpenJDK 17 · Tomcat 10.1.55 · Nginx 1.24

时间 :2026-06-11

规格:2vCPU / 4GiB / 40GB × 4 台


一、Tomcat 是什么,为什么选它

1.1 定位

Apache Tomcat 是 Jakarta EE(原 Java EE)规范的开源实现,核心提供两种容器:

容器类型 规范 作用
Servlet Container Jakarta Servlet 处理 HTTP 请求 → 分发给 Servlet
JSP Container Jakarta Pages 将 .jsp 编译为 Servlet

Tomcat 不是完整的 Jakarta EE 服务器(不含 EJB、JMS 等),但覆盖了绝大多数 Web 应用场景。

1.2 版本与 Java/规范对应关系

Tomcat Jakarta Servlet Jakarta Pages 最低 Java
10.1.x 6.0 3.1 Java 11
10.0.x 5.0 3.0 Java 8
9.0.x 4.0 2.3 Java 8

⚠️ 坑点一:包名迁移

Tomcat 10 起,Servlet API 从 javax.servlet.* 迁移为 jakarta.servlet.*

如果你的旧项目用了 import javax.servlet.http.HttpServlet,在 Tomcat 10 上会报 ClassNotFoundException

解法 :升级依赖坐标,或使用 Jakarta EE Migration Tool 自动迁移。

1.3 竞品对比

维度 Tomcat 10 Undertow (WildFly) Jetty 12 Spring Boot Embedded
部署方式 独立/嵌入 独立/嵌入 独立/嵌入 仅嵌入
内存占用 ~120MB ~80MB ~60MB ~130MB
管理界面 Manager Web UI ✅ Admin Console ✅ Actuator
热部署 ✅ WAR 自动解包 需 DevTools
适用场景 传统 WAR 部署 高并发/微服务 轻量级嵌入 Spring 生态

二、架构总览

复制代码
                    互联网
                      │
               ┌──────▼──────┐
               │   Nginx 1.24 │  ← 80 端口,反向代理
               │  (ecs-53b4-0001: 1.92.101.204) │
               └──────┬──────┘
                      │ proxy_pass 127.0.0.1:8080
               ┌──────▼──────┐
               │  Tomcat 10.1.55 │  ← 8080 端口
               │   JVM (Java 17)  │
               │   -Xms512m -Xmx1024m │
               └──────┬──────┘
                      │
         ┌────────────┼────────────┐
         ▼            ▼            ▼
    /webapps/ROOT  /webapps/helloapp  /webapps/manager
    (默认首页)      (示例 Servlet)     (管理控制台)

文件系统:
/opt/
├── apache-tomcat-10.1.55/   ← 实际安装目录
│   ├── bin/                 ← 启动脚本 + setenv.sh
│   ├── conf/                ← server.xml / tomcat-users.xml
│   ├── logs/                ← catalina.out + 访问日志
│   ├── webapps/             ← WAR 部署目录
│   ├── work/                ← JSP 编译缓存
│   └── temp/                ← 临时文件 + PID
└── tomcat -> apache-tomcat-10.1.55  ← 软链接(版本切换用)

三、实操:服务器环境准备

3.1 集群节点信息

节点 公网 IP 私有 IP 角色
ecs-53b4-0001 1.92.101.204 192.168.0.58 Tomcat + Nginx 主节点
ecs-53b4-0002 1.92.95.49 192.168.0.221 备用节点
ecs-53b4-0003 1.92.90.163 192.168.0.7 备用节点
ecs-53b4-0004 1.92.111.126 192.168.0.63 备用节点

规格:2vCPU / 4GiB / 40GB · Ubuntu 24.04.4 LTS · Kernel 6.8.0-106-generic

3.2 系统初始化检查

bash 复制代码
# 登录验证
ssh root@1.92.101.204

# 确认系统信息
hostnamectl
# Static hostname: ecs-53b4-0001
# Operating System: Ubuntu 24.04.4 LTS
# Kernel: Linux 6.8.0-106-generic

uname -r
# 6.8.0-106-generic

free -h
#                total        used        free
# Mem:           3.3Gi       425Mi       2.7Gi

df -h /
# Filesystem      Size  Used Avail Use%
# /dev/vda1        40G  3.4G   35G   9%

四、安装 OpenJDK 17

4.1 安装命令

bash 复制代码
# 更新软件源
apt-get update -qq

# 安装 OpenJDK 17(headless 版本,无 GUI 组件,节省约 80MB)
DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-17-jdk-headless

# 验证版本
java -version

实际输出

复制代码
openjdk version "17.0.19" 2026-04-21
OpenJDK Runtime Environment (build 17.0.19+10-1-24.04.2-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.19+10-1-24.04.2-Ubuntu, mixed mode, sharing)

4.2 确认 JAVA_HOME

bash 复制代码
# 通过 update-alternatives 获取 JDK 路径
JAVA_PATH=$(update-alternatives --query java | grep 'Value:' | head -1 | awk '{print $2}')
echo JAVA_HOME=$(dirname $(dirname $JAVA_PATH))
# JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

为什么用 JDK 而不是 JRE?

Tomcat 在编译 JSP 时需要 javac,JRE 不包含编译器,会报 Cannot find javac

openjdk-17-jdk-headless 包含完整 JDK 但去掉了图形组件,适合服务器场景。


五、安装 Tomcat 10.1.55

5.1 下载(使用华为云镜像加速)

⚠️ 坑点二:直连 Apache 官网速度极慢

华为云香港节点直连 dlcdn.apache.org 下载速度约 20KB/s,14MB 包需要 10+ 分钟。

改用华为开源镜像后速度提升到 2MB/s+,30秒完成。

bash 复制代码
TOMCAT_VER="10.1.55"

# ❌ 慢(不推荐,香港节点下载实测约 20KB/s)
# wget https://dlcdn.apache.org/tomcat/tomcat-10/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz

# ✅ 快(推荐:华为开源镜像)
wget -q "https://mirrors.huaweicloud.com/apache/tomcat/tomcat-10/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz" \
     -O /tmp/apache-tomcat-${TOMCAT_VER}.tar.gz

ls -lh /tmp/apache-tomcat-${TOMCAT_VER}.tar.gz
# -rw-r--r-- 1 root root 14M May  5 21:39 /tmp/apache-tomcat-10.1.55.tar.gz

5.2 解压与软链接

bash 复制代码
# 解压到 /opt
tar -xzf /tmp/apache-tomcat-${TOMCAT_VER}.tar.gz -C /opt/

# 创建软链接(方便后续升级:只需更新软链即可)
ln -sfn /opt/apache-tomcat-${TOMCAT_VER} /opt/tomcat

# 验证目录结构
ls /opt/tomcat/

输出

复制代码
bin   BUILDING.txt  conf         CONTRIBUTING.md  lib
LICENSE  logs  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  temp  webapps  work

5.3 目录说明

目录/文件 用途
bin/ 启动脚本:startup.shshutdown.shcatalina.shsetenv.sh(自建)
conf/ 核心配置:server.xml(端口/Host)、tomcat-users.xml(用户权限)、web.xml(全局Servlet)
logs/ 日志:catalina.out(主日志)、catalina.YYYY-MM-DD.loglocalhost_access_log(访问日志)
webapps/ 应用部署目录:将 .war 放入此处即自动解包部署
work/ JSP 编译后的 .java.class 缓存,Tomcat 重启会清空
temp/ 临时文件,存放 PID 文件

5.4 创建专用系统用户

bash 复制代码
# 创建系统用户(无登录 shell,home 目录设为 /opt/tomcat)
useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat

# 验证
id tomcat
# uid=996(tomcat) gid=988(tomcat) groups=988(tomcat)

# 设置目录归属
chown -R tomcat:tomcat /opt/apache-tomcat-10.1.55
chown -h tomcat:tomcat /opt/tomcat

为什么要创建专用用户?

用 root 运行 Tomcat 是安全风险。如果应用存在 RCE 漏洞,攻击者将获得 root 权限。

专用用户 tomcat 只有读写 /opt/tomcat 的权限,攻击面大幅缩小。


六、关键配置文件详解

6.1 setenv.sh --- JVM 调优参数

bash 复制代码
cat > /opt/tomcat/bin/setenv.sh << 'EOF'
# JDK 路径(必须设置,否则 systemd 启动找不到 java)
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

# Catalina 基础路径
export CATALINA_HOME=/opt/tomcat

# PID 文件路径(systemd stop 时用于找到进程)
export CATALINA_PID=$CATALINA_HOME/temp/tomcat.pid

# JVM 系统属性
# - java.security.egd:加速随机数生成(解决 SecureRandom 慢的问题)
# - java.awt.headless:无头模式,服务器必须设为 true,否则某些图表库报错
export JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom -Djava.awt.headless=true"

# Catalina JVM 参数
# -server:启用 JIT 优化,生产环境必加
# -Xms512m:JVM 堆初始大小(建议与 Xmx 相同,避免动态扩容开销)
# -Xmx1024m:JVM 堆最大大小(本机 3.3GiB,给 OS 留余量,设 1GB)
# -XX:MetaspaceSize=128m:初始 Metaspace(方法区),防止频繁 GC
# -XX:MaxMetaspaceSize=256m:最大 Metaspace,防止类加载器泄漏撑爆内存
export CATALINA_OPTS="-server -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"
EOF

chmod +x /opt/tomcat/bin/setenv.sh
chown tomcat:tomcat /opt/tomcat/bin/setenv.sh

⚠️ 坑点三:不设 java.security.egd 导致启动极慢

Linux 默认 /dev/random 是阻塞随机源,在熵池不足时会卡住。

Tomcat 启动时大量使用 SecureRandom,不设此参数可能导致启动需要 30+ 秒。

指向 /dev/./urandom(注意中间有 .)使用非阻塞伪随机源解决此问题。

6.2 systemd 服务单元

bash 复制代码
cat > /etc/systemd/system/tomcat.service << 'EOF'
[Unit]
Description=Apache Tomcat 10 Web Application Server
Documentation=https://tomcat.apache.org
After=network.target

[Service]
Type=forking
# forking:父进程 startup.sh 启动后退出,JVM 作为子进程继续运行

User=tomcat
Group=tomcat

Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_OPTS=-server -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"

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

Restart=on-failure      # 异常退出时自动重启
RestartSec=10           # 等待 10s 再重启
SuccessExitStatus=143   # Java 进程被 SIGTERM 终止时退出码为 143,视为正常退出

StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable tomcat   # 开机自启

# 验证
# Created symlink /etc/systemd/system/multi-user.target.wants/tomcat.service

6.3 server.xml 核心结构(简化版)

xml 复制代码
<!-- /opt/tomcat/conf/server.xml(部分摘录,含注释) -->
<Server port="8005" shutdown="SHUTDOWN">
  <!-- 8005:关闭端口,生产建议改为随机端口或禁用 -->

  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               maxThreads="200"
               minSpareThreads="10">
      <!-- port:HTTP 监听端口
           maxThreads:最大工作线程数(默认200,高并发可适当调大)
           connectionTimeout:连接超时毫秒 -->
    </Connector>

    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <!-- appBase:WAR 部署根目录
             unpackWARs:是否自动解包 WAR(true = 热部署)
             autoDeploy:是否监视目录变化自动部署(生产建议 false) -->
      </Host>
    </Engine>
  </Service>
</Server>

七、启动与验证

7.1 启动 Tomcat

bash 复制代码
systemctl start tomcat
systemctl status tomcat --no-pager

实际输出

复制代码
● tomcat.service - Apache Tomcat 10 Web Application Server
     Loaded: loaded (/etc/systemd/system/tomcat.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-06-11 15:19:13 CST; 3s ago
       Docs: https://tomcat.apache.org
    Process: 8618 ExecStart=/opt/tomcat/bin/startup.sh (code=exited, status=0/SUCCESS)
   Main PID: 8625 (java)
      Tasks: 37 (limit: 3994)
     Memory: 95.5M (peak: 95.9M)
        CPU: 2.055s
     CGroup: /system.slice/tomcat.service
             └─8625 /usr/lib/jvm/java-17-openjdk-amd64/bin/java -server -Xms512m ...

7.2 查看 catalina.out 启动日志

bash 复制代码
tail -20 /opt/tomcat/logs/catalina.out

实际输出(关键行)

复制代码
11-Jun-2026 15:19:14.124 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent
  The Apache Tomcat Native library which allows using OpenSSL was not found on the java.library.path: ...
  # ⬆️ 这个 INFO 不是错误!只是说明没有安装 Native Library,SSL 会使用纯 Java 实现(性能稍差但功能正常)

11-Jun-2026 15:19:14.381 INFO [main] org.apache.coyote.AbstractProtocol.init
  Initializing ProtocolHandler ["http-nio-8080"]

11-Jun-2026 15:19:14.405 INFO [main] org.apache.catalina.startup.Catalina.load
  Server initialization in [406] milliseconds

11-Jun-2026 15:19:14.442 INFO [main] org.apache.catalina.core.StandardService.startInternal
  Starting service [Catalina]

11-Jun-2026 15:19:14.442 INFO [main] org.apache.catalina.core.StandardEngine.startInternal
  Starting Servlet engine: [Apache Tomcat/10.1.55]

11-Jun-2026 15:19:14.895 INFO [main] org.apache.coyote.AbstractProtocol.start
  Starting ProtocolHandler ["http-nio-8080"]

11-Jun-2026 15:19:14.906 INFO [main] org.apache.catalina.startup.Catalina.start
  Server startup in [500] milliseconds   # ✅ 500ms 启动完成!

7.3 端口验证

bash 复制代码
ss -tlnp | grep 8080
# LISTEN 0  100  *:8080  *:*  users:(("java",pid=8625,fd=43))

7.4 curl 验证

bash 复制代码
curl -s -o /dev/null -w 'HTTP Status: %{http_code}\nTime: %{time_total}s\n' http://localhost:8080/
# HTTP Status: 200
# Time: 0.832432s

八、部署应用:WAR 包实战

8.1 编写示例 Servlet(Jakarta Servlet 6.0)

java 复制代码
// /tmp/helloapp/WEB-INF/classes/com/example/HelloServlet.java
package com.example;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.*;
import java.time.*;
import java.time.format.*;

public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("application/json;charset=UTF-8");
        String now = LocalDateTime.now()
            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String json = String.format(
            "{\"status\":\"ok\",\"message\":\"Hello from Tomcat 10.1!\",\"server\":\"ecs-53b4-0001\",\"time\":\"%s\"}",
            now
        );
        resp.getWriter().println(json);
    }
}
xml 复制代码
<!-- /tmp/helloapp/WEB-INF/web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.example.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

8.2 编译 & 打包

bash 复制代码
# 编译(使用 Tomcat 自带的 servlet-api.jar,避免引入额外依赖)
javac -cp /opt/tomcat/lib/servlet-api.jar \
      /tmp/helloapp/WEB-INF/classes/com/example/HelloServlet.java

# 打包 WAR
cd /tmp/helloapp
jar -cvf /tmp/helloapp.war .

ls -lh /tmp/helloapp.war
# -rw-r--r-- 1 root root 2.5K  helloapp.war

8.3 部署到 Tomcat(热部署)

bash 复制代码
# 直接将 WAR 复制到 webapps 目录,Tomcat 会自动检测并解包
cp /tmp/helloapp.war /opt/tomcat/webapps/
chown tomcat:tomcat /opt/tomcat/webapps/helloapp.war

# 等待约 5 秒(Tomcat 后台扫描周期)
sleep 5

# 验证解包
ls /opt/tomcat/webapps/
# docs  examples  helloapp  helloapp.war  host-manager  manager  ROOT

部署日志(catalina.out):

复制代码
11-Jun-2026 15:19:54.901 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployWAR
  Deploying web application archive [/opt/apache-tomcat-10.1.55/webapps/helloapp.war]
11-Jun-2026 15:19:54.924 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployWAR
  Deployment of web application archive [/opt/apache-tomcat-10.1.55/webapps/helloapp.war] has finished in [23] ms

8.4 验证接口响应

bash 复制代码
curl -s http://localhost:8080/helloapp/hello

实际输出

json 复制代码
{"status":"ok","message":"Hello from Tomcat 10.1!","server":"ecs-53b4-0001","time":"2026-06-11 15:20:04"}

九、配置 Tomcat Manager 控制台

9.1 创建管理员用户

bash 复制代码
cat > /opt/tomcat/conf/tomcat-users.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml" version="1.0">
  <!-- 角色说明:
       manager-gui:   Web 控制台访问(查看/启停应用、查看内存/线程)
       manager-script:REST API 访问(用于 CI/CD 自动部署)
       admin-gui:     Host Manager 控制台访问
       admin-script:  Host Manager REST API -->
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <role rolename="admin-gui"/>
  <role rolename="admin-script"/>
  <user username="tomcat" password="Tomcat@2026"
        roles="manager-gui,manager-script,admin-gui,admin-script"/>
</tomcat-users>
EOF
chown tomcat:tomcat /opt/tomcat/conf/tomcat-users.xml

9.2 解除 Manager 远程访问 IP 限制

⚠️ 坑点四:Manager 默认只允许 localhost 访问

默认 context.xml 包含 <Valve className="...RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|...">

从外网访问 /manager/html 会返回 403。

bash 复制代码
# 覆写 manager 和 host-manager 的 context.xml,去掉 IP 过滤
cat > /opt/tomcat/webapps/manager/META-INF/context.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true">
  <CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
                   sameSiteCookies="strict"/>
  <!-- 移除了 RemoteAddrValve,允许所有 IP 访问 -->
  <!-- 生产环境建议用 Nginx 的 allow/deny 做 IP 过滤 -->
</Context>
EOF

cat > /opt/tomcat/webapps/host-manager/META-INF/context.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true">
  <CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
                   sameSiteCookies="strict"/>
</Context>
EOF

systemctl restart tomcat

Manager 控制台访问

http://1.92.101.204:8080/manager/html → 用户名 tomcat / 密码 Tomcat@2026


十、Nginx 反向代理配置

10.1 为什么需要 Nginx

需求 Tomcat 直接暴露 Nginx 反代
80 端口访问 需要 root 启动(不安全) Nginx 以 root 绑定 80,请求转发给 tomcat 用户的 8080 ✅
SSL/HTTPS 需配置 Tomcat Connector Nginx 统一卸载 TLS,配置更简单 ✅
静态文件 Tomcat 处理(慢) Nginx 直接服务(快 10x)✅
限流/访问控制 不支持 limit_req_zone / allow deny
多应用路由 单一实例 location /app1 → 不同后端 ✅

10.2 安装 & 配置

bash 复制代码
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx

cat > /etc/nginx/sites-available/tomcat << 'EOF'
server {
    listen 80;
    server_name _;  # 接受所有域名,生产环境改为实际域名

    access_log /var/log/nginx/tomcat_access.log;
    error_log  /var/log/nginx/tomcat_error.log;

    location / {
        proxy_pass         http://127.0.0.1:8080;

        # 传递真实客户端 IP(Tomcat 的访问日志中显示)
        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;

        # 超时设置(根据应用响应时间调整)
        proxy_connect_timeout 10s;
        proxy_send_timeout    30s;
        proxy_read_timeout    30s;
    }
}
EOF

ln -sf /etc/nginx/sites-available/tomcat /etc/nginx/sites-enabled/tomcat
rm -f /etc/nginx/sites-enabled/default   # 移除默认配置,避免冲突

nginx -t   # 语法检查
systemctl restart nginx

验证输出

复制代码
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

10.3 验证反代效果

bash 复制代码
# 通过 80 端口访问
curl -s -o /dev/null -w 'HTTP Status: %{http_code}\nTime: %{time_total}s\n' \
     http://localhost:80/helloapp/hello
# HTTP Status: 200
# Time: 0.040484s   ← 注意比直连 8080 快(0.83s → 0.04s,因为连接已预热)

curl -s http://localhost:80/helloapp/hello
# {"status":"ok","message":"Hello from Tomcat 10.1!","server":"ecs-53b4-0001","time":"2026-06-11 15:20:42"}

# 端口全局视图
ss -tlnp | grep -E '8080|:80'
# LISTEN  0  511  0.0.0.0:80    0.0.0.0:*   users:(("nginx",...))
# LISTEN  0  100        *:8080        *:*    users:(("java",...))
# LISTEN  0    1  [::ffff:127.0.0.1]:8005    *:*    users:(("java",...))

十一、日志管理

11.1 日志文件说明

bash 复制代码
ls -lh /opt/tomcat/logs/
# -rw-r----- 1 tomcat tomcat  17K Jun 11 15:20 catalina.2026-06-11.log   ← 按日滚动
# -rw-r----- 1 tomcat tomcat  17K Jun 11 15:20 catalina.out              ← 所有输出汇总(不滚动!会越来越大)
# -rw-r----- 1 tomcat tomcat 1.1K Jun 11 15:20 localhost.2026-06-11.log  ← 应用部署/卸载日志
# -rw-r----- 1 tomcat tomcat  328 Jun 11 15:20 localhost_access_log.2026-06-11.txt ← HTTP 访问日志
文件 内容 注意事项
catalina.out JVM stdout/stderr 汇总,不自动滚动 生产必须配 logrotate! 否则会撑满磁盘
catalina.YYYY-MM-DD.log Tomcat 内部日志,按日滚动 正常
localhost_access_log HTTP 请求日志(Combined 格式) 可替换为 Nginx access log

11.2 配置 logrotate 防止日志撑满磁盘

bash 复制代码
cat > /etc/logrotate.d/tomcat << 'EOF'
/opt/tomcat/logs/catalina.out {
    daily
    rotate 14          # 保留 14 天
    missingok
    compress           # 压缩旧日志
    delaycompress      # 延迟一天压缩(当天日志还在写)
    notifempty
    copytruncate       # 复制后截断(不需要重启 Tomcat)
}
EOF

11.3 常用日志查看命令

bash 复制代码
# 实时跟踪主日志
tail -f /opt/tomcat/logs/catalina.out

# 过滤 ERROR 级别
grep -i 'error\|exception\|severe' /opt/tomcat/logs/catalina.out | tail -20

# 查看某个应用的部署日志
grep 'helloapp' /opt/tomcat/logs/catalina.out

# 查看 HTTP 访问日志(实时)
tail -f /opt/tomcat/logs/localhost_access_log.$(date +%Y-%m-%d).txt

# 用 journalctl 查看 systemd 日志(含 stdout/stderr)
journalctl -u tomcat -f --no-pager
journalctl -u tomcat --since "1 hour ago"

十二、性能调优

12.1 JVM 参数调优速查

bash 复制代码
# /opt/tomcat/bin/setenv.sh 中的 CATALINA_OPTS
# 按服务器内存规格推荐值:

# 2GB 内存(本实验环境)
CATALINA_OPTS="-server -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"

# 4GB 内存
CATALINA_OPTS="-server -Xms1g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

# 8GB 内存(生产推荐加 GC 日志)
CATALINA_OPTS="-server -Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
               -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
               -Xlog:gc*:file=/opt/tomcat/logs/gc.log:time,uptime:filecount=10,filesize=10m"
参数 说明 建议值
-Xms 堆初始大小 = -Xmx(避免运行时扩容开销)
-Xmx 堆最大大小 物理内存 × 0.5(留给 OS 和 Metaspace)
-XX:MetaspaceSize 初始 Metaspace 128m~256m
-XX:MaxMetaspaceSize 最大 Metaspace 256m~512m(防止类泄漏)
-XX:+UseG1GC 使用 G1 垃圾收集器 Java 9+ 推荐,低延迟
-XX:MaxGCPauseMillis G1 目标 GC 停顿时间 200ms(平衡吞吐和延迟)

12.2 Tomcat 线程池调优(server.xml)

xml 复制代码
<Connector port="8080" protocol="HTTP/1.1"
           maxThreads="300"        <!-- 最大工作线程(每线程约 256KB 栈)-->
           minSpareThreads="25"    <!-- 最小空闲线程 -->
           acceptCount="100"       <!-- 连接等待队列长度 -->
           connectionTimeout="20000"
           keepAliveTimeout="15000" <!-- HTTP keep-alive 超时 -->
           maxKeepAliveRequests="100"
           compression="on"
           compressionMinSize="2048"
           compressibleMimeType="text/html,text/xml,text/plain,application/json"/>

十三、踩坑总结

# 问题现象 根因 解决方案
1 Tomcat 10 报 ClassNotFoundException: javax.servlet.http.HttpServlet Tomcat 10 用 jakarta.* 包,旧代码用 javax.* 升级依赖或用 Eclipse Transformer 迁移
2 wget 下载超时(20KB/s) 华为云香港节点直连 Apache 官网慢 改用华为开源镜像 mirrors.huaweicloud.com
3 Tomcat 启动需要 30+ 秒 SecureRandom 阻塞在 /dev/random 熵池不足 setenv.sh-Djava.security.egd=file:/dev/./urandom
4 Manager 返回 403 Forbidden 默认 RemoteAddrValve 限制只允许 localhost 覆写 webapps/manager/META-INF/context.xml 去掉 Valve
5 catalina.out 磁盘撑满 catalina.out 不自动滚动 配置 /etc/logrotate.d/tomcat
6 WAR 部署后 404 应用未正常解包(权限问题) chown tomcat:tomcat /opt/tomcat/webapps/*.war
7 多个应用 JSP 互相干扰 work/ 缓存未清理 systemctl stop tomcat && rm -rf /opt/tomcat/work/* && systemctl start tomcat
8 Cannot find javac 安装了 JRE 而非 JDK 改装 openjdk-17-jdk-headless

十四、常用运维命令速查

bash 复制代码
# ====== 服务管理 ======
systemctl start tomcat      # 启动
systemctl stop tomcat       # 停止(优雅关闭,等待请求完成)
systemctl restart tomcat    # 重启
systemctl reload tomcat     # 不支持,需 restart
systemctl status tomcat     # 状态查看

# ====== 日志 ======
tail -f /opt/tomcat/logs/catalina.out                      # 实时主日志
journalctl -u tomcat -f                                    # systemd 日志流
grep -E 'ERROR|SEVERE|Exception' /opt/tomcat/logs/catalina.out  # 过滤错误

# ====== 应用管理 ======
# 部署(热部署)
cp myapp.war /opt/tomcat/webapps/ && chown tomcat:tomcat /opt/tomcat/webapps/myapp.war

# 卸载(删除 war 和解包目录)
rm -f /opt/tomcat/webapps/myapp.war
rm -rf /opt/tomcat/webapps/myapp/

# 查看已部署应用
ls /opt/tomcat/webapps/

# ====== 调试 ======
# 查看 JVM 线程堆栈(thread dump)
kill -3 $(pgrep -f catalina)   # 输出到 catalina.out
# 或
jstack $(pgrep -f catalina) | head -100

# 查看 JVM 内存使用
jstat -gcutil $(pgrep -f catalina) 5000 10   # 每5秒采样,共10次

# 查看打开的文件描述符数量
ls -la /proc/$(pgrep -f catalina)/fd | wc -l

# ====== 端口检查 ======
ss -tlnp | grep -E ':80|:8080|:8005|:8443'
lsof -i :8080

# ====== 版本信息 ======
cat /opt/tomcat/RELEASE-NOTES | head -5
/opt/tomcat/bin/catalina.sh version 2>/dev/null || java -cp /opt/tomcat/lib/catalina.jar org.apache.catalina.util.ServerInfo

十五、附录

15.1 Tomcat 目录结构完整速查

复制代码
/opt/tomcat/
├── bin/
│   ├── startup.sh          # 启动(调用 catalina.sh start)
│   ├── shutdown.sh         # 停止(调用 catalina.sh stop)
│   ├── catalina.sh         # 核心控制脚本
│   ├── setenv.sh           # ✅ 自建:JVM 参数配置
│   ├── bootstrap.jar       # Tomcat 启动引导程序
│   └── tomcat-juli.jar     # 日志框架
├── conf/
│   ├── server.xml          # 核心:端口、Connector、Host 配置
│   ├── tomcat-users.xml    # ✅ 已改:Manager 用户/权限
│   ├── web.xml             # 全局 Servlet 配置(DefaultServlet、JspServlet)
│   ├── context.xml         # 全局 Context 配置(数据源、Session)
│   └── logging.properties  # 日志级别配置
├── lib/
│   ├── servlet-api.jar     # Jakarta Servlet 6.0 API(编译时依赖)
│   ├── jsp-api.jar         # Jakarta Pages 3.1 API
│   └── catalina.jar        # Tomcat 容器核心类
├── logs/                   # 运行时日志
├── webapps/
│   ├── ROOT/               # 默认首页(/ 路径)
│   ├── manager/            # ✅ 管理控制台(/manager/html)
│   ├── host-manager/       # Host 管理(/host-manager/html)
│   ├── examples/           # 官方示例(生产建议删除)
│   ├── docs/               # 文档(生产建议删除)
│   └── helloapp/           # ✅ 我们部署的示例应用
├── work/
│   └── Catalina/localhost/ # JSP 编译缓存(按应用隔离)
└── temp/
    └── tomcat.pid          # PID 文件

15.2 访问地址汇总

地址 说明 账号
http://1.92.101.204/ Tomcat 默认首页(Nginx 反代) ---
http://1.92.101.204/helloapp/hello 示例 JSON 接口 ---
http://1.92.101.204:8080/manager/html Tomcat Manager 控制台 tomcat / Tomcat@2026
http://1.92.101.204:8080/host-manager/html Host Manager tomcat / Tomcat@2026

15.3 生产加固 Checklist

  • 删除 webapps/exampleswebapps/docs(减少攻击面)
  • 修改 server.xml8005 关闭端口为随机端口(防 SHUTDOWN 攻击)
  • tomcat-users.xml 中密码使用强密码并加密存储
  • Manager 用 Nginx allow/deny 限制可访问 IP
  • 配置 HTTPS(Nginx SSL 终止 + 强制 HTTP→HTTPS 跳转)
  • 配置 logrotate 防止 catalina.out 撑满磁盘
  • 设置 autoDeploy="false"(生产环境,防止意外部署)
  • 启用 HTTP Security Headers(Nginx 层配置)
相关推荐
zavoryn1 小时前
Jackson 序列化踩坑:LocalDateTime、Long 精度丢失和 boolean isXxx 字段
java·开发语言·后端
曹牧1 小时前
Java:XML转义
xml·java·开发语言
心之伊始1 小时前
Dubbo 3 Consumer 调用链路源码分析:从 Proxy 到 Cluster、Directory、Router、LoadBalance
java·微服务·dubbo·源码分析·服务治理
我认不到你1 小时前
【开源、教程】RAG全流程实现(java+完整代码):第一弹
java·开发语言·人工智能·深度学习·ai·语言模型·开源
sbjdhjd1 小时前
企业级 Tomcat (上):WEB 技术栈 + 架构演进 + 生产级安装部署
linux·运维·云原生·开源·tomcat·云计算·负载均衡
程序员小羊!1 小时前
16 JAVA MySQL 8.0
java·开发语言·mysql
放下华子我只抽RuiKe51 小时前
FastAPI 全栈后端(五):后台任务与消息队列
前端·javascript·react.js·ai·前端框架·fastapi·ai编程
丷丩1 小时前
MapLibre GL JS第44课:生成并添加缺失图标
前端·javascript·gis·mapblibre gl js
ywl4708120871 小时前
IDEA 集成 Claude Code (Beta)
java·ide·intellij-idea