Linux 服务启动顺序配置实战

4次阅读

systemd服务启动顺序依赖需用After=与Wants=/Requires=组合:After=仅表时序,Wants=为弱依赖,Requires=为强依赖;避免循环依赖;验证须用systemd-analyze critical-chain等命令查真实链路,而非仅看配置。

Linux 服务启动顺序配置实战

systemd 服务启动顺序依赖怎么写

linux 现在基本都用 systemd,控制启动顺序靠的是依赖关系,不是传统 SysV 的数字编号。写错依赖会导致服务起不来,或者起得太早——比如数据库还没监听,Web 服务就去连它,报 Connection refused

关键不是“谁先谁后”,而是“谁等谁”。用 After=Wants=(或 Requires=)组合才有效:

  • After=network.target 只表示时间先后,不保证网络已就绪;真要等网卡配好,得加 Wants=network-online.target + After=network-online.target
  • Requires=redis.service 表示强依赖:如果 redis.service 启动失败,本服务直接 abort,不会尝试启动
  • Wants=redis.service 是弱依赖:redis.service 启动失败不影响本服务继续,但本服务仍会在它之后启动(配合 After=
  • 避免循环依赖:A Wants=B + After=BB Wants=A + After=A 会让 systemd 拒绝加载单元文件,报错 Unit A has a dependency cycle with B

如何验证服务实际启动顺序

光看配置不顶用,得查真实执行流。最直接的方式是看启动时序图:

systemd-analyze plot > boot.svg

打开 boot.svg 就能看到所有服务横向排布、启动/结束时间轴。但更常用的是过滤关键服务:

  • systemd-analyze blame 查耗时最长的服务(常暴露隐性阻塞)
  • systemd-analyze critical-chain myapp.service 显示从 default.targetmyapp.service 的完整依赖链,每一步是否成功、耗时多少一目了然
  • journalctl -u myapp.service -u postgresql.service --since "1 hour ago" 对比日志时间戳,确认 PostgreSQL 是否真在 myapp.service 启动前 ready

注意:systemd-analyze 默认只分析上次 boot,重启后数据刷新,别拿旧结果当依据。

自定义服务里误用 Type=forking 导致顺序错乱

很多老脚本习惯后台化(&daemonize=yes),写 unit 文件时就随手设 Type=forking。但 systemd 对它的识别很脆弱:

  • 必须严格指定 PIDFile=,且进程真要写这个文件;否则 systemd 认为服务“没起来”,后续依赖服务永远等不到它
  • 如果程序 fork 多次(比如 supervisord 起多个子进程),PIDFile 里写的可能不是主进程 PID,systemd 会误判状态
  • 推荐优先用 Type=simple(默认):只要主进程没 exit,就认为服务 running;适合现代 Go/python 服务(不 daemonize,前台运行)
  • 实在要用 forking,务必加 TimeoutSec=(比如 TimeoutSec=60),防止 systemd 卡死等待一个永远不会出现的 PID 文件

容器或云环境里 network-online.target 不可靠

docker、LXC、AWS EC2 等环境,network-online.target 经常假成功:网卡起来了,但 DHCP 还没回包、dns 没通、甚至元数据服务(如 169.254.169.254)不可达。这时候硬等它,服务反而启动更慢甚至超时失败。

更稳妥的做法是让服务自己重试,而不是靠 systemd 等网络:

  • 去掉 Wants=network-online.target,改用 After=multi-user.target
  • 在服务启动命令里包装一层重试逻辑,比如:
    ExecStart=/usr/bin/sh -c 'until nc -z localhost 5432; do sleep 2; done && /usr/local/bin/myapp'
  • 或者用 systemd 原生的重启机制:Restart=on-failure + RestartSec=3,配合应用内连接池的重试,比外部等网络更健壮

真正难处理的不是“怎么配顺序”,而是“什么时候不该配顺序”——多数场景下,让服务自己应对依赖未就绪,比让 init 系统强行串行更可靠。

text=ZqhQzanResources