从一次线上排查谈 systemd 服务守护:为什么你的进程"活着"但服务"不可用
【摘要】 ## 前言在自托管服务的日常运维中,有一个现象几乎每个开发者都会遇到:进程还在跑,`ps` 里能看到,但服务就是访问不通。最近我在维护一套基于 pm2 和 systemd 托管的多服务环境时,又踩了一遍这个坑,索性把排查思路和守护配置的最佳实践整理成文,希望帮你少走弯路。## 一、问题现场某天发现托管在云服务器上的一个 Web 服务突然 502。登录服务器检查:```bashps aux |...
## 前言
在自托管服务的日常运维中,有一个现象几乎每个开发者都会遇到:进程还在跑,`ps` 里能看到,但服务就是访问不通。最近我在维护一套基于 pm2 和 systemd 托管的多服务环境时,又踩了一遍这个坑,索性把排查思路和守护配置的最佳实践整理成文,希望帮你少走弯路。
## 一、问题现场
某天发现托管在云服务器上的一个 Web 服务突然 502。登录服务器检查:
```bash
ps aux | grep myapp # 进程存在,PID 正常
curl -i http://127.0.0.1:8000/health # connection refused
```
进程活着,端口却没人监听——这就是典型的"僵尸服务"。
## 二、三层排查法
遇到"进程在、服务不通",我习惯按三层从内向外排查:
### 第 1 层:端口是否在监听
```bash
ss -tlnp | grep 8000
```
没有输出说明应用虽然启动了,但监听失败。常见原因:
- **端口被占用**:另一个旧进程还占着端口,新进程绑定失败;
- **绑定地址错误**:配置成了 `127.0.0.1` 而外部需要 `0.0.0.0`;
- **启动顺序问题**:依赖的数据库没就绪,应用启动时抛异常但被 nohup 吞掉了。
### 第 2 层:日志是否被吞掉
用 `nohup app >/dev/null 2>&1 &` 启动的服务,报错信息直接进了黑洞。这也是我后来全面改用 systemd / pm2 托管的直接原因——**守护工具的价值不只是自动重启,更是强制保留 stdout/stderr**。
### 第 3 层:回源链路是否正确
如果本机 curl 通了但公网不通,问题在反向代理或隧道层。以 Cloudflare Tunnel 为例:
- **530**:隧道本身没连上(cloudflared 挂了或 token 失效);
- **502**:隧道通,但回源地址/端口配置错了。
记住这两个状态码的语义差异,能把排查范围直接砍掉一半。
## 三、systemd 守护配置模板
这是我目前在用的服务单元配置,几个关键点都加了注释:
```ini
[Unit]
Description=My Web App
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStartPre=/opt/myapp/precheck.sh
ExecStart=/opt/myapp/app -c config.json
Restart=always
RestartSec=3
# 关键:日志进 journald,绝不静默丢弃
StandardOutput=journal
StandardError=journal
# 启动失败判定:10 秒内崩溃超过 5 次就放弃重启,避免疯狂循环
StartLimitIntervalSec=10
StartLimitBurst=5
[Install]
WantedBy=multi-user.target
```
其中 `ExecStartPre` 是我的预检脚本,核心逻辑是**启动前逐端口探测占用**:
```bash
#!/bin/bash
PORT=8000
if ss -tln | grep -q ":${PORT} "; then
echo "FATAL: port ${PORT} already in use" >&2
exit 1
fi
```
这个十几行的脚本,帮我消灭了 90% 的"静默失败"。
## 四、常用运维命令速查
```bash
# 查看服务状态与最近日志
systemctl status myapp
journalctl -u myapp -n 100 --no-pager
# 实时跟踪日志
journalctl -u myapp -f
# 改完配置后
systemctl daemon-reload && systemctl restart myapp
# 开机自启
systemctl enable myapp
```
pm2 用户对应的等价命令:
```bash
pm2 list # 注意看 ↺ 重启次数,重启次数暴涨 = 崩溃循环
pm2 logs myapp --lines 100
pm2 startup # 生成开机自启配置
pm2 save # 固化当前进程列表
```
## 五、总结
三句话概括这次的经验:
1. **"进程在跑"≠"服务可用"**,端口监听验证(`ss -tlnp`)是排障第一课;
2. **永远不要用 nohup + /dev/null 启动生产服务**,日志进 journald 或 pm2;
3. **启动前预检 + 崩溃自动重启 + 日志可查**,三件套配齐,服务稳定性上一个台阶。
---
*本文为原创内容,首发于华为云开发者社区。*
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)