最近被拉去一起看一台反复在凌晨自动重启的 Ubuntu 18.04 服务器,现象很典型:凌晨三点左右机器开始刷 apt 源、下载安全补丁,如果涉及内核更新,安装完成后就自动重启。业务日志断了几分钟,批处理任务全部被打散。查了一圈,罪魁祸首就是 unattended-upgrade 这个默认开启的自动升级功能。很多人以为“关闭 unattended-upgrade”就是把某个开关改掉,实际操作下来你会发现,它背后牵着一整条 apt 定时器、systemd 服务、debconf 配置和包依赖链,哪一层没照顾到,它都有可能换个入口再跑起来。
这篇文章就围绕 18.04 环境,讲清楚 unattended-upgrade 的完整关闭思路:先说它到底是怎么被调起来的,再给三种不同力度的关闭方式,然后是关闭后的验证手段,最后会聊几个“别一刀切”的折中方案和常见翻车恢复流程。不管你是只想让服务器别自动重启,还是想把自动升级彻底赶出生产环境,这篇都能直接照着操作。
1. 先认清 unattended-upgrade 的运作链路,才知道要关掉谁
1.1 自动更新不是“一个脚本在跑”,而是配置、服务、定时器三层协作
在 Ubuntu 18.04 上,unattended-upgrade 这个功能至少横跨三层。
第一层是 apt 配置文件,最核心的是/etc/apt/apt.conf.d/20auto-upgrades。这个文件里的APT::Periodic::Unattended-Upgrade变量就是总开关,置为"1"表示允许无人值守升级,置为"0"表示禁止。旁边还有Update-Package-Lists、Download-Upgradeable-Packages等变量,分别控制“自动刷新软件源列表”和“自动下载可升级包”。
第二层是 systemd 服务和定时器。18.04 上常见的有apt-daily.service、apt-daily-upgrade.service,以及配套的apt-daily.timer、apt-daily-upgrade.timer。定时器到点后拉起服务,服务再去执行 apt 的相关脚本。apt-daily-upgrade.timer负责的就是真正执行升级动作的路径,它会调用/usr/bin/unattended-upgrade。有的带桌面环境的发行版还会额外有unattended-upgrades.service和unattended-upgrades.timer这两兄弟,用途一样,也是定时触发自动升级。
第三层是unattended-upgrade这个 Python 脚本本身。它读取/etc/apt/apt.conf.d/50unattended-upgrades中的策略,判断哪些更新来源允许自动安装,哪些包要放进黑名单,安装完成后如果发现系统处于“需要重启”状态,还会根据配置决定要不要自动重启。
这三层是联动的:只有配置文件里开着自动升级,定时器就算触发了也只是空跑;反过来,如果只把配置文件里的开关改成"0",但没处理定时器,以后有人把配置文件再改回来,自动升级会立刻恢复。这就是为什么“关闭 unattended-upgrade”经常出现“关了跟没关一样”的情况。
1.2 哪些环境真正需要关闭它,哪些环境其实不该关
我见过不少对自动升级深恶痛绝的维护者,但说实话,不是所有机器都应该关。
如果是一台跑着面向用户业务、又没人实时盯的服务器,自动升级带来的不确定风险往往大于收益。最典型的就是凌晨自动重启:内核一更新,unattended-upgrade检测到/var/run/reboot-required标记,就会按照配置直接重启机器。对跑了长任务、批处理、数据库、消息队列这类服务的机器来说,一次没人预期的重启可能是灾难。
第二个典型场景是多节点集群。节点都从同一个镜像模板拉起,理想状态下所有节点软件版本一致。如果让每个节点各自自动更新,几个月后再看,各节点内核版本、动态库版本可能已经五花八门,排查问题的时候非常痛苦。对这种环境,我更建议把更新变成可控的发布流程,而不是放任每台机器自行漂移。
第三种是合规要求比较严格的环境。内部变更流程可能要求“任何软件变更必须经过审批窗口”,自动更新相当于绕过了审批,这在审计复盘时是说不清道不明的事项。
反过来,个人开发机、测试环境、没有专职维护的小型 VPS,我反而建议保留自动安全更新。毕竟 18.04 这类系统已经过了大版本支持期,安全补丁的及时性比什么都重要,手动维护一旦忘了,风险更不可控。
2. 实操:配置层、服务层、包层三种关闭方式怎么组合
2.1 最直接的配置层关闭:把 20auto-upgrades 里的开关翻成 0
先看当前配置文件长什么样:
cat /etc/apt/apt.conf.d/20auto-upgrades正常情况下你会看到类似这样的内容:
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Download-Upgradeable-Packages "1"; APT::Periodic::AutocleanInterval "0"; APT::Periodic::Unattended-Upgrade "1";要关闭无人值守升级,核心是把Unattended-Upgrade改成"0":
APT::Periodic::Update-Package-Lists "0"; APT::Periodic::Download-Upgradeable-Packages "0"; APT::Periodic::AutocleanInterval "0"; APT::Periodic::Unattended-Upgrade "0";这里有个容易被忽略的细节:Update-Package-Lists是不是也要改成"0",取决于你的诉求。如果你只是不想让系统自动安装软件包,但希望它每天自动刷新软件源索引,那可以让Update-Package-Lists保持"1"。刷新索引占用资源很小,也能避免以后手动执行apt update时发现源列表已经过期很久。如果你要的是“这台机器除了我手动操作之外,apt 什么都不能自动做”,那四个变量全部清零。
提示:apt 配置文件是“后读取覆盖先读取”的机制,如果你在
/etc/apt/apt.conf.d/下还有其他文件也想设置同名变量,最终生效值以数字排序靠后的文件为准。所以别只盯 20auto-upgrades,排查时还要看同目录下其他文件。
2.2 用 dpkg-reconfigure 快速生成标准配置
除了手工编辑文件,还有一条更省事的命令:
sudo dpkg-reconfigure unattended-upgrades执行后会弹出交互界面,核心问题就是“是否自动下载并安装稳定更新”。选择“No”,系统会自动把 20auto-upgrades 里的Update-Package-Lists和Unattended-Upgrade都写成"0"。
但我必须提醒你,这个命令是把双刃剑。它生成的是“完全关闭”的标准模板,不会帮你保留“只刷新索引、不自动安装”的中间状态。如果你有中间态需求,最后还是得手动去编辑 20auto-upgrades。
另外,如果你的环境里没有这个包或者 dpkg 数据库状态异常,dpkg-reconfigure unattended-upgrades可能会提示找不到可配置的包,这时就走手工编辑那一步更可靠。
2.3 服务层兜底:停掉并禁用定时器,防止“卷土重来”
配置层做了之后,强烈建议再处理服务层。因为在某些系统里,即使配置文件已经不允许自动升级,相关的 systemd 定时器依旧会周期性触发,只是脚本内部判断后立即退出。如果哪天配置文件被桌面端更新工具或人为误操作改回去,定时器又会立刻恢复自动升级。
在 18.04 上,最常见的相关单元有这些:
sudo systemctl stop unattended-upgrades.service sudo systemctl disable unattended-upgrades.service sudo systemctl stop unattended-upgrades.timer sudo systemctl disable unattended-upgrades.timer sudo systemctl stop apt-daily-upgrade.timer sudo systemctl disable apt-daily-upgrade.timer注意:执行到不存在的单元时,systemctl 会报Unit xxx could not be found。这是正常的,说明你的系统没启用这个单元,直接忽略并继续处理其他的就行。apt-daily-upgrade.timer在大多数服务器版 18.04 上是真实存在的,它就是自动安全更新背后最常见的定时触发器,这一条不能漏。
如果希望绝对稳妥,还可以把对应服务 mask 掉:
sudo systemctl mask unattended-upgrades.service sudo systemctl mask apt-daily-upgrade.servicemask比disable更狠,disable只是取消开机启动,mask是把单元软链接到/dev/null,任何服务想拉起它都会被系统拒绝。这个操作适合对稳定性要求极高的生产机。恢复的时候要记得unmask。
2.4 包层处理:卸载 unattended-upgrades 是不是最彻底的做法
如果前面的操作你都嫌麻烦,也可以直接卸载:
sudo apt remove unattended-upgrades或者连配置文件一起清干净:
sudo apt purge unattended-upgrades卸载之后,20auto-upgrades 文件不一定被删除,但Unattended-Upgrade "1"这种设置通常会随包被移除。问题是,有些发行版元包会把unattended-upgrades作为依赖带上,比如某些服务器环境的最小化安装集合。一旦你之后执行了apt install ubuntu-minimal之类的操作,或者运行某些会重置依赖关系的系统整理工具,它很可能又被拉回来。
所以我的建议是:卸载不是终点,卸载后仍要检查配置层和服务层。更常见的做法是“修改配置 + 禁用定时器 + 不依赖卸载”,这样既保留包,又不触发自动升级。万一以后想恢复自动安全更新,重新开启的时间成本也最低。
下面用一个表格把这几种方式的关键差异列出来,方便你在不同场景下选型:
| 关闭方式 | 作用层级 | 最适配场景 | 需要注意的副作用 |
|---|---|---|---|
| 修改 20auto-upgrades | 配置控制 | 永久关闭,不需要保留自动刷新索引 | 如果只改这里,后续配置文件被改写则可能恢复 |
| dpkg-reconfigure | 配置生成 | 快速批量操作,不想接触文本编辑器 | 会同时关闭自动刷新索引,中间状态需手动改回 |
| 停用/禁用 systemd 单元 | 服务调度 | 防止定时器触发空跑或意外恢复 | disable不阻止其他依赖拉起服务,必要时配合mask |
| 卸载/清除包 | 包管理 | 已确认不需要任何无人值守升级能力 | 可能被元包依赖重新拉回,需要复查 |
3. 关闭之后怎么验证,不留“已关闭”的错觉
3.1 用 systemctl 确认定时器状态
关闭操作做完后,先查 systemd 层面:
systemctl list-timers --all | grep -E 'apt|unattended'如果之前禁用了apt-daily-upgrade.timer和unattended-upgrades.timer,输出里应该看不到它们,或者即使列出,名字前面也不会是可执行状态。再逐个确认服务状态:
systemctl is-enabled unattended-upgrades.service systemctl is-active unattended-upgrades.serviceis-enabled返回disabled或masked,is-active返回inactive,基本可以确定服务层已经关死。如果is-active还显示active,说明服务正在运行,你可以继续追查是哪个定时器把它拉起来的。
3.2 用 apt-config 验证真实生效值
配置层到底生效没有,必须看 apt 实际读取到的值,而不是只看文件里的文本。因为可能存在多个 apt.conf.d 文件互相覆盖,文件里写的和实际生效的值可能不一致。用下面命令直接看运行时配置:
apt-config dump | grep -E 'Unattended-Upgrade|Update-Package-Lists|Download-Upgradeable'重点看APT::Periodic::Unattended-Upgrade的最终值是不是"0"。同时也看一下Update-Package-Lists最终值是否符合预期。
再配合检查文件内容:
cat /etc/apt/apt.conf.d/20auto-upgrades grep -R "Unattended-Upgrade" /etc/apt/apt.conf.d/后者能帮你找出是否有其他文件也定义了该变量,避免出现某个文件把值覆盖回"1"的情况。
3.3 观察日志和进程,确认没有任何残留行为
关闭不是一瞬间完成的,如果关闭时正好有自动升级在跑,可能会有残留的 dpkg 进程或锁文件。验证一下:
pgrep -af unattended-upgrade pgrep -af 'apt.systemd.daily' ls -lt /var/log/unattended-upgrades/正常情况下,pgrep没有输出。日志目录如果存在,观察最新日志文件的修改时间,如果已经停在关闭操作之前的日期,说明之后没有新的自动升级会话。如果你不放心,还可以看 systemd 日志:
journalctl -u unattended-upgrades.service --since "1 hour ago" journalctl -u apt-daily-upgrade.service --since "1 hour ago"没有新记录就是干净状态。
提示:我遇到过一种“关了但没完全关”的情况,配置文件和服务都处理好了,结果系统里还残留着
/etc/cron.daily/apt-compat定时任务,它会周期性执行 apt 脚本。虽然最终脚本也会读取配置变量并返回,但排查时你会在日志里看到 apt 活动,导致误判。处理掉服务层和配置层之后,这类 cron 残留一般不足为惧,但如果你的环境对“apt 完全不可自动运行”有严格要求,也要把它纳入检查范围。
4. 不想彻底关掉?只关自动重启、只排除内核更新也足够
4.1 需求一:保留安全补丁,但禁止自动重启
很多人真正讨厌的不是自动升级,而是自动重启。如果你愿意让系统安装安全补丁,只是不想在凌晨被毫无征兆地重启打断,可以在/etc/apt/apt.conf.d/50unattended-upgrades里找到并修改这两项:
Unattended-Upgrade::Automatic-Reboot "false"; Unattended-Upgrade::Automatic-Reboot-Time "02:00";Automatic-Reboot设为"false",意思就是无论升级后是否需要重启,都不主动重启机器。重启动作留给你自己来决定,你可以在业务低峰期统一重启,或者等下一次计划维护窗口一并处理。
需要说明的是,Automatic-Reboot-Time是给“允许自动重启”的情况用的,比如你希望它在凌晨四点重启,就把时间写成"04:00"。一旦你把Automatic-Reboot设成了"false",时间这项存不存在都无所谓。这种配置适合那种“可以打补丁,但绝不能擅自重启”的数据库节点、缓存节点、长任务执行机。
4.2 需求二:升级普通软件包,但把内核更新排除在外
还有一种常见诉求:普通软件包随便升,内核更新坚决不动。这样做的主要动机是,内核升级后通常要求重启,而且内核变更对线上行为的影响比普通包大得多。
修改/etc/apt/apt.conf.d/50unattended-upgrades,找到Package-Blacklist部分,把内核相关包写进黑名单:
Unattended-Upgrade::Package-Blacklist { "linux-image-.*"; "linux-generic"; "linux-headers-.*"; };linux-image-.*匹配所有内核镜像包,linux-generic是元包,直接锁掉可以防止它把新内核镜像带进来。这样系统每天依然会安装其他安全更新,但不会自动升级内核。
我必须提醒一句:这个方案的代价是内核安全隐患需要你手动处理。如果你所在团队的运维能力不足,或者长期没人维护,我宁可你保留自动内核升级,至少安全上不裸奔。排除内核更新只适合那种“有固定内核版本要求、有专门安全负责人”的环境。
4.3 需求三:自动升级关掉后,手动维护应该形成什么习惯
关闭自动升级之后,很多人会陷入另一个极端:彻底忘了更新这回事。三个月后再去apt update,会发现源列表里的仓库签名过期、依赖解析出问题,升级成本远比按时更新高得多。
我建议形成固定的手动维护节奏。最基础的命令组合是这样:
sudo apt update apt list --upgradable然后根据升级清单决定是否执行:
sudo apt upgrade --with-new-pkgs--with-new-pkgs这个参数在 18.04 上比较关键,它允许 apt 在升级过程中把新依赖的软件包一并安装,避免出现“为了升级 A,必须新装 B,但 apt 默认不愿装新包”的尴尬。开启无人值守升级时系统内部也是类似逻辑,手动维护时别漏了它。
升级完成后检查是否需要重启:
test -f /var/run/reboot-required && echo "reboot required" cat /var/run/reboot-required.pkgs 2>/dev/null如果需要重启,在业务低峰期挑个时间统一重启,再通过uptime和服务状态确认恢复。习惯了这套节奏之后,你完全可以用脚本或定期任务把“固定维护窗口 + 手动升级”自动化,效果比无人值守升级更可控。
5. 翻车现场与恢复指南:从“又自动升级了”到“重新开启默认策略”
5.1 翻车现场:只改了配置没动服务,或者反过来
最常见的翻车,就是只处理了一侧。有人说“我明明把 20auto-upgrades 里的开关改成 0 了,为什么还自动升级?”大概率是没注意到系统里还有另一个配置文件或者其他触发路径。有人说“我明明把服务停了,怎么还会更新?”多半是因为 apt 配置文件里的开关还开着,定时器被其他依赖重新拉起后又开始干活。
我在实际环境中的处理顺序永远是:先看配置,再停服务,最后再看日志。配置是自动升级的“油门”,服务是“发动机”,两个都踩住才叫停车。只松开其中一个,它总有一天会自己跑起来。
5.2 翻车现场:桌面端软件更新器把配置改回“1”
如果你在带桌面环境的 18.04 上操作,还有一个隐蔽的重灾区:桌面的“软件更新器”设置。在里面把“自动更新”改成“仅下载更新”或“立即安装”,系统会悄悄把 20auto-upgrades 里的变量改写。有时候你前一天刚改成"0",第二天 someone 打开软件更新器点了一下“检查更新”,配置又被写回"1"。
对于服务器环境,解决办法是绕开桌面端,直接用命令行维护。对于桌面环境,我建议在关闭自动升级后,顺便把软件更新器的相关设置固定成“从不检查更新”,减少误触发的可能。
5.3 翻车现场:关闭时撞上 apt 锁,折腾了半天发现升级还没停干净
关闭操作如果在自动升级正在运行的时候进行,可能会撞上 dpkg 的锁。症状就是执行apt命令时报:
Could not get lock /var/lib/dpkg/lock-frontend这时候不要强行 kill,也不要直接删锁文件,那是最后的手段。先停掉相关服务,让正在跑的脚本正常退出:
sudo systemctl stop apt-daily-upgrade.service sudo systemctl stop apt-daily.service再检查有没有残留的升级进程:
pgrep -af 'unattended-upgrade|apt.systemd.daily'如果有,给脚本一点时间,或者选择在确认业务影响可控的情况下再处理。等锁释放后,重新执行验证步骤。
5.4 恢复指南:怎么把自动升级重新接回来
有些环境是“先关一阵子,等版本稳定了再打开”。恢复时不要只改配置文件,要系统地反向操作:
sudo dpkg-reconfigure unattended-upgrades交互里选择“Yes”,dpkg 会重新生成允许自动更新的配置。然后恢复定时器:
sudo systemctl unmask unattended-upgrades.service sudo systemctl enable --now unattended-upgrades.timer sudo systemctl enable --now apt-daily-upgrade.timer如果之前没有卸载包,恢复就非常简单。如果卸载了包,那就先重新安装:
sudo apt install unattended-upgrades装完后 20auto-upgrades 通常会自动生成,再用apt-config dump确认变量已经变成"1"。最后看一眼日志,确认下一次定时器触发时能正常写入新的升级记录,这样才算真正恢复。
我在实际环境里最喜欢用的关闭方案,概括起来就是一句话:配置文件清零、定时器禁用、日志留底。不需要把包卸载掉,也不要只改一半。这样既满足“不让机器随便动”的诉求,也保留将来一键恢复的余地。如果你也是被凌晨自动重启折腾到头大的人,不妨照着这个链路走一遍,确认完三层状态之后,那台 18.04 才能真正睡个安稳觉。