重启一次要等八分钟:一次 Linux 开机慢 systemd 启动分析复盘

一台对外服务的服务器打完补丁重启,整整 8 分钟才上线,而印象里以前两三分钟就好,多次重启都稳定在 8 分钟左右不像偶发硬件问题。排查梳理:开机慢不是一个不可分割的黑盒,它是几十个服务按依赖关系启动的总和,慢一定是某一步在慢;systemd-analyze 先看 kernel 和 userspace 各占多久把问题定位到内核还是服务启动;systemd-analyze blame 把每个服务启动耗时从大到小排序,排第一的通常就是头号嫌疑;blame 是各服务各自耗时而服务多是并行起的,要看谁卡住关键路径得用 critical-chain,加号后的数字是该服务自身耗时;这次的元凶是 NetworkManager-wait-online 它的职责是阻塞等网络真正就绪,一块配了静态 IP 却没接线的废网卡让它死等到自己的 600 秒超时,把下游依赖 network-online.target 的业务服务全堵在后面;其他常见元凶有服务 ExecStart 卡住撞 TimeoutStartSec 整 90 秒、fstab 挂载不存在的设备、磁盘 fsck、依赖写得过死该并行的串行了;正确解法是对症治那个慢服务:废网卡用 nmcli 取消 autoconnect 或删掉、改短 wait-online 超时、用不到的服务 disable、顽固的 mask、挂载加 nofail,以及一套开机慢排查纪律。

2024 年,我给一台生产服务器打完安全补丁,需要重启一次。我点下重启,然后盯着监控等它回来。一分钟,没回来;两分钟,没回来;我开始有点慌——这是一台对外服务的机器,它多停一秒,就是多一秒的损失。直到第八分钟,它才终于上线。我心里一沉:八分钟。我印象里这台机器以前重启,顶多两三分钟就好了。我第一反应是机器硬件老化了,或者是磁盘自检 fsck 卡住了。可下一次我再重启它,掐着表看,还是八分钟左右——这么稳定的"慢",不像是偶发的硬件抽风,倒像是某个固定的环节,每次都在那儿磨蹭。我想看看到底是哪一步慢,可开机过程"刷刷刷"滚屏太快,我根本抓不住。我盯着这个稳定的八分钟想了很久,最后才意识到:我一直把"开机"当成一个不可分割的黑盒,我只知道它"慢",却从来没想过,开机其实是由几十个有先后、有依赖的步骤拼起来的,而 systemd 把每一步花了多少时间,都清清楚楚地记着——我只是从来没问过它。这件事逼着我把 Linux 的 systemd 启动流程、启动分析、服务依赖、target 这一整套彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,systemd 管理,一台对外服务的服务器
事故现象:
- 打完补丁重启,机器整整 8 分钟才重新上线
- ★ 印象里以前重启两三分钟就好,这次明显慢
- 多次重启都是稳定的 8 分钟左右 —— 不像偶发硬件问题

现场排查:
# 1. 看这次开机总共花了多久
$ systemd-analyze
Startup finished in 3.1s (kernel) + 7min 42s (userspace) = 7min 45s
#                                    ^^^^^^^^^ ★ 用户空间花了近 8 分钟

# 2. ★ 看每个服务各自启动花了多久,排个序
$ systemd-analyze blame | head
7min 30s  network-online.target wait / NetworkManager-wait-online
   8.2s   kdump.service
   3.1s   tuned.service
   ...                                # ★ 一个服务就占了 7 分半!

# 3. ★ 看启动的"关键链路"——到底是谁卡着谁
$ systemd-analyze critical-chain
graphical.target @7min 44s
└─multi-user.target @7min 44s
  └─myapp.service @7min 43s
    └─network-online.target @7min 42s          # ★ 卡在这
      └─NetworkManager-wait-online.service @12s +7min 30s

# 4. ★ 这个 wait-online 在等什么
$ journalctl -u NetworkManager-wait-online.service
... Timeout waiting for online state           # ★ 它在【等网络就绪】等超时了

