systemd:配置与 LVM 设备的正确依赖关系

systemd:配置与 LVM 设备的正确依赖关系

我有一个设备设置,但我不知道如何使用 systemd 正确建模:

设置

我有以下设置:

                            ---------
                            | mount |  /mnt/*
                            ---------
                              |   A
           4. umount /mnt/*   |   |  3. mount /dev/data/* /mnt/*
                              v   |
                            ---------
                            |  LVM  |  /dev/mapper/data-*, /dev/data/*
                            ---------
                              |   A
           5. vgchange -a n   |   |  2. automatic or `vgchange -a y`
                              v   |
                            ---------
                            | LUKS  |  /dev/mapper/decr_device
                            ---------
                              |   A
 6. cryptsetup luksClose      |   |  1. cryptsetup luksOpen /dev/sdb decr_device ...
    /dev/mapper/decr_device   v   |
                            ---------
                            |  HDD  |  /dev/sdb
                            ---------

因此,(人工)设备有几个阶段。步骤 1、2 和 3 是安装分区所必需的。步骤 4、5 和 6 是加密/luks 再次关闭硬盘所必需的。

问题

我想在每个步骤基础上执行 systemd 中的所有步骤(因此每个步骤都成为一个自己的单元)。

当我提供正确的 时,Systemd 会自动执行步骤 1 和 6。/etc/crypttab当我提供正确的 时,它也会正确执行步骤 3 和 4。/etc/fstab但是,我找不到将步骤 2 和 5 放入 systemd 的可能性。

我努力了:

systemctl add-requires dev-data-stuff.device systemd-cryptsetup@decr_device.service

错误内容如下:

Failed to add dependency: Unit file dev-data-stuff.device does not exist.

我考虑过使用一些在启动或退出时调用的手动脚本来(覆盖)编写 dev-data-stuff.device,vgchange -a ...但没有找到任何使用“设备”文件执行此操作的文档。你知道有什么办法吗?

有趣的事实:系统已经自动挂载了。我猜是因为 systemd 解密了decr_device,然后 LVM 会自动创建/dev/data/*,这会在 udev 的帮助下触发挂载脚本。但是,我希望在 systemd 中对依赖链进行整体建模,以便能够手动执行它们。目前,除了手动之外,没有其他方法可以拆解整个东西,即执行步骤 4-6。

答案1

方法 1:插入时“自下而上”安装

我已经介绍过了更简单的情况,没有 LVM 层,因为这是我自己的用例。与查阅不同的 systemd 单元相关的 man 文件相关的帖子应该足以理解底层机制。

正如您正确指出的那样,LVM 层需要采取额外的措施,为此,我创建my_vgchange@<lvmvolgrp>.service/etc/systemd/system

[Unit]
DefaultDependencies=no
IgnoreOnIsolate=true

BindsTo=systemd-cryptsetup@<luksvol>.service
After=systemd-cryptsetup@<luksvol>.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/vgchange -ay <lvmvolgrp>
ExecStop=/sbin/vgchange -an <lvmvolgrp>

[Install]
WantedBy=systemd-cryptsetup@<luksvol>.service

DefaultDependencies=noIgnoreOnIsolate=true严格来说,这些设置不是必需的,但这些设置与生成的单元中的设置相同。[email protected]在这两种情况下,这都是有意义的,因为对于.service单元而言,这些单元的调用/时间/用例都是非典型的。

接下来,我们通过属性确保底层 LUKS 层停止时 LVM 层也停止BindsTo=。我们还需要确保这两个单元之间的时间顺序正确,After=才能实现这一点。(请注意,After=在一个单元中做出声明等同于Before=在另一个单元中做出声明,任何一个都可以)。

Type=oneshot+RemainAfterExit=yes都是使服务按预期运行所必需的,ExecStart=在启动和ExecStop=停止时触发语句(与其他服务类型不同,oneshot可以有多个ExecStart=and语句)。意味着该单元在启动后被视为“活动”,否则它将在激活后立即被视为“死亡”,这将阻止我们在更大的启动/关闭链中正确触发该单元。这些设置反映了(至少在我的发行版中)的设置,并且出于完全相同的原因,在安装单元的生命周期内跟踪/调用。ExecStop=RemainAfterExit=yes[email protected]ExecStartExecStop

理论上,ExecStart=可以省略,因为 udev 带有默认规则,只要系统中出现物理卷,/lib/udev/rules.d/就会自动启动,这最终会导致在 systemd 中创建一个代表逻辑卷的单元。但是,调用已经激活的设备似乎没有坏处,并且允许我们忽略 udev 层/规则(就 LVM 而言),并将其视为完全可启动和可停止的单元。lvm2-pvscan@<device_no>.service.devicevgchange -ay[email protected]

最后,WantedBy=在 install 部分中确保自动生成的[email protected]单元将自动尝试启动[email protected],从而确保“自下而上”的链条。与往常一样,install 部分中的设置只能通过调用systemctl enable(或重新启用,或禁用 -> 启用)来实现。

[所有启用/禁用单元文件实际上都是(重新)创建<unit>.wants <unit>.requires目录并在其中放置符号链接,否则从开始/停止是完全正交的。]

此时,自定义 udev 规则应systemd-cryptsetup@<luksvol>.service在设备连接时自动启动,然后设备“想要”启动my_vgchange@<lvmvolgrp>.service。此外,由于定义的时间顺序,systemd 将尝试在尝试停止 LUKS 服务之前停止 LVM 服务。

此时,我们要做的就是将挂载点连接到/etc/fstabmy_vgchange@<lvmvolgrp>.service条目将如下所示:

/dev/lvmvg/lv    /mnt/lv    ext4    defaults,nofail,x-systemd.wanted-by=my_vgchange@<lvmvolgrp>.service,x-systemd.requires=my_vgchange@<lvmvolgrp>.service,x-systemd.after=my_vgchange@<lvmvolgrp>.service    0 2

挂载点仍然隐含地表示 LVM 逻辑卷的单元,从而确保每当底层设备停止/消失时(例如由于)它就会BindsTo=停止。.devicevgchange -an

现在,新插入的 LUKS-LVM 驱动器应该会自动安装,只需停止systemd-cryptsetup@<luksvol>.service就足以卸载/停止所有内容,而启动mnt-lv.mount单元也会拉入底层单元。

缺点:经典mount/umount调用可能不起作用。如果驱动器未从系统中移除,systemd 计时器似乎会以某种方式触发 udev 并导致所有内容重新挂载。

方法 2:使用.automount带有 LUKS+LVM 的 systemd 单元

此方法依赖于 systemd 提供的自动挂载功能。仅当用户/进程尝试访问挂载点下的文件时,才会挂载卷。只要设置了“空闲超时”(默认停用)值(单位为x-systemd.idle-timeout=秒),卸载也会自动进行。/etc/fstabTimeoutIdleSec=.automount

/etc/fstab条目看起来如下:

/dev/lvmvg/lv   /mnt/lv    ext4    nofail,x-systemd.automount,x-systemd.idle-timeout=600    0 2

systemd-fstab-generator现在从这条线生成两个单元,<mnt-lv>.automount<mnt-lv>.mount,都可以在里面找到/run/systemd/generator/。与通常情况不同,该.mount单元是不是“想要/需要” ,而是与其对应的单位local-fs.target有关系(见TriggeredBy.automountsystemd.unit)。

重要的是,安装选项与 systemd 单元相关的现在仅影响单元.automount。例如,fail/nofail将决定<mnt-lv>.automountWantedBy=还是RequiredBy= local-fs.targetauto/noauto选项无效,而x-systemd.requires=和类似x-systemd.*设置也仅影响自动挂载单元

总结一下,在系统启动时<mnt-lv>.automount由 启动local-fs.target,并且在整个系统运行过程中应该始终处于“活动”状态。它监视挂载点并<mnt-lv>.mount在必要时启动/停止该单元。因此,我们需要做的就是确保:

  1. 需要底层 LUKS 和 LVM 单元通过安装单元
  2. LUKS -> LVM -> 安装单元顺序正确
  3. 当挂载单元停止时,底层单元会通过绑定到它来关闭

由于我们无法再在via中编辑.mount单元属性,因此我们可以使用“覆盖”机制向生成的单元 via 添加配置:fstabx-systemd.*systemctl edit <mnt-lv>.mount

[Unit]
Requires=systemd-cryptsetup@<luksvol>.service my_vgchange@<lvmvolgrp>.service
After=systemd-cryptsetup@<luksvol>.service my_vgchange@<lvmvolgrp>.service

LVMmy_vgchange@<lvmvolgrp>.service单元自然与方法 1 非常相似:

[Unit]
DefaultDependencies=no
IngoreOnIsolate=true

BindsTo=<mnt-lv>.mount
Before=<mnt-lv>.mount
After=systemd-cryptsetup@<luksvol>.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/vgchange -ay <lvmvolgrp>
ExecStop=/sbin/vgchange -an <lvmvolgrp>

重要的区别在于,这一次,它的生命周期与“上层”.mount单元绑定在一起。Before=这并不是真正必要的,因为它相当于After=mount 单元中的指令。After=将 LVM 服务置于 LUKS 单元之后。

最后,我们通过调用以下代码将其附加到生成的 LUKS 服务单元:systemctl edit systemd-cryptsetup@<luksvol>.service

[Unit]
BindsTo=<mnt-lv>.mount
Before=<mnt-lv>.mount

与 LVM 服务一样,现在只要挂载单元停止,这两个服务都会停止,而只要挂载单元激活,这两个服务都是挂载单元所必需的。所有三个单元都按正确的顺序激活和停用。

相关内容