Linux 服务启动顺序如何控制?

12次阅读

systemd通过After=和Requires=声明依赖关系控制启动顺序,After=仅表示时间先后,Requires=才是硬性前提;漏写Requires=易致服务因依赖失败而异常,应优先修改/etc/下的单元文件并执行daemon-reload。

Linux 服务启动顺序如何控制?

After=Requires= 控制服务启动先后

systemd 不靠“谁排第几个”决定顺序,而是靠依赖声明。你写 After=network.target,只表示“我比 network.target 启动得晚”,但不保证 network.target 一定成功;而加了 Requires=network.target,就变成硬性前提——如果 network.target 启动失败,你的服务压根不会尝试启动。

常见错误是只写 After= 却漏掉 Requires=,结果网络还没通,你的服务就急着连数据库,直接超时失败。反过来,对非关键服务(比如日志轮转),应避免滥用 Requires=,改用 Wants= 降低耦合。

  • 查当前服务的依赖:运行 systemctl show nginx.service -p Wants,Requires,After
  • 修改前先备份原文件:sudo cp /usr/lib/systemd/system/nginx.service /etc/systemd/system/nginx.service(优先改 /etc/ 下副本)
  • 改完必须执行 sudo systemctl daemon-reload,否则 enablerestart 都不生效

为什么 systemctl enable 不等于“立刻按顺序启动”

systemctl enable 只是创建软链接到 /etc/systemd/system/multi-user.target.wants/,让服务被 multi-user.target 拉起——但它不指定“在哪个时刻启动”,真正顺序由单元文件里的 After=Wants= 等字段决定。很多人禁用一个服务后发现另一个服务也起不来,其实是后者在单元文件里写了 Requires= 前者,而非启动脚本里有硬编码调用。

  • 检查某服务是否真被启用:systemctl is-enabled servicename(输出 enabled 才算)
  • 确认它是否实际参与了本次启动:systemctl status servicenameLoaded: 行末尾是否带 enabled,且 Active:active (running)
  • 禁用服务用 sudo systemctl disable servicename,不是删文件,也不是改 WantedBy= 字段

可视化启动瓶颈:用 systemd-analyze plot 找卡点

系统启动慢,不一定是某个服务本身慢,更可能是它前面一服务串行堵着。比如 postgresql.service 被设为 After=network.target,但 network.target 又等 systemd-networkd-wait-online.service(默认超时 2 分钟),结果整个数据库启动被拖垮。

  • 生成 svg 启动时序图:systemd-analyze plot > boot.svg,用浏览器打开就能看到哪段横条最长、哪些服务并行度低
  • 快速定位耗时大户:systemd-analyze blame 列出各服务启动耗时(单位 ms),排第一的未必是问题根源,但值得优先看它的 After= 依赖链
  • 若某服务只需网络“可达”而非“完全在线”,把 After=network-online.target 改成 After=network.target,再加 Wants=network.target,能跳过等待 DHCP 的长延迟

别碰 /etc/rc.local —— 它和 systemd 有隐式冲突

很多老教程教你在 /etc/rc.local 里写启动命令,但在 systemd 系统中,这个文件本质是通过 rc-local.service 单元来加载的,它默认 After=multi-user.target,但没声明 Wants= 任何具体服务。结果就是:你的脚本可能在 mysql 还没起来时就去连库,或者在 docker socket 尚未就绪时就执行 docker run,报错 connection refused

  • 替代方案:写一个专用 .service 文件,明确写 After=mysqld.serviceRequires=mysqld.service
  • 如果只是想开机跑一条命令,用 systemd-run --on-boot --scope --unit=my-onboot-script /path/to/script.sh(需 systemd v249+)
  • 检查 rc-local.service 是否启用:systemctl is-enabled rc-localubuntu 22.04+ 默认已禁用,强行启用反而易出问题

最常被忽略的一点:服务单元文件里写的 After= 是“相对顺序”,不是“绝对时间点”。哪怕你把所有服务都设成 After=multi-user.target,systemd 仍会按依赖图自动调度并行度——所以别迷信“我把 A 改成 After=B,B 就一定先于 A 启动”,最终顺序还得看整个依赖网如何收敛。

text=ZqhQzanResources