我有一个设备设置,但我不知道如何使用 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=no
IgnoreOnIsolate=true
严格来说,这些设置不是必需的,但这些设置与生成的单元中的设置相同。[email protected]
在这两种情况下,这都是有意义的,因为对于.service
单元而言,这些单元的调用/时间/用例都是非典型的。
接下来,我们通过属性确保底层 LUKS 层停止时 LVM 层也停止BindsTo=
。我们还需要确保这两个单元之间的时间顺序正确,After=
才能实现这一点。(请注意,After=
在一个单元中做出声明等同于Before=
在另一个单元中做出声明,任何一个都可以)。
Type=oneshot
+RemainAfterExit=yes
都是使服务按预期运行所必需的,ExecStart=
在启动和ExecStop=
停止时触发语句(与其他服务类型不同,oneshot
可以有多个ExecStart=
and语句)。意味着该单元在启动后被视为“活动”,否则它将在激活后立即被视为“死亡”,这将阻止我们在更大的启动/关闭链中正确触发该单元。这些设置反映了(至少在我的发行版中)的设置,并且出于完全相同的原因,在安装单元的生命周期内跟踪/调用。ExecStop=
RemainAfterExit=yes
[email protected]
ExecStart
ExecStop
理论上,ExecStart=
可以省略,因为 udev 带有默认规则,只要系统中出现物理卷,/lib/udev/rules.d/
就会自动启动,这最终会导致在 systemd 中创建一个代表逻辑卷的单元。但是,调用已经激活的设备似乎没有坏处,并且允许我们忽略 udev 层/规则(就 LVM 而言),并将其视为完全可启动和可停止的单元。lvm2-pvscan@<device_no>.service
.device
vgchange -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/fstab
。my_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=
停止。.device
vgchange -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/fstab
TimeoutIdleSec=
.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
.automount
systemd.unit)。
重要的是,安装选项与 systemd 单元相关的现在仅影响单元.automount
。例如,fail/nofail
将决定<mnt-lv>.automount
是WantedBy=
还是RequiredBy=
local-fs.target
。auto/noauto
选项无效,而x-systemd.requires=
和类似x-systemd.*
设置也仅影响自动挂载单元。
总结一下,在系统启动时<mnt-lv>.automount
由 启动local-fs.target
,并且在整个系统运行过程中应该始终处于“活动”状态。它监视挂载点并<mnt-lv>.mount
在必要时启动/停止该单元。因此,我们需要做的就是确保:
- 需要底层 LUKS 和 LVM 单元通过安装单元
- LUKS -> LVM -> 安装单元顺序正确
- 当挂载单元停止时,底层单元会通过绑定到它来关闭
由于我们无法再在via中编辑.mount
单元属性,因此我们可以使用“覆盖”机制向生成的单元 via 添加配置:fstab
x-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 服务一样,现在只要挂载单元停止,这两个服务都会停止,而只要挂载单元激活,这两个服务都是挂载单元所必需的。所有三个单元都按正确的顺序激活和停用。