Ubuntu系统默认每天会检查一次安全更新,但不会自动重启。对于暴露在公网的服务器,一个高危内核漏洞从公布到被广泛利用可能只需几小时。如果没人手动重启,补丁等于白打。解决这个问题的核心手段不是关闭更新,而是让系统在必要时自己完成重启,同时把对业务的影响降到最低。
理解无人值守升级与重启的分离机制Ubuntu的无人值守升级由unattended-upgrades包控制。安装后它会自动下载并安装安全更新,但默认配置里重启行为是被禁用的。很多人以为装了它就万事大吉,其实系统更新完依然运行着旧内核,直到下次手动重启。要验证这一点,可以执行cat /var/run/reboot-required,如果文件存在,说明系统需要重启才能完成某些更新。
检查当前状态:
sudo apt install unattended-upgrades sudo systemctl status unattended-upgrades
如果服务处于运行状态,说明自动更新已经在工作,只是重启环节缺失。接下来的工作就是补上这个环节。
配置自动重启的核心文件关键配置文件有两个:/etc/apt/apt.conf.d/50unattended-upgrades和/etc/apt/apt.conf.d/20auto-upgrades。第一个控制升级的具体行为,第二个控制更新检查周期。
编辑50unattended-upgrades,找到并修改以下行:
Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "03:00"; Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
第一行开启自动重启。第二行指定重启时间,建议选在凌晨业务低谷期。第三行决定即使有用户登录是否也执行重启,生产环境建议设为true,否则一个挂着的SSH会话就会阻止重启。
还有一个容易被忽略的参数:
Unattended-Upgrade::Remove-Unused-Kernels "true"; Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
这两行让系统在更新后自动清理旧内核和不再需要的依赖包,避免/boot分区被撑满导致后续更新失败。
凌晨三点重启听起来简单,但如果多台服务器同时重启,可能导致服务全部中断。更精细的做法是利用systemd的定时器配合脚本,实现随机延迟或分批重启。
创建一个自定义重启检查脚本/usr/local/bin/reboot-if-required.sh:
#!/bin/bash
if [ -f /var/run/reboot-required ]; then
sleep $(( RANDOM % 1800 ))
/sbin/shutdown -r now
fi
这个脚本检测到需要重启的标志文件后,会随机等待0到1800秒再执行重启。给脚本添加执行权限:
sudo chmod +x /usr/local/bin/reboot-if-required.sh
然后创建一个systemd服务单元/etc/systemd/system/reboot-if-required.service:
[Unit] Description=Reboot if required after unattended upgrades After=unattended-upgrades.service [Service] Type=oneshot ExecStart=/usr/local/bin/reboot-if-required.sh
再创建一个对应的定时器/etc/systemd/system/reboot-if-required.timer:
[Unit] Description=Timer for reboot-if-required service [Timer] OnCalendar=daily Persistent=true RandomizedDelaySec=3600 [Install] WantedBy=timers.target
启用定时器:
sudo systemctl daemon-reload sudo systemctl enable reboot-if-required.timer sudo systemctl start reboot-if-required.timer
这套方案比单纯依赖unattended-upgrades的内置重启功能更灵活。RandomizedDelaySec让定时器本身就有随机偏移,加上脚本内的随机睡眠,多台服务器几乎不可能同时重启。
处理需要重启的服务而非整机重启并非所有安全更新都需要重启整台机器。比如OpenSSL库更新后,只需要重启依赖它的服务。Ubuntu的needrestart工具能精确识别哪些服务需要重启,并自动处理。
安装needrestart:
sudo apt install needrestart
编辑配置文件/etc/needrestart/needrestart.conf,设置自动重启服务:
$nrconf{restart} = 'a';
这个值设为'a'表示自动重启所有需要重启的服务。设为'l'则仅列出不执行,设为'i'为交互模式。生产环境建议先用'l'观察一段时间,确认无误后再改为'a'。
needrestart会hook到apt的DPkg::Post-Invoke阶段,每次安装或更新软件包后自动运行。它会检查哪些守护进程使用了被替换的库文件,然后根据配置决定是否重启这些服务。对于内核更新,它仍然会提示需要整机重启,但配合前面的重启脚本就能形成完整闭环。
内核热补丁方案绕过重启对于绝对不能停机的环境,Ubuntu Pro订阅提供了Livepatch服务,可以在不重启的情况下给运行中的内核打补丁。免费用户可以给最多5台机器使用。
获取Ubuntu Pro令牌后,执行:
sudo pro attach YOUR_TOKEN sudo pro enable livepatch
Livepatch只覆盖高危和关键安全漏洞,不是所有内核补丁都能热更新。当内核版本跨度太大时,最终仍需要重启。它的价值在于大幅降低重启频率,从每月几次降到每年几次。
检查Livepatch状态:
canonical-livepatch status --verbose
输出会显示当前内核的补丁覆盖情况和历史记录。这个方案适合数据库服务器、核心网络设备等对可用性要求极高的场景。
监控与验证机制配置完自动重启后,必须建立验证手段,否则系统可能悄悄失败而无人知晓。最直接的方式是监控/var/run/reboot-required文件的存在时间。
一个简单的监控脚本思路:如果该文件存在超过48小时,说明自动重启可能未生效,需要人工介入。可以把这个逻辑接入现有的监控系统如Zabbix或Prometheus的textfile collector。
对于使用systemd定时器的方案,检查定时器是否正常触发:
systemctl list-timers reboot-if-required.timer systemctl status reboot-if-required.service
查看最近一次执行的时间和退出状态。如果服务单元状态为失败,检查日志:
journalctl -u reboot-if-required.service
另外,/var/log/unattended-upgrades/目录下有详细的升级日志,可以确认安全更新是否真的被安装了。如果更新都没下载成功,重启自然也不会发生。
unattended-upgrades支持邮件通知。在50unattended-upgrades中配置:
Unattended-Upgrade::Mail "admin@example.com"; Unattended-Upgrade::MailReport "on-change";
MailReport设为"on-change"表示只在有更新时才发邮件,避免每天收到无意义的报告。设为"only-on-error"则只在出错时通知。
对于重启事件,可以在重启脚本中加入日志记录:
#!/bin/bash
if [ -f /var/run/reboot-required ]; then
echo "$(date): System rebooting due to security updates" >> /var/log/auto-reboot.log
sleep $(( RANDOM % 1800 ))
/sbin/shutdown -r now "Automatic reboot for security updates"
fi
这样每次自动重启都会留下时间戳记录,方便事后审计。
针对不同场景的推荐策略对于个人VPS或小型网站,直接使用unattended-upgrades内置的Automatic-Reboot功能即可,设置凌晨时段重启,简单有效。
对于中型业务集群,采用systemd定时器加随机延迟的方案,确保多台服务器不会同时重启。配合needrestart减少不必要的整机重启。
对于大型生产环境,优先考虑Livepatch减少重启次数,同时用needrestart处理服务级重启。剩余必须重启的情况通过滚动更新的方式分批执行,可以结合负载均衡器的健康检查自动摘除节点。
无论哪种场景,都要确保/boot分区有足够空间。定期检查:
df -h /boot
如果使用率超过80%,手动清理旧内核:
sudo apt autoremove --purge
这条命令可以加入cron月度任务,防止空间耗尽。
安全更新的最终目的是降低风险,但配置不当的自动重启本身就是一种风险。关键在于找到适合自身业务的平衡点,用脚本和监控把不可控变成可控。设置好后定期检查日志,确认整个链路畅通,才能真正做到高枕无忧。
