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.sh、shutdown.sh、catalina.sh、setenv.sh(自建) |
conf/ |
核心配置:server.xml(端口/Host)、tomcat-users.xml(用户权限)、web.xml(全局Servlet) |
logs/ |
日志:catalina.out(主日志)、catalina.YYYY-MM-DD.log、localhost_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/examples、webapps/docs(减少攻击面) - 修改
server.xml的8005关闭端口为随机端口(防 SHUTDOWN 攻击) -
tomcat-users.xml中密码使用强密码并加密存储 - Manager 用 Nginx
allow/deny限制可访问 IP - 配置 HTTPS(Nginx SSL 终止 + 强制 HTTP→HTTPS 跳转)
- 配置 logrotate 防止
catalina.out撑满磁盘 - 设置
autoDeploy="false"(生产环境,防止意外部署) - 启用 HTTP Security Headers(Nginx 层配置)