openSUSE:Systemd 打包指南
RPM 宏
从 openSUSE 12.1 开始,systemd-rpm-macros 包提供了几个 RPM 宏,用于打包单元文件、tmpfiles 和 sysusers 配置。
截至 2020-11-20,systemd-rpm-macros 是 Leap 和 Tumbleweed 中 rpm-build 的必需依赖项,因此您无需在 specfile 中请求它;尽管如此,如果您担心 rpm-build 在未来版本中会删除它,请随时明确此依赖项。
BuildRequires: systemd-rpm-macros
构建依赖
如果您需要将一些编译后的源代码链接到 systemd 库,您应该使用此 pkgconfig 虚拟提供程序
BuildRequires: pkgconfig(libsystemd)
这将允许构建系统根据需要选择 systemd-mini-devel。
同样,如果您需要在构建过程中使用 systemd 程序,请使用
BuildRequires: pkgconfig(systemd)
以允许根据需要使用 systemd-mini。
运行时依赖
与 systemd 相关的 rpm 宏(例如 %service_add_post),它们扩展为 shell 代码,并在脚本中调用 systemctl 和其他相关实用程序,能够检测 systemd 的缺失,并且实际上可以在完全没有 systemd 的安装上工作。因此,使用 Requires(post): systemd 或相关的 %systemd_requires 既不必要,甚至不鼓励这样做。
只有当作为软件包一部分发行的程序将 systemd 命令作为必要条件并且否则无法正常工作时,您才可以添加适当的依赖项。
单元文件
Systemd 单元文件,包括但不限于.service, .socket, .timer和.path文件应始终安装在 %_unitdir 中,即 /usr/lib/systemd/system,而绝不在 /etc/systemd/system 中,以便用户可以覆盖它们而不会与打包冲突。例外情况是用户单元文件,必须安装在 %_userunitdir 中,即 /usr/lib/systemd/user。
这是一个合法的安装行示例,其中 %{S:3} 是服务文件的示例引用
install -D -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/foo.service
Systemd 单元文件不应由系统管理员编辑。因此,不应在 specfile 的 %files 部分中将文件标记为 %config。相反,应将修改放置在 /etc/systemd/system 目录中,要么作为完整的替换文件,例如 /etc/systemd/system/foo.service,要么作为扩展片段,例如 /etc/systemd/system/foo.service.d/my.conf。
在安装脚本中注册单元文件
为了告诉 systemd 更改过的单元文件,应调用 systemctl daemon-reload。要从 specfile 执行此操作,可以使用 systemd-rpm-macros 提供的各种 %service_* 宏。这些宏将透明地处理 sysv initscripts 迁移(只要 initscripts 和 systemd 服务具有相似的名称)。每个 service_ 宏理想情况下应调用一次,传递所有单元,而不是例如有多个 %service_add_post。这样可以使生成的 shell 代码更小。
%pre %service_add_pre foo.service bar.socket elsewhere.path %post %service_add_post foo.service bar.socket elsewhere.path %preun %service_del_preun foo.service bar.socket elsewhere.path %postun %service_del_postun foo.service bar.socket elsewhere.path
请与手册页 systemd.service(5)、systemd.socket(5) 和 systemd.path(5) 以及概述 systemd.exec(5) 和 systemd.unit(5) 进行比较。
如果您的软件包应为早于 12.1 的 openSUSE 构建,则应使用条件测试来测试这些宏。
在软件包更新期间,%service_del_postun 会重启单元。如果不需要重启单元,则应使用 %service_del_postun_without_restart。
启用单元
默认情况下,服务在安装软件包时不会启用。如果您希望您的服务默认启用,则应在 systemd-presets-common-SUSE 或 systemd-presets-branding-openSUSE 包上提交请求,通过添加例如
enable your_service_name.service
这样的自动启用的服务不应在启动前需要任何手动配置。
最近的 openSUSE 和 SUSE 产品在两个预设包中包含两个重要的预设文件。systemd-presets-common-SUSE 与 95-default-SUSE.preset(或高达 Leap 15.0 的 90-default-SUSE.preset)和 systemd-presets-branding-openSUSE(或通常 systemd-presets-branding-brand)与 90-default-openSUSE.preset(或 90-default-brand.preset)。
两者在技术上是等效的。根据您的服务的目的,选择合适的预设文件
systemd-presets-common-SUSE 适用于系统或软件包的正常功能所必需的服务。将预设文件放入此文件意味着:所有系统都应将此值作为默认值。此软件包是开源和企业产品的通用基础,并且是所有基本安装的一部分。
systemd-presets-branding-openSUSE 适用于供应商定制。将预设文件放在此处意味着:对于 openSUSE 来说,这是一个明智的默认设置,但不在其他产品中启用此服务是完全有意义的。软件包结构允许创建具有自定义 systemd-presets-branding-brand 作为自定义品牌的一部分的自定义品牌。有关品牌包的通用规则,请参见 openSUSE:Packaging_Branding。
注意:第三方软件包可以安装自定义预设文件,但不允许在 openSUSE 发行版中使用软件包。
预设更改和升级
当您更改服务的预设时,升级脚本会调用一次性自定义重置。这意味着,如果您添加了启用,那么它将在所有系统上强制启用该服务。反之,如果您删除了启用或添加了禁用,它将在升级期间强制禁用该服务。
安装顺序
考虑一个已安装 systemd 正在运行的系统,并且您计划安装一个程序包 P2,其单元文件使用仅在较新 systemd 版本中可用的功能。例如,这可能是 RestrictSUIDSGID 单元指令,它在 systemd-242 中可用。
如果 P2 单独安装,它将不得不满足可用的功能(通常这意味着 systemd 会忽略单元文件中未知的行)。这不一定是坏事。但是,如果计划在同一事务中安装较新的 systemd 包,那么首先安装 systemd 可能会有益,这样在安装和重启 P2 时,这些功能就已经可用了。
为此,您可以将 %{?systemd_ordering} 添加到相应的 %package 块(也可以是隐式的主子包)。例如,
Name: foo
Version: 0
Release: 0
Requires: bash
%{?systemd_ordering}
…
%post
%service_add_post foo.service
…
当然,仍然可以拆分事务,先单独安装 P2,然后再单独安装 systemd。%systemd_ordering 的意图不是涵盖这一点,因为如果存在硬依赖项,您需要知道确切的版本号。
(对于历史学家,此 是 Github 拉取请求。)
实例化服务
如果需要实例化服务(与手册页 systemd.unit(5) 比较),则 RPM 中的服务文件应称为 foo@.service。因此,这是一个合法的安装行
install -D -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/foo@.service
目前不应将实例模板传递给 %service_*,因为这些宏无法处理它们。同样,通常不应启用实例(来自 .spec 文件),而应通过预设文件启用(参见上文)。
在安装 RPM 包后,管理员可以通过如下命令实例化服务
sytemctl enable autossh@someinstance.service
管理员可以像任何其他单元一样覆盖该服务,即通过在 /etc/systemd/system/foo@someinstance.service.d/my.conf 中创建文件,或使用 systemctl edit foo@someinstance。
因此,每个实例必须由系统管理员或系统生成器(与手册页 systemd.generator(7) 比较)单独实例化和覆盖,如 http://www.freedesktop.org/wiki/Software/systemd/Generators 中所述。
发布单元文件 drop-ins
用户可以通过 drop-in 文件来更改由 SUSE 提供的软件包发运的单元的配置设置,而无需修改单元文件本身。drop-in 文件的完整描述可以在 systemd.unit(5) 手册页中找到。
为了避免 SUSE 发运的 drop-in 文件与用户文件之间可能发生的任何冲突,建议使用以两位数字和破折号为前缀的所有 drop-in 文件名,以简化文件的排序。例如“29-override.conf”。
此外,以下范围的两位数字已保留如下
- [0-19] 保留给 systemd 上游
- [20-29] 保留给 SUSE 发运的 systemd
- [30-39] 保留给 SUSE 包(systemd 除外)
- [40-49] 保留给第三方包
- 50 保留给使用 `systemctl set-property` 创建的单元 drop-in 文件
因此,通过使用大于 50 的两位数字,用户可以确保 SUSE 发运的任何 drop-in 文件将来都不会覆盖用户自己的 drop-in 文件。
沙盒化
许多服务以 root 权限运行,因此会使系统面临本地和有时是远程攻击。可以通过在服务文件中设置来缓解其中一些问题。请考虑应用 Systemd 硬化工作 中的沙盒设置
定时器
定时器单元 是 systemd 功能,可帮助定义时间激活的服务或事件。它是 cron 作业的替代方案。
定时器是一种单元类型,其配置文件名以 .timer 结尾,并且此定时器单元激活匹配的服务单元(foo.timer 激活 foo.service)。systemd 会自动将具有相同名称的定时器和服务配对,并且可以通过单元文件中的额外 Unit= 行指定不同的关系。
定时器需要在 [Timer] 部分中包含定义何时激活定时器的内容。有关用于定时器配置的时间格式的完整描述,请参见 systemd.timer(5)。有关时间和日期规范,请参见 systemd.time(7)。
定时器单元可以由上游提供,也可以由为发行版编写定时器的打包者提供(例如,在从 cron 作业迁移到 systemd 定时器期间)。
与其他单元一样,定时器应作为参数传递给 %service_* 调用,并且只能通过预设文件启用。
示例
示例 logrotate.timer
[Unit] Description=Daily rotation of log files Documentation=man:logrotate(8) man:logrotate.conf(5) [Timer] OnCalendar=daily AccuracySec=12h Persistent=true [Install] WantedBy=timers.target
示例 logrotate.service
[Unit] Description=Rotate log files Documentation=man:logrotate(8) man:logrotate.conf(5) ConditionACPower=true [Service] Type=oneshot ExecStart=/usr/sbin/logrotate /etc/logrotate.conf
MAILTO
systemd 当前不支持 cron 的 MAILTO 函数,用于在作业失败时发送电子邮件。但是 openSUSE 提供了一个包 systemd-status-mail,可以配置为在 systemd 定时器成功或失败时发送电子邮件。
在 /var/run 和 /run 中创建文件和子目录
从 openSUSE 12.2 开始,/var/run(要么被绑定挂载,要么链接到 /run)被挂载为 tmpfs,因此软件包不应在 /var/run(或 /run)下包含任何目录(或文件),因为它们将在下次重新启动时消失。
如果需要在那里创建新的文件/目录,你应该打包一个 tmpfiles.d 文件(请参阅 tmpfiles.d 的语法),安装在 %_tmpfilesdir (/usr/lib/tmpfiles.d/) 中。一个这样的文件示例
# create a directory with permissions 0770 owned by user foo and group bar d /run/my_new_directory 0770 foo bar
不要使用 /var 作为前缀:/var/run 只有在 /var 被挂载并且创建了兼容性符号链接后才可用。/run 本身从一开始就可用。
你应该在 %install 部分安装它们,如下所示
%install
...
install -D -m 0644 %{SOURCE4} %{buildroot}%{_tmpfilesdir}/%{name}.conf
并将它们添加到 %files 部分,并以 %ghost 宏为前缀
%files ... %ghost /run/my_new_directory
这将防止 RPM Lint 警告 tmpfile-not-in-filelist。用户将能够轻松查询谁创建了该目录,它将在软件包删除时被卸载,并且其他的 RPM Lint 检查也会看到它。