根因(后来想清楚的):
1. ★ "开机慢"不是一个整体,它是几十个服务【串起来
   /并行】启动的总和。慢,一定是某一步在慢。
2. systemd-analyze blame 一排序就看出来:
   NetworkManager-wait-online 一个就占了 7 分半。
3. ★ 这个服务的作用是【阻塞在那,等网络真正就绪】,
   好让后面依赖网络的服务能正常起。
4. 这台机器有一块网卡配了静态 IP、但其实【没接线
   /配置有误】,永远不可能 up。wait-online 默认
   要【等所有网卡都就绪】,于是它死等,直到自己
   的超时(默认就是几分钟)到点才放弃。
5. ★ 我的业务服务 myapp,依赖 network-online.target,
   只能排在 wait-online 后面 —— 它被白白拖了 7 分半。
开机慢 = 某个服务卡住了,而它后面的服务被迫排队等它。

修复 1:别把"开机慢"当黑盒——systemd-analyze

# === ★ 先纠正最核心的误解:开机不是一个黑盒 ===

# === 我以为的开机 vs 真实的开机 ===
# 我以为:开机就是一个整体的、不可分割的过程,
#   它"慢"或"快",像天气一样,我只能观察、不能细究。
# ★ 真相:开机是 systemd 按【依赖关系】把几十个
#   服务(unit)一个个/一批批拉起来的过程。每个
#   服务花了多少时间,systemd 全程都【精确记着】。
#   "开机慢" 永远可以被拆解到 "某个具体服务慢"。

# === 第一步:看开机总耗时的"大账" ===
$ systemd-analyze
Startup finished in 3.1s (kernel) + 7min 42s (userspace) = 7min 45s
# ★ 它把开机分成两段:
#  - kernel   :内核自己启动 + 初始化的时间
#  - userspace:★ 内核之后,systemd 拉起所有服务的时间
# 我这次:kernel 才 3 秒,userspace 占了 7 分 42 秒 ——
#   ★ 问题 100% 在 userspace,在某个服务上。

# === ★ 第二步:systemd-analyze blame —— 按耗时排序 ===
$ systemd-analyze blame
7min 30s  NetworkManager-wait-online.service     # ★ 元凶,一眼可见
   8.2s   kdump.service
   3.1s   tuned.service
   1.8s   lvm2-monitor.service
   ...
# ★ blame 把每个服务的启动耗时【从大到小排序】。
#   排第一的,通常就是拖慢开机的主犯。
# ★ 注意:blame 是"各服务各自耗时",不代表它们
#   串行相加 —— 很多服务是并行起的。要看"谁卡住
#   了关键路径",得用下面的 critical-chain。

# === 一句话认知 ===
# ★ 遇到"开机慢",第一反应不该是猜硬件,
#   而是 systemd-analyze + blame —— 让 systemd
#   自己告诉你,时间到底花在了哪个服务上。

修复 2:critical-chain——找出真正卡住开机的那条链

# === ★ blame 看"谁慢",critical-chain 看"谁卡住了大家" ===

# === 为什么光看 blame 还不够 ===
# blame 列出每个服务各自的耗时,但服务是【并行】
#   启动的 —— 一个服务慢 5 秒,如果它和别人并行、
#   又没人等它,那它对总开机时间【毫无影响】。
# ★ 真正决定开机总时长的,是那条最长的【依赖链】:
#   A 必须等 B,B 必须等 C…… 这条链有多长,
#   开机就有多慢。这条链叫"关键链路"。

# === ★ systemd-analyze critical-chain ===
$ systemd-analyze critical-chain
graphical.target @7min 44s
└─multi-user.target @7min 44s
  └─myapp.service @7min 43s
    └─network-online.target @7min 42s
      └─NetworkManager-wait-online.service @12s +7min 30s
