Ubuntu 16.04 服务器 VM 映像显然每 12 小时左右启动一次“apt-daily.service”;该服务执行各种与 APT 相关的任务,例如刷新可用软件包列表、根据需要执行无人值守升级等。
从虚拟机“快照”启动时,会触发该服务立即地,因为(我推测)systemd 很快意识到计时器早就应该关闭了。
然而,正在运行的 APT 会阻止其他apt
进程运行,因为它持有/var/lib/dpkg
.指示此情况的错误消息如下所示:
E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
我需要禁用这个自动 APT 任务,直到 Ansible 完成机器设置(通常涉及安装软件包);看https://github.com/gc3-uzh-ch/elasticluster/issues/304了解更多信息和背景。
我尝试了各种选项来通过“用户数据”脚本禁用“无人值守升级”功能cloud-init
,但到目前为止所有选项都失败了。
1.禁用systemd任务
systemd 任务apt-daily.service
由 触发apt-daily.timer
。我尝试使用以下命令的各种组合来禁用其中之一或两者;尽管如此,它还是apt-daily.service
在虚拟机准备好接受 SSH 连接后立即启动:
#!/bin/bash
systemctl stop apt-daily.timer
systemctl disable apt-daily.timer
systemctl mask apt-daily.service
systemctl daemon-reload
2.禁用配置选项APT::Periodic::Enable
脚本/usr/lib/apt/apt.systemd.daily
读取一些 APT 配置变量;该设置APT::Periodic::Enable
完全禁用该功能(第 331--337 行)。我尝试使用以下脚本禁用它:
#!/bin/bash
# cannot use /etc/apt/apt.conf.d/10periodic as suggested in
# /usr/lib/apt/apt.systemd.daily, as Ubuntu distributes the
# unattended upgrades stuff with priority 20 and 50 ...
# so override everything with a 99xxx file
cat > /etc/apt/apt.conf.d/99elasticluster <<__EOF
APT::Periodic::Enable "0";
// undo what's in 20auto-upgrade
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Unattended-Upgrade "0";
__EOF
然而,尽管从命令行中APT::Periodic::Enable
获得了值(见下文),该程序仍然运行......0
unattended-upgrades
ubuntu@test:~$ apt-config shell AutoAptEnable APT::Periodic::Enable
AutoAptEnable='0'
3./usr/lib/apt/apt.systemd.daily
完全删除
以下cloud-init
脚本完全删除无人值守升级脚本:
#!/bin/bash
mv /usr/lib/apt/apt.systemd.daily /usr/lib/apt/apt.systemd.daily.DISABLED
尽管如此,任务仍在运行,我可以在进程表中看到它!尽管如果从命令行探测该文件不存在::
ubuntu@test:~$ ls /usr/lib/apt/apt.systemd.daily
ls: cannot access '/usr/lib/apt/apt.systemd.daily': No such file or directory
看起来cloud-init
脚本(与 SSH 命令行一起)和 root systemd 进程在单独的文件系统和进程空间中执行......
问题
我是否缺少一些明显的东西?或者是否有一些我不知道的命名空间魔法正在发生?
最重要的是:我如何apt-daily.service
通过
cloud-init
脚本禁用它?
答案1
是的,我明显遗漏了一些东西。
Systemd 是关于服务的并发启动,因此cloud-init
运行脚本同时被apt-daily.service
触发。当
cloud-init
执行用户指定的有效负载时,apt-get update
它已经在运行。因此,尝试 2. 和 3. 失败并不是因为某些名称空间魔法,而是因为它们对系统的更改太晚了,无法apt.systemd.daily
接受更改。
这也意味着基本上没有办法预防
apt.systemd.daily
阻止运行——只有在它启动后才能杀死它。
这个“用户数据”脚本采用以下路线::
#!/bin/bash
systemctl stop apt-daily.service
systemctl kill --kill-who=all apt-daily.service
# wait until `apt-get updated` has been killed
while ! (systemctl list-units --all apt-daily.service | egrep -q '(dead|failed)')
do
sleep 1;
done
# now proceed with own APT tasks
apt install -y python
仍然有一个时间窗口,在此期间 SSH 登录是可能的,但apt-get
不会运行,但我无法想象另一个可以在 Ubuntu 16.04 云映像上运行的解决方案。
答案2
注意:不幸的是下面的解决方案的一部分不适用于 Ubuntu 16.04 系统(例如提问者的)因为建议的systemd-run
调用仅适用于Ubuntu 18.04 及以上版本(参见评论了解详情)。我将把答案留在这里,因为无论你使用哪个 Ubuntu 版本,这个问题仍然很受欢迎......
在 Ubuntu 18.04(及更高版本)上,启动时 apt 更新/升级可能涉及最多两个服务。第一个apt-daily.service
刷新包列表。然而,可能有第二个apt-daily-upgrade.service
实际安装安全关键软件包。一个回答“命令返回之前终止并禁用/删除无人值守升级”问题给出了一个很好的例子,说明如何等待这两个任务完成(为了方便起见,在此处复制):
systemd-run --property="After=apt-daily.service apt-daily-upgrade.service" --wait /bin/true
(请注意,这必须以 root 身份运行)。如果您尝试在将来启动时禁用这些服务,则需要屏蔽这两项服务:
systemctl mask apt-daily.service apt-daily-upgrade.service
或者,您可以systemctl disable
同时使用服务及其关联的计时器(即apt-daily.timer
和apt-daily-upgrade.timer
)。
请注意,此答案中的屏蔽/禁用技术仅阻止将来启动时的更新/升级 - 如果它们已经在当前启动中运行,则它们不会阻止它们。
答案3
您可以通过“bootcmd”cloud-init 模块禁用此功能。它在网络启动之前运行,这是 apt update 有机会运行之前所必需的。
#cloud-config
bootcmd:
- echo 'APT::Periodic::Enable "0";' > /etc/apt/apt.conf.d/10cloudinit-disable
- apt-get -y purge update-notifier-common ubuntu-release-upgrader-core landscape-common unattended-upgrades
- echo "Removed APT and Ubuntu 18.04 garbage early" | systemd-cat
一旦您通过 ssh 进入实例,您还应该等待 cloud-init 的最后阶段完成,因为它会移动 apt 源/列表。
# Wait for cloud-init to finish moving apt sources.list around...
# a good source of random failures
# Note this is NOT a replacement for also disabling apt updates via bootcmd
while [ ! -f /var/lib/cloud/instance/boot-finished ]; do
echo 'Waiting for cloud-init to finish...'
sleep 3
done
这也有助于查看 bootcmd 运行的时间:
# Show microseconds in systemd journal
journalctl -r -o short-precise
您可以按如下方式验证此操作是否有效:
apt-config dump | grep Periodic
# Verify nothing was updated until we run apt update ourselves.
cd /var/lib/apt/lists
sudo du -sh . # small size
ls -ltr # old timestamps
答案4
掩盖这个单位不是更容易吗
systemctl mask apt-daily.service
?