systemd 服务起不来?五步排查法

交付给一线运维:一条服务重启了十几次、systemctl start 只回一句 Job for app.service failed, journalctl 里翻半天只有一行 exited。其实 systemd 的失败信息非常规范,按五步走, 从"现象分类"到"读到根因",五分钟内能定位到具体那一行配置。

一、先分清三种"起不来"

不同失败形态,排查入口完全不同,先归类再动手:

  1. 根本起不来 :systemctl start 立刻返回失败,进程连创建都没成功。多为 ExecStart 路径、权限、解释器问题,退出码常见 203/EXEC。
  2. 起来又马上退出 :能启动一秒,然后进程自己崩。表现为 Restart=always 不断重启,状态里是 activating (auto-restart),多半是应用自身配置或依赖问题。
  3. 起来了但状态不对 :Active: active (running),可端口不通、健康检查失败。这是 Type= 与就绪判定不匹配,或进程 daemon 化后 systemd 跟丢了。

二、第 1 步:systemctl status 一眼读四行

bash 复制代码
systemctl status app.service --no-pager

重点看四行:

  • Loaded: ------ unit 文件路径是否正确,是否被 masked、disabled。
  • Active: ------ 失败会直接标注 (Result: exit-code) 或 (Result: timeout)。
  • Process: ------ 实际执行的命令行,以及 status=203/EXEC 这类关键退出码。
  • 末尾日志 ------ 只有最近 10 行,够用来判断方向,但不足以定根因。

203/EXEC 基本等于"这个命令 systemd 跑不起来":路径写错、没有执行权限、shelbang 坏了、脚本是 CRLF 换行。

三、第 2 步:journalctl -u 拿全量日志

bash 复制代码
# 看这个服务最近 100 行
journalctl -u app.service -n 100 --no-pager

# 只看本次开机、只看错误级别
journalctl -u app.service -b -p err --no-pager

# 缩小到出问题的时间窗
journalctl -u app.service --since "10 min ago" --no-pager

三句高频报错的含义:

  • Unit app.service not found. ------ 文件没放在 /etc/systemd/system/,或放完没执行 systemctl daemon-reload。
  • Start request repeated too quickly. ------ 重启太密被 systemd 熔断,需要 systemctl reset-failed app.service 再试。
  • Address already in use ------ 端口被别的进程占了,用 ss -lntp 找占用者。

四、第 3 步:核对 unit 文件本身

bash 复制代码
systemctl cat app.service                  # 看最终生效内容(含 drop-in 覆盖)
systemd-analyze verify /etc/systemd/system/app.service   # 语法与依赖检查
systemctl show app.service -p ExecStart -p Environment   # 看 systemd 眼中的真实值

unit 文件里最容易写错的五处:

  1. ExecStart 必须写绝对路径 ,且不能带 shell 语法(管道、&&、重定向都要包一层 bash -c)。
  2. Type= 选错 。前台常驻用 simple(默认),会 fork 后退出的老程序要 forking + PIDFile,oneshot 只跑一次不能常驻。
  3. EnvironmentFile= 路径写错会让变量全空;写成 EnvironmentFile=-/etc/app/env(前面加 -)则文件缺失不报错。
  4. User= / Group= 指定的账号对 WorkingDirectory 和日志目录没有写权限,进程会直接退出。
  5. 依赖写反 :需要网络的要 After=network-online.target 且 Wants=network-online.target,只写 After=network.target 常常起太早。

改完 unit 文件必须:

bash 复制代码
systemctl daemon-reload
systemctl restart app.service

五、第 4 步:查环境差异与资源限制

最常见的"我手工跑明明可以"------因为交互 shell 和 systemd 的环境完全不同:

  • 不加载 /etc/profile :PATH 只有极简几条,java、node 找不到。解决:ExecStart 写全路径,或在 unit 里显式 Environment=PATH=/usr/local/bin:/usr/bin。
  • WorkingDirectory 默认是 /:相对路径配置一律找不到,必须显式指定。
  • 资源上限 :LimitNOFILE、MemoryMax、TasksMax 触顶会直接把进程打掉,journalctl 里能看到 oom 或 Failed to fork。
  • 沙箱与 SELinux :ProtectSystem=strict、PrivateTmp=true 会让服务看不到宿主机路径,表现为"文件明明存在却报 no such file"。
  • 看真实上限 :systemctl show app.service -p LimitNOFILE -p MemoryMax。

六、第 5 步:排除端口与依赖冲突

bash 复制代码
ss -lntp | grep -E ':8080|:3306'      # 端口是否被占
systemctl list-dependencies app.service --reverse | head

如果服务启动后秒退,且日志停在"connecting to db...",那就是依赖的下游没起来。此时不要给应用加 Restart=always 硬扛------重启掩盖的是依赖顺序问题 ,应该在 unit 里用 After= / Requires= 声明清楚,或在应用层加重试与退避。

七、避坑清单

  1. 改完 unit 不 daemon-reload:改动完全不生效,白排查半天。
  2. 用 Restart=always 掩盖根因 :日志被反复重启刷满,真正那一行错误被冲掉。先 systemctl stop 手动跑一次。
  3. ExecStart 里用 shell 语法 :管道、&&、$VAR 都不被解析,必须 bash -lc '...'。
  4. Type=forking 却没配 PIDFile :systemd 不知道主进程是谁,状态一直 activating 直到超时。
  5. TimeoutStartSec 太短:大应用初始化要 2 分钟,默认 90 秒直接判失败,应显式放宽。
  6. 脚本 CRLF 换行 :从 Windows 传上来的启动脚本会报 203/EXEC,dos2unix 一下。
  7. 别在 /usr/lib/systemd/system/ 里直接改 :升级会被覆盖,自定义放 /etc/systemd/system/,覆盖用 drop-in(/etc/systemd/system/app.service.d/override.conf)。

小结

口诀:看状态分类型 → 翻日志找报错 → 核 unit 查语法 → 比环境与限额 → 排端口与依赖。

systemd 的报错信息其实很诚实,它只是被"重启刷屏"和"环境不一致"藏起来了。 把服务先停掉手动跑一次、把 unit 文件当成代码来 review,绝大多数"起不来"都能一眼看破。

相关推荐
LMT_Lanmantech1 小时前
HyperWorks许可:almutil显示Unit满了,数据到底准不准?
运维·服务器·算法
system_error_x1 小时前
服务器的替代方案:从物理机到云原生的全面对比
运维·服务器·云原生
努力进修1 小时前
多源异构数据库同步难题:DDL 结构变更自动化解决方案
运维·数据库·自动化
云票1 小时前
从技术视角看AI采购:多智能体、流程重设计与自动化的落地边界
运维·人工智能·自动化
wei_shuo1 小时前
极空间部署1Panel实战:应用商店装AList、Docker管理与远程访问
运维·docker·容器
朗尊软件技术团队1 小时前
电商系统Docker容器化部署实践:从Dockerfile镜像瘦身到K8s滚动上线
运维·docker·云原生·容器·kubernetes
天远数科2 小时前
零信任架构实战:基于天远柠檬查出险构建自动化二手车延保车况资质核验网关
运维·人工智能·架构·自动化
科技研学社2 小时前
服装自动化产线品质管控:机器能降不良,但不能自动兜底
运维·自动化
lambo mercy2 小时前
Day4:流式传输
运维·服务器·网络