#                                          ^^^^  ^^^^^^^^
#                                          启动点  ★ 自身耗时
# ★ 怎么读这棵树:
#  - 从下往上,是依赖关系:下面的先起,上面的才能起。
#  - @7min 42s :这个 unit 是在开机第 7 分 42 秒"完成"的。
#  - +7min 30s :★ 这个 unit【自己】花了 7 分 30 秒。
# ★ 一眼看出:NetworkManager-wait-online 自己耗了
#   7 分半,它上面的一长串(network-online.target、
#   myapp、multi-user)全都被它【堵在后面排队】。

# === ★ blame 和 critical-chain 配合着看 ===
# - blame:哪个服务【绝对耗时】最长 -> 锁定嫌疑人
# - critical-chain:这个慢服务,是不是真的【卡在
#   关键链路上】,把别人都堵住了 -> 确认它有没有
#   "罪"。
# 两个都指向 NetworkManager-wait-online,实锤了。

# === 把启动过程画成图,更直观 ===
$ systemd-analyze plot > /tmp/boot.svg
# ★ plot 生成一张 SVG,横轴是时间,每个服务一条
#   横条,哪个服务又长又把别人堵在后面,看图一目了然。

# === 看某个具体服务的启动日志 ===
$ journalctl -b -u NetworkManager-wait-online.service
# ★ -b = 本次开机的日志。盯着这个慢服务看它的日志,
#   往往能直接看到它"在等什么 / 为什么超时"。

修复 3:wait-online 为什么会卡——它在死等网络

# === ★ 看懂这次的元凶:NetworkManager-wait-online ===

# === 这个服务是干什么的 ===
# 开机时,很多服务需要网络【真正能用】才能正常起
#   (比如要连数据库、要注册到注册中心的服务)。
# 但"网卡 up"和"网络真正就绪(拿到 IP、路由通)"
#   是两回事,后者要晚一些。
# ★ NetworkManager-wait-online 的职责,就是【阻塞】
#   在那里,一直等到网络真正就绪,再放行 ——
#   它对应一个特殊的目标:network-online.target。

# === ★ 它为什么会卡到超时 ===
# 默认情况下,wait-online 要等到【所有被管理的
#   网卡】都进入就绪状态,它才算完成。
# 我这台机器:有一块网卡 eth1,配了静态 IP,但
#   实际上【没插网线 / 配置是错的】,它永远 up 不了。
# ★ 于是 wait-online 就为这块永远好不了的网卡,
#   死等,一直等到它【自己的超时】(默认几分钟)
#   到点,才悻悻地放弃、报一个 timeout。
$ journalctl -b -u NetworkManager-wait-online.service
... Timeout waiting for online state

# === ★ 一个连锁反应:它拖累了所有下游 ===
# network-online.target 要等 wait-online 完成;
# 我的 myapp.service 写了 After=network-online.target;
# multi-user.target 要等 myapp……
# ★ 一块没接线的网卡 -> wait-online 死等 7 分半
#   -> 整条依赖链上的服务全在后面排队 -> 开机 8 分钟。
# 一个小问题,被依赖关系【放大】成了大事故。

# === 确认到底是哪块网卡的锅 ===
$ nmcli device status
DEVICE  TYPE      STATE         CONNECTION
eth0    ethernet  connected     eth0
eth1    ethernet  connecting    eth1-static    # ★ 一直 connecting,上不去
$ ip link show eth1
... state DOWN                                  # ★ 物理上就是 DOWN
# ★ 找到了:eth1 永远连不上,wait-online 却非要等它。

# === 看 wait-online 自己的超时设了多久 ===
$ systemctl cat NetworkManager-wait-online.service | grep ExecStart
ExecStart=/usr/bin/nm-online -s -q --timeout=600   # ★ 超时 600 秒 = 10 分钟
# ★ 这就是"7 分半""8 分钟"这种大数的来源 ——
#   它就是被这个 timeout 顶着的。

修复 4:开机慢的其他常见元凶

# === ★ 除了 wait-online,开机慢还常见这几种 ===

