如何在Ubuntu云VM映像上禁用`apt-daily.service`?


59

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?

在Ansible完成机器设置(通常涉及安装软件包)之前,我需要禁用此自动APT任务。有关更多信息和上下文,请参见https://github.com/gc3-uzh-ch/elasticluster/issues/304

我尝试了各种选项,通过的“用户数据”脚本禁用“无人值守的升级”功能cloud-init,但是到目前为止,所有这些选项都失败了。

1.禁用系统任务

systemd任务apt-daily.service由触发apt-daily.timer。我试图通过以下命令的各种组合来禁用一个或另一个,或者两者都禁用。仍然apt-daily.service是VM准备好接受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::Enable0(请参阅下文),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脚本?


2
在正式发布软件包更新之前,这将无济于事,但是请参阅我刚刚发布到Debian Bug#844453的补丁
zwol

也许您丢失了命令中的--now标志systemctl disable以便立即使更改生效。那是我的问题。
Daniel F

@DanielF没有,因为disable --now对等于stop后面disable
sourcejedi

1
显然,这已经FINALLY固定systemd在2019年二月:github.com/systemd/systemd/issues/5659。因此,希望它将在Ubuntu 20.04中使用。
捕捉

Answers:


37

是的,我显然缺少了一些东西。

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

还有一个时间窗,在此期间可能apt-get 会进行SSH登录,但不会运行,但是我无法想象可以在现有Ubuntu 16.04云映像上使用的其他解决方案。


这对aws ubuntu 16.04来说对我
有用

是的,我正在走创建自定义AMI的道路。这也加快了通用服务的安装。
giorgiosironi

这似乎还不够,我发现apt-get -o Acquire::http::AllowRedirect=false update
Edward Z. Yang

11

注意:不幸的是,以下解决方案的一部分不适用于Ubuntu 16.04系统(例如发问者的系统),因为建议的systemd-run调用仅适用于Ubuntu 18.04及更高版本有关详细信息,请参见注释)。我将答案留在这里,因为无论您使用的是哪个Ubuntu版本,该问题仍然很受欢迎。

在Ubuntu 18.04(及更高版本)上,启动时间可能需要两项服务才能更新/升级。第一个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.timerapt-daily-upgrade.timer)。

请注意,此答案中的屏蔽/禁用技术仅阻止将来启动时的更新/升级-如果它们已在当前启动中运行,则它们不会停止。


2
很好的答案,谢谢!虽然,请注意,systemd-run在Ubuntu 16.04上已经太老了,无法支持该--wait选项,但对于眼前的目的,它实际上并不是必需的。(根据手册页,--wait等待单元终止,但足以等待其启动,这是默认行为systemd-run。)
Riccardo Murri,

我的立场是正确的:给定的systemd-run咒语在Ubuntu 16.04上根本不起作用;它死于错误消息Unknown assignment After = apt-daily.service apt-daily-upgrade.service。似乎某些单位属性在中不可用systemd-run,例如,在此处查看
Riccardo Murri,

@ riccardo-murri你让我:-)!我实际上是想知道自己的差异16.04 / 18.04(因此,狡猾地“最多两个”)然后忘了提出警告。您会建议什么更改?
Anon

@ riccardo-murri啊,真是太糟糕了,我会在回答的最上方添加一个大警告,说它不能在Ubuntu 16.04上使用
Anon

禁用服务,然后重新启动即可使用!
digz6666 '18

3

您可以通过“ 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

一旦进入实例,您还应该等待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

1

掩盖单位不会更容易

systemctl mask apt-daily.service


不起作用-请参阅第1在问题文本中禁用systemd任务。但是无论如何,谢谢你的建议!:-)
Riccardo Murri

2
禁用和屏蔽服务是不一样的。mask创建到/ dev / null的链接。 ls -al /etc/systemd/system/ | grep alsa lrwxrwxrwx 1 root root 9 Sep 1 13:17 alsa-init.service -> /dev/null数据为空。

2
我摆脱无人值守的升级sudo dpkg-reconfigure -plow unattended-upgrades并放弃它。因此,apt-daily.service单元的状态已消失。

嗨@Bahamut谢谢您的努力!但是,问题是如何apt-daily.servicecloud-init脚本中禁用以及在VM重新引导后启动之前禁用脚本:这意味着:(1)必须以非交互方式完成,(2)必须apt-daily.service在首次触发之前完成。(如果我对systemd的理解是正确的,则(2)实际上无法完成并不能cloud-init同时apt-daily运行-请参阅我自己的答复以获取更多信息。)
Riccardo Murri

1
我在普通的物理计算机(即不是VM)上尝试了此操作,并且可以确认它不起作用。您还需要停止计时器:systemctl stop apt-daily.timer; systemctl禁用apt-daily.timer
happyskeptic

0

这将在循环中等待1秒,然后检查锁定是否已释放。

while : ; do
                sleep 1
                echo $( ps aux | grep -c lock_is_held ) processes are using apt.
                ps aux | grep -i apt
                [[ $( ps aux | grep -c lock_is_held ) > 2 ]] || break
        done
        echo Apt released
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.