Appearance
Tomcat 部署
Tomcat 是一个 Servlet 容器,用来运行 Java Web 应用。它和 Nginx 的定位不同——Nginx 处理静态文件和反向代理,Tomcat 负责加载 Java 应用、处理 Servlet 逻辑、执行 Java 代码后返回响应。
典型链路:
text
client → nginx → tomcat → java web application一、几个核心对象
| 对象 | 做什么 | 运维关注什么 |
|---|---|---|
| JDK/JRE | Java 运行环境 | 版本、路径、JVM 内存参数 |
| Tomcat | Servlet 容器 | 端口、线程、日志、部署目录 |
| WAR | Java Web 应用包 | 发布包路径、解压目录、回滚版本 |
| Connector | Tomcat 接收 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.sh、shutdown.sh、catalina.sh) |
conf/ | 配置文件(server.xml、web.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" />字段含义:
| 字段 | 做什么 |
|---|---|
port | Tomcat 监听端口 |
protocol | HTTP 协议处理实现 |
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 status 和 journalctl 里的 Java 路径错误,比直接翻 catalina.out 更快定位到启动失败原因。
CATALINA_HOME 和 CATALINA_BASE 的区别:
| 变量 | 指向什么 | 典型内容 |
|---|---|---|
CATALINA_HOME | Tomcat 程序目录 | 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.warTomcat 默认会自动解压并部署。访问路径由 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.warContext 配置(/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.log | Tomcat 自身的 Catalina 引擎日志 |
logs/localhost.YYYY-MM-DD.log | 应用上下文相关的日志 |
logs/localhost_access_log.*.txt | Tomcat 的访问日志 |
查看:
bash
tail -f /opt/tomcat/logs/catalina.out
tail -f /opt/tomcat/logs/localhost_access_log.*.txt
journalctl -u tomcat -n 100 --no-pagercatalina.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 tomcat、catalina.out |
| WAR 不解压 | 权限不足、磁盘满、WAR 包损坏 | logs/、webapps/ 目录权限 |
| 访问 404 | Context 路径不对、Nginx 代理路径不匹配 | Nginx access log + Tomcat access log |
| 访问 502 | Nginx 连不上 Tomcat | ss -lntp 确认 8080 在监听 |
| 启动很慢 | 应用初始化慢、数据库连接池初始化慢 | 应用日志、网络连通性 |
| 改配置不生效 | 没 reload/restart、改错了 CATALINA_BASE 目录 | systemctl status、启动参数确认 |
每次发布记录中保留:Java 版本、Tomcat 版本、WAR 包构建号、配置变更内容、JVM 启动参数和回滚包的路径。Java 应用出问题时,这些信息经常比"服务启动了还是没启动"更关键。