# === 元凶 1:某个服务 ExecStart 卡住,触发自身超时 ===
# 任何服务,如果它的启动命令卡住(连不上远端、
#   等一个永远不来的资源),systemd 会等到它的
#   TimeoutStartSec(默认 90 秒)才判它失败。
$ systemd-analyze blame | head
1min 30s  some-broken.service       # ★ 整 90 秒 = 撞了启动超时
# ★ "整 90 秒"这种规整数字,就是 TimeoutStartSec 的指纹。

# === ★ 元凶 2:/etc/fstab 里挂载一个不存在的设备 ===
# fstab 里写了一个挂载项,但那个设备/网络存储不在了:
$ cat /etc/fstab
/dev/sdb1  /data  ext4  defaults  0 0      # ★ sdb1 已经拔了
# ★ systemd 会为这个挂载死等(尤其网络盘 NFS),
#   拖慢开机。解法:用 nofail 选项,挂不上也别卡开机:
/dev/sdb1  /data  ext4  defaults,nofail  0 0

# === 元凶 3:磁盘自检 fsck ===
# 文件系统被标记为"需要检查",开机会跑 fsck,
#   大磁盘的 fsck 可能很久。
$ journalctl -b | grep -i fsck
# ★ 看本次开机日志里 fsck 花了多久。

# === ★ 元凶 4:服务的依赖写得太"死",该并行的串行了 ===
# 服务 unit 里 After= / Requires= 写得过度保守,
#   本可并行启动的服务,被排成了一长串串行。
$ systemd-analyze critical-chain
# ★ 如果关键链路特别长、一环扣一环,就要审视:
#   这些 After= 是不是真的必要。

# === 元凶 5:用户登录相关(plymouth、等待用户输入)===
# 极少数情况开机停在某处等输入(比如等输入磁盘
#   解密密码、等修复 shell)。
$ journalctl -b | tail -50      # 看开机日志最后停在哪

# === ★ 通用排查手法:就盯 blame 第一名 ===
# 不管是哪种元凶,套路都一样:
#  systemd-analyze blame  -> 看排第一的服务
#  journalctl -b -u 那个服务  -> 看它的日志,它在等啥
# ★ 几乎所有"开机慢",都能用这两步定位到具体服务。

修复 5:正确解法——对症治那个慢服务

# === ★ 解法:针对定位到的那个服务,对症下药 ===

# === ★ 我这次的根治:把那块废网卡处理掉 ===
# 根因是 eth1 永远连不上,却被 wait-online 死等。
# 方案 A(推荐):这块网卡确实不用了,就别让
#   NetworkManager 管它、也别让它参与开机等待:
$ nmcli connection modify eth1-static connection.autoconnect no
# 或者干脆删掉这个连接配置:
$ nmcli connection delete eth1-static
# ★ wait-online 不再为它等待,开机立刻恢复正常。

# 方案 B:网卡要保留,但不想为它卡开机 —— 让
#   wait-online 只要【任意一块】网卡就绪就放行:
$ systemctl edit NetworkManager-wait-online.service
[Service]
ExecStart=
ExecStart=/usr/bin/nm-online -s -q --timeout=30   # ★ 超时砍到 30s
# ★ 把 600 秒的超时砍短,就算还有网卡上不来,
#   最多也就拖 30 秒,不会再是 8 分钟。

# === ★ 通用解法 1:不需要的服务,直接禁掉 ===
# blame 里那些你根本用不到的服务,disable 掉:
$ systemctl disable --now 用不到的服务
# ★ 比如这台机器不需要 kdump,就可以禁掉 kdump.service。
#   开机要起的服务越少,自然越快。

# === 通用解法 2:不需要"等网络就绪"的服务,别依赖它 ===
# 如果某个服务其实不需要网络【完全就绪】才能起,
#   就别让它 After=network-online.target ——
#   只依赖 network.target(网卡 up)就够,起得更早。

# === ★ 通用解法 3:挂载项加 nofail ===
$ vi /etc/fstab
非关键挂载项末尾加  nofail            # 挂不上也不卡开机

# === ★ 通用解法 4:用 mask 彻底屏蔽顽固服务 ===
# 有的服务 disable 了还会被别的服务拉起来。
#   彻底不想要它,用 mask:
$ systemctl mask 服务名               # ★ 彻底屏蔽,谁都拉不起它
# (注意:mask 是重手段,确认真的不需要再用)

