Tomcat启动失败排查:AliOS 3系统下完整解决方案
一、背景:为什么 AliOS 3 上的 Tomcat 9 启不来
在企业网站部署、Java 后台系统、OpenCMS 内容管理平台中,Apache Tomcat 9 是最主流的 Web 容器。我们的标准部署流程是:在 Alibaba Cloud Linux 3(基于 OpenAnolis 内核,与 RHEL 8 / CentOS 8 生态兼容)上,把 Tomcat 9.0.89 解压到 /opt/tomcat9,通过 systemd 托管服务,并在 setenv.sh 中统一管理 JVM 参数(-Xms1024m -Xmx3072m -XX:+UseG1GC -XX:MaxGCPauseMillis=200),最终让 8080 端口对外提供服务。

理论上,sudo systemctl enable --now tomcat 一条命令就能让服务跑起来。但在实际工作中,AliOS 3 系统下的启动失败案例非常高频,常见的现象有:
Active: failed (Result: exit-code),主进程瞬间退出PID file ... not readable (yet?) after start- 服务显示
active,但ss -lntp | grep 8080没有任何输出 - 浏览器访问超时,源站
curl却能通
我们可以先通过下面这条命令对外网链路做一个基线确认,把"是不是网络问题"先排除掉:
curl -I https://www.wangzhanjianshe9.com.cn
如果外网正常返回 HTTP/1.1 200 OK,那就说明问题集中在服务器内部,可以放心地按下面的清单逐项排查。
二、定位失败的入口:systemd 与三类日志
排查 Tomcat 启动问题,第一步永远不是去重启,而是先把日志看全。在 AliOS 3 上有三个必看的入口:
# 1. systemd 视角的服务状态
sudo systemctl status tomcat -l --no-pager
# 2. systemd 完整日志(含错误堆栈)
sudo journalctl -u tomcat -e --no-pager
# 3. Tomcat 自身的标准输出
sudo tail -n 200 /opt/tomcat9/logs/catalina.out
绝大多数的根因都能在这三处中的某一处找到关键字。下面按出现频率从高到低,逐一拆解。
三、JDK 环境问题:JAVA_HOME 与 libjvm.so
AliOS 3 默认仓库里 OpenJDK 17 的实际安装路径不一定是 /usr/lib/jvm/java-17-openjdk,而往往是带版本号的 java-17-openjdk-17.0.12.0.7-1.al8.x86_64。如果 tomcat.service 里写死了短路径,启动直接报错。
# 确认真实 JDK 路径
sudo alternatives --list | grep java
ls -ld /usr/lib/jvm/java-17-openjdk*
# 推荐做一个稳定软链接
sudo ln -sfn /usr/lib/jvm/java-17-openjdk-17.0.* /usr/lib/jvm/java-17-openjdk
另一个隐蔽问题是 LD_LIBRARY_PATH 没有指向 $JAVA_HOME/lib/server,catalina.out 中会出现 error while loading shared libraries: libjvm.so。setenv.sh 中以下两行是必须的:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export LD_LIBRARY_PATH=$JAVA_HOME/lib/server:$LD_LIBRARY_PATH
修正后再次启动并验证:
sudo systemctl restart tomcat
curl -I http://127.0.0.1:8080/
四、用户与权限问题
tomcat.service 里指定了 User=tomcat、Group=tomcat。AliOS 3 默认并不存在这个用户,需要手工创建:
id tomcat || sudo useradd -r -m -U -d /opt/tomcat9 -s /sbin/nologin tomcat
sudo chown -R tomcat:tomcat /opt/tomcat9
sudo find /opt/tomcat9/bin -type f -name "*.sh" -exec chmod +x {
} \;
特别要注意 setenv.sh 经常是用 sudo tee 创建的,属主是 root,但 systemd 服务以 tomcat 身份启动,会读不到环境变量,最终以默认 JVM 参数运行,行为异常。修复:
sudo chown tomcat:tomcat /opt/tomcat9/bin/setenv.sh
sudo chmod 0755 /opt/tomcat9/bin/setenv.sh
权限问题修复后,temp、logs、work 三个目录必须可写,否则 Tomcat 在解压 webapps 时会失败:
sudo chmod -R u+rwX /opt/tomcat9/{
temp,logs,work}
五、PID 文件残留与 8080 端口占用
Type=forking 模式依赖 catalina.pid 来判断进程状态。当 Tomcat 上一次是被强杀(kill -9 或服务器异常断电),temp/catalina.pid 会留下来,下次启动时 systemd 看到 PID 文件已存在就拒绝启动。
| 报错关键字 | 根因 | 解决命令 |
|---|---|---|
PID file ... not readable |
残留 catalina.pid | sudo rm -f /opt/tomcat9/temp/catalina.pid |
Address already in use |
8080 被占用 | sudo lsof -i:8080 找到进程后 kill -15 |
Permission denied (port 80) |
非 root 绑定特权端口 | 用反代或 setcap |
清理动作:
sudo systemctl stop tomcat
sudo rm -f /opt/tomcat9/temp/catalina.pid
sudo ss -lntp | egrep ':8080|:80' || true
sudo systemctl start tomcat
六、systemd 服务配置易错点
AliOS 3 上 systemd 版本较新(239+),对一些写法更严格。下面是经过实战验证的规范模板:
[Unit]
Description=Apache Tomcat 9
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk"
Environment="CATALINA_HOME=/opt/tomcat9"
Environment="CATALINA_BASE=/opt/tomcat9"
Environment="CATALINA_PID=/opt/tomcat9/temp/catalina.pid"
PIDFile=/opt/tomcat9/temp/catalina.pid
ExecStart=/opt/tomcat9/bin/startup.sh
ExecStop=/opt/tomcat9/bin/shutdown.sh
SuccessExitStatus=143 0
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
四个最容易踩坑的点:
Environment行的等号两边不能有空格ExecStart必须是绝对路径Type=forking一定要配套PIDFile=- 改完任何 service 文件,都必须
daemon-reload:
sudo systemctl daemon-reload
sudo systemctl restart tomcat
七、SELinux、防火墙与云安全组
AliOS 3 默认 SELinux 是 enforcing 模式。Tomcat 装在 /opt 下、监听 8080,一般不会被拦;但如果你把 webapps 挪到 /data、/home/xxx,就可能因为 context 不对导致 403:
ls -Z /opt/tomcat9/webapps/ROOT/index.html
sudo chcon -R -t httpd_sys_content_t /data/webapps
sudo setsebool -P httpd_can_network_connect 1
防火墙方面,AliOS 3 默认启用 firewalld:
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload
云厂商安全组同样要放行 8080,否则浏览器访问不通,但服务器本机 curl 仍然 OK。最稳妥的做法是分两层验证:
# 内网验证(绕过防火墙/安全组)
curl -I http://127.0.0.1:8080/
# 公网验证(含防火墙/安全组/CDN 链路)
curl -I https://www.wangzhanjianshe9.com.cn
两条都 200,才算真正"对外可用"。
八、JVM 内存与 OOM Killer
setenv.sh 默认给的是 -Xms512m -Xmx1024m,对小测试机够用,但生产服务器加载几个大型 WAR 包后启动就会触发 OOM。AliOS 3 内核的 OOM Killer 工作很积极,dmesg 里能看到:
Out of memory: Killed process xxxx (java)
按经验,4 核 8G 的云主机推荐:
export JAVA_OPTS="-Dfile.encoding=UTF-8 -Djava.awt.headless=true \
-Xms1024m -Xmx3072m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
并确认 swap 不为 0:
free -h
sudo swapon --show
| 服务器规格 | 推荐 -Xms | 推荐 -Xmx |
|---|---|---|
| 2核4G | 512m | 1536m |
| 4核8G | 1024m | 3072m |
| 8核16G | 2048m | 6144m |
九、webapps 部署冲突
最容易被忽视的失败是部署残留。OpenCMS、企业站静态导出后,webapps/ROOT/export/sites 下常会留下海量旧文件,重启时 Tomcat 重新扫描应用,遇到损坏的 web.xml 或重复的 context 就会让 ROOT 应用启动失败。表现是 systemctl 看着 active,但 8080 不监听,logs/catalina.<date>.log 中大量 SEVERE。
清理方式:
sudo rm -rf /opt/tomcat9/webapps/ROOT/export/sites/<站点目录>/*
sudo systemctl restart tomcat
十、完整启动验证流程
排查完成后,按以下顺序做端到端验证,任何一步异常都不要进入下一步:
# 1. 重新加载 systemd 配置
sudo systemctl daemon-reload
# 2. 重启 Tomcat
sudo systemctl restart tomcat
# 3. 查看服务状态
sudo systemctl status tomcat -l --no-pager
# 4. 确认端口监听
sudo ss -lntp | grep ':8080'
# 5. 本机回源验证
curl -I http://127.0.0.1:8080/
# 6. 公网域名验证
curl -I https://www.wangzhanjianshe9.com.cn
如果你前面接了 Nginx 做反向代理,还要检查 Nginx 是否将 localhost 解析到了 IPv6(::1),而 Tomcat 默认只监听 127.0.0.1(IPv4),这是 AliOS 3 上一个非常高频的"看起来都对,就是 502"的坑。Nginx 配置全局把 localhost 替换成 127.0.0.1 即可解决。
总结
Tomcat 启动失败在 AliOS 3 系统上看起来千奇百怪,但归根到底跳不出七类:JDK 路径、用户权限、PID 残留、systemd 配置、防火墙/SELinux、JVM 内存、webapps 冲突。掌握上面的排查路径,再配合 catalina.out、journalctl -u tomcat -e 两个日志入口,绝大多数线上故障都可以在十分钟内定位。
对于以网站为业务核心的企业来说,Tomcat 是流量进入业务逻辑的第一道闸口,一次启动失败往往意味着访问中断、搜索引擎抓取失败甚至排名下降。把这套排查清单沉淀为运维手册的一部分,可以显著降低重复故障对业务的冲击,让基于 Tomcat 9 + AliOS 3 的网站架构真正稳定地服务于用户。