从一次线上排查谈 systemd 服务守护:为什么你的进程"活着"但服务"不可用

举报
yd_242888859 发表于 2026/10/05 15:16:08 2026/10/05
【摘要】 ## 前言在自托管服务的日常运维中,有一个现象几乎每个开发者都会遇到:进程还在跑,`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)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。