问题描述
Ubuntu 16.04服务器VM映像显然每12小时左右启动”apt-daily.service”;此服务执行各种APT-related任务,例如刷新可用软件包的列表,在需要时执行无人值守的升级等。
从VM “snapshot”启动时,该服务会立即触发,因为(我想)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?
在Ansible完成机器设置(通常涉及安装软件包)之前,我需要禁用此自动APT任务。有关更多信息和上下文,请参见https://github.com/gc3-uzh-ch/elasticluster/issues/304。
我已经尝试过各种选项,以通过针对cloud-init
的”user data”脚本禁用”unattended upgrades”功能,但是到目前为止,所有这些选项均已失败。
1.禁用系统任务
系统任务apt-daily.service
由apt-daily.timer
触发。我试图通过以下命令的各种组合来禁用一个或另一个,或者两者都禁用。仍然,在VM准备好接受SSH连接之后,立即启动apt-daily.service
:
#!/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 命令行一起)和根systemd进程在单独的文件系统和进程空间中执行…
Questions
有什么明显的我想念的东西吗?还是发生了一些我不知道的名称空间魔术?
最重要的是:如何通过cloud-init
脚本禁用apt-daily.service
?
最佳方法
是的,我显然缺少了一些东西。
Systemd与服务的并发启动有关,因此,在触发apt-daily.service
的同时运行cloud-init
脚本。到cloud-init
执行user-specified有效负载时,apt-get update
已经在运行。因此,尝试2和尝试3失败的原因不是因为某些命名空间魔术,而是因为它们更改系统的时间太晚了,以至于apt.systemd.daily
不能接受更改。
这也意味着,基本上没有办法阻止apt.systemd.daily
运行-只有在启动后才能杀死它。
此”user data”脚本采用以下路线::
#!/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云映像上使用的其他解决方案。
次佳方法
注意:不幸的是,以下解决方案的一部分不适用于Ubuntu 16.04系统(例如发问者),因为建议的systemd-run
调用仅适用于Ubuntu 18.04及更高版本(请参阅comments for details)。我将答案留在这里,因为无论您使用的是哪个Ubuntu版本,该问题仍然很受欢迎。
在Ubuntu 18.04(及更高版本)上,启动时间可能需要两项服务才能更新/升级。第一个apt-daily.service
刷新软件包列表。但是,可能还有第二个apt-daily-upgrade.service
实际上安装了安全关键软件包。一个answer to the “Terminate and disable/remove unattended upgrade before command returns”问题提供了一个很好的示例,说明如何等待它们都完成(为方便起见,在此处复制):
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
)。
请注意,此答案中的屏蔽/禁用技术仅阻止将来启动时的更新/升级-如果它们已在当前启动中运行,则它们不会停止。
第三种方法
您可以通过”bootcmd” cloud-init模块禁用此功能。它在网络启动之前运行,这是在apt更新获得运行机会之前所必需的。
#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的最后阶段完成,因为它会移动适当的源代码/列表。
# 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