# === ★ 验证:重启,再量一次 ===
$ systemctl reboot
# 起来后:
$ systemd-analyze
Startup finished in 3.1s (kernel) + 21s (userspace) = 24s   # ★ 8 分钟 -> 24 秒
$ systemd-analyze blame | head     # 确认没有哪个服务再异常地长
$ systemd-analyze critical-chain   # 确认关键链路短了
# ★ 三个一起确认,才算这次开机慢真的根治了。

修复 6:开机慢排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ "开机慢"不是黑盒,它能被拆解到具体服务 ===
$ systemd-analyze            # 先看 kernel / userspace 大账

# === 2. ★ systemd-analyze blame —— 按服务耗时排序 ===
$ systemd-analyze blame | head    # 排第一的就是头号嫌疑

# === 3. ★ critical-chain 看谁卡在关键链路上 ===
# blame 看绝对耗时,critical-chain 看它有没有堵住别人。

# === 4. 慢服务的日志直接 journalctl -b -u 服务名 ===
# -b = 本次开机,日志里常能看到"它在等什么"。

# === 5. ★ "整 90 秒""整 600 秒"这种规整数,是超时指纹 ===
# 撞 TimeoutStartSec / wait-online timeout,不是真在干活。

# === 6. 用不到的服务 disable,顽固的 mask,挂载加 nofail ===

# === 7. ★ 别让服务过度依赖 network-online.target ===
# 不是真需要网络完全就绪的,依赖 network.target 即可。

# === 8. 排查"开机慢"的步骤链 ===
$ systemd-analyze                  # ① kernel 慢还是 userspace 慢
$ systemd-analyze blame            # ② 哪个服务耗时最长
$ systemd-analyze critical-chain   # ③ 它是不是卡在关键链路
$ journalctl -b -u 那个服务        # ④ 它到底在等什么
$ disable/改超时/加nofail          # ⑤ 对症处理那个服务
# 按这个顺序,开机慢基本能定位、能根治。

命令速查

需求                        命令
=============================================================
看开机总耗时(kernel/用户)  systemd-analyze
按服务耗时排序              systemd-analyze blame
看关键依赖链路              systemd-analyze critical-chain
把启动过程画成图            systemd-analyze plot > boot.svg
看本次开机某服务的日志      journalctl -b -u 服务名
看本次开机完整日志          journalctl -b
看网卡设备状态              nmcli device status
禁用一个服务                systemctl disable --now 服务名
彻底屏蔽一个服务            systemctl mask 服务名
改服务的启动参数/超时       systemctl edit 服务名
挂载失败不卡开机            /etc/fstab 该挂载项加 nofail

口诀:开机慢不是黑盒,systemd-analyze 拆到服务,blame 看耗时 critical-chain 看卡点
      整数秒多半是撞超时,慢服务看 journalctl -b -u,对症 disable 或改超时

避坑清单

  1. 开机慢不是一个不可分割的黑盒,它是几十个服务按依赖关系启动的总和,能拆到具体服务
  2. systemd-analyze 先看 kernel 和 userspace 各占多久,定位问题在内核还是在服务启动
  3. systemd-analyze blame 把每个服务启动耗时从大到小排序,排第一的通常是头号嫌疑
  4. blame 是各服务各自耗时,服务多是并行起的,要看谁卡住关键路径得用 critical-chain
  5. critical-chain 显示依赖链,加号后的数字是该服务自身耗时,能看出谁把下游都堵住了
  6. NetworkManager-wait-online 的职责是阻塞等网络就绪,一块连不上的网卡能让它死等到超时
  7. 启动耗时是整 90 秒整 600 秒这种规整数字,多半是撞了超时配置而非真在干活
  8. 慢服务用 journalctl -b -u 服务名 看本次开机日志,往往直接能看到它在等什么
  9. fstab 里挂载不存在的设备会卡开机,非关键挂载项加 nofail 让它挂不上也不卡
  10. 用不到的服务用 systemctl disable 禁掉,顽固的用 mask 屏蔽,服务越少开机越快

