020-29876379

常见问题

 一、背景:为什么 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` 却能通

我们可以先通过下面这条命令对外网链路做一个基线确认,把"是不是网络问题"先排除掉:

```bash
curl -I https://www.wangzhanjianshe9.com.cn
```

如果外网正常返回 `HTTP/1.1 200 OK`,那就说明问题集中在服务器内部,可以放心地按下面的清单逐项排查。

---

二、定位失败的入口:systemd 与三类日志

排查 Tomcat 启动问题,第一步永远不是去重启,而是先把日志看全。在 AliOS 3 上有三个必看的入口:

```bash
# 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` 里写死了短路径,启动直接报错。

```bash
# 确认真实 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` 中以下两行是必须的:

```bash
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export LD_LIBRARY_PATH=$JAVA_HOME/lib/server:$LD_LIBRARY_PATH
```

修正后再次启动并验证:

```bash
sudo systemctl restart tomcat
curl -I http://127.0.0.1:8080/
```

---

四、用户与权限问题

`tomcat.service` 里指定了 `User=tomcat`、`Group=tomcat`。AliOS 3 默认并不存在这个用户,需要手工创建:

```bash
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 参数运行,行为异常。修复:

```bash
sudo chown tomcat:tomcat /opt/tomcat9/bin/setenv.sh
sudo chmod 0755 /opt/tomcat9/bin/setenv.sh
```

权限问题修复后,`temp`、`logs`、`work` 三个目录必须可写,否则 Tomcat 在解压 webapps 时会失败:

```bash
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 |

清理动作:

```bash
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+),对一些写法更严格。下面是经过实战验证的规范模板:

```ini
[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
```

四个最容易踩坑的点:

1. `Environment` 行的等号两边**不能有空格**
2. `ExecStart` 必须是绝对路径
3. `Type=forking` 一定要配套 `PIDFile=`
4. 改完任何 service 文件,都必须 `daemon-reload`:

```bash
sudo systemctl daemon-reload
sudo systemctl restart tomcat
```

---

七、SELinux、防火墙与云安全组

AliOS 3 默认 SELinux 是 **enforcing** 模式。Tomcat 装在 `/opt` 下、监听 8080,一般不会被拦;但如果你把 webapps 挪到 `/data`、`/home/xxx`,就可能因为 context 不对导致 403:

```bash
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:

```bash
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload
```

云厂商安全组同样要放行 8080,否则浏览器访问不通,但服务器本机 `curl` 仍然 OK。最稳妥的做法是分两层验证:

```bash
# 内网验证(绕过防火墙/安全组)
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 的云主机推荐:

```bash
export JAVA_OPTS="-Dfile.encoding=UTF-8 -Djava.awt.headless=true \
  -Xms1024m -Xmx3072m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
```

并确认 swap 不为 0:

```bash
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。

清理方式:

```bash
sudo rm -rf /opt/tomcat9/webapps/ROOT/export/sites/<站点目录>/*
sudo systemctl restart tomcat
```

---

十、完整启动验证流程

排查完成后,按以下顺序做端到端验证,任何一步异常都不要进入下一步:

```bash
# 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 的网站架构真正稳定地服务于用户。