总结

这次"重启一次要等八分钟"的事故,纠正了我一个关于"开机"的、几乎是不假思索的认知方式。在我的脑子里,"开机"长久以来是一个浑然一体的、不透明的过程:我按下电源或者敲下重启,然后就是一段我无法干预、也无法看透的等待,直到机器"好了"。它快,我说不出为什么快;它慢,我也只能笼统地说一句"今天它慢"。在我心里,开机就像天气,是一个我只能观察其结果、却无从过问其内部的现象。正因为我把它当成这样一个黑盒,所以当它从两三分钟变成八分钟时,我的第一反应是去猜——是不是硬件老了?是不是磁盘自检卡住了?我所有的猜测,都是在这个黑盒的【外面】打转,因为我从来不觉得自己能走进它的【里面】。我甚至为"我抓不住到底哪一步慢"而感到理所当然,因为开机滚屏太快了——我把"我看不清"直接等同于"它本来就看不清"。复盘到根上,我才意识到,这个黑盒,其实从来就不是黑的。开机这件事,在 systemd 接手之后,根本不是一团混沌的等待,而是一个被严格编排的、有几十个参与者的过程:每一个服务都是一个独立的步骤,它们之间有着清清楚楚的先后和依赖——谁必须等谁、谁可以和谁并行。而最关键的是,systemd 在做这件事的时候,像一个一丝不苟的记账员,把每一个服务是第几秒开始的、自己花了多少秒、又是因为等谁而被耽误的,全都精确地记录在案。我以为我面对的是一个沉默的黑盒,可实际上,我面对的是一个事无巨细、有完整账本的过程——我只是从来没有翻开过那本账本。当我第一次敲下 systemd-analyze blame,那本账本"哗"地一下摊在我面前:八分钟里的七分半,被一个我从没听说过的服务 NetworkManager-wait-online 独自占掉了。它在干一件很尽职、却很冤的事——它在等网络真正就绪,可这台机器上有一块永远也连不上的废网卡,它就为这块网卡死等,一直等到自己十分钟的超时。而我那个真正的业务服务,因为依赖网络,只能眼睁睁排在它后面,被白白拖了七分半。一块没插线的网卡,顺着依赖关系,被放大成了一场八分钟的事故。这次最大的收获,是我意识到,很多我以为的"黑盒",并不是因为它们真的不可知,而是因为我从来没有去寻找那把能打开它的钥匙。我把"我不知道怎么看"误当成了"它本来就没法看",于是我心安理得地停留在"猜"的层面——猜硬件、猜磁盘、猜运气。可一个成熟的系统,它在做一件复杂的事的时候,几乎总会留下记录,总会有一个工具,能让你看见它内部的每一个齿轮是怎么咬合、怎么转动的。我之所以看不见,不是因为没有窗户,而是因为我从来没有想过去找那扇窗户在哪。我太习惯于把一整类问题——开机慢、启动慢、某个过程慢——当成一个不可分割的整体去面对,而一个整体是没法被排查的,你只能对着它干瞪眼。所以下一次,当我又想脱口而出"这个东西它就是慢"的时候,我会先逼自己改一句话:不是"它慢",而是"它【的哪一步】慢"。这一字之差,是天壤之别——"它慢"是一个让我束手无策的判断,而"哪一步慢"是一个能被工具回答、能被一层层拆解、最终一定能定位到某个具体环节的问题。任何一个看起来浑然一体的"慢",背后都是一连串有名有姓的步骤;我要做的,从来不是去猜这个整体,而是去找到那本一定存在的账本,然后翻开它,让那个一直在磨蹭的、具体的环节,自己从账本里站出来。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

脚本手动跑正常 cron 里就不执行:一次 Linux 定时任务排查复盘

2026-5-20 21:33:31

Linux教程

进程半夜无声消失:一次 Linux OOM Killer 排查复盘

2026-5-20 21:42:51

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索