Red Hat Training

A Red Hat training course is available for Red Hat Enterprise Linux

10.6. 创建和修改 systemd 单元文件

单元文件包含描述这个单元并定义其行为的配置指令。几个 systemctl 命令可在后台使用单元文件。要进行更细的调整,系统管理员必须手动编辑或创建单元文件。表 10.2 “systemd 单元文件位置” 列出存储在系统中单元文件的三个主要目录,/etc/systemd/system/ 目录为系统管理员创建或自定义的单元文件保留。

单元文件名的格式如下:

unit_name.type_extension

此处, unit_name 代表单元名称,type_extension 代表单元类型,请参阅 表 10.1 “可用的 systemd 单元类型” 了解单元类型的完整列表。例如,系统通常会有 sshd.servicesshd.socket 单元。

可通过一个目录来补充单元文件,以了解额外的配置文件。例如,要将自定义配置选项添加到 sshd.service,请创建 sshd.service.d/custom.conf 文件,并在其中插入其他指令:有关配置目录的详情请参考 第 10.6.4 节 “修改现有单元文件”

另外, sshd.service.wants/sshd.service.requires/ 目录可以被创建。这些目录包含到 sshd 服务依赖的单元文件的符号链接。符号链接会在安装过程中根据 [Install] 单元文件选项(请参阅 表 10.11 “重要 [Install] 部分选项”)或者在运行时根据 [Unit] 选项自动创建(请参阅 表 10.9 “重要 [Unit] 部分选项”)。也可以手动创建这些目录和符号链接。

可以使用名为 单元指定符 - 通配符字符串(在加载单元文件时动态替换为单元参数)来设置许多单元文件选项。这可创建通用单元文件,用作生成实例化单元的模板。详情请查看 第 10.6.5 节 “使用 Instantiation 单元”

10.6.1. 了解单元文件结构

单元文件通常由三个部分组成:

  • [Unit] - 包含不依赖于这个单元类型的通用选项。这些选项提供单元描述,指定单元的行为,并将依赖项设置为其他单元。有关最常用 [Unit] 选项的列表请参考 表 10.9 “重要 [Unit] 部分选项”
  • [单元类型] - 如果单元具有特定于类型的指令,则这些指令分组在以单元类型命名的部分中。例如,服务单元文件包含 [Service] 部分,有关最常用的 [Service] 选项,请参阅 表 10.10 “重要 [Service] 部分选项”
  • [install] - 包含 systemctl enabledisable 命令使用的单元安装信息,请参阅 表 10.11 “重要 [Install] 部分选项” 了解 [Install] 选项列表。

表 10.9. 重要 [Unit] 部分选项

选项[a] 部分,请参阅 systemd.unit(5) 手册页。]描述

描述

对单元进行有意义的描述。这个文本显示在 systemctl status 命令的输出中。

Documentation

提供单元参考文档的 URI 列表。

[b]

定义启动单位的顺序。这个单元仅在 After 中指定的单元处于活跃状态后才启动。与 Requires 不同,After 不会显式激活指定的单元。Before 选项与 After 的功能相反。

Requires

配置其它单元上的依赖关系。Requires 中列出的单元与单元一同被激活。如果任何需要的单元无法启动,则该单位就不会被激活。

期望

配置比 Requires 更弱的依赖项。如果列出的单元没有成功启动,它对单元激活不会有影响。这是建立自定义单元依赖项的建议方法。

Conflicts

配置负的依赖关系,与 Requires 相反。

[a] 使用 [Unit] 配置选项的完整列表
[b] 在大多数情况下,只需要AfterBefore 单元文件选项设置顺序依赖关系就足够了。如果还使用 Wants(推荐)或 Requires设置了需要的依赖关系,仍需要指定依赖关系顺序。这是因为排序和要求依赖关系可以独立地工作。

表 10.10. 重要 [Service] 部分选项

选项[a] 部分,请参阅 systemd.service(5) 手册页。]描述

Type

配置单元进程启动类型,它会影响 ExecStart 的功能 和相关选项。其中之一:

* simple - 默认值。使用 ExecStart 启动的进程是该服务的主要进程。

* forking - 使用 ExecStart 启动的进程生成一个子进程,成为服务的主进程。父进程在启动完成后会退出。

* oneshot – 这个类型与 simple 类似,但在启动相应单位前会退出。

* dbus - 这个类型与 simple 类似,但仅在主进程获得 D-Bus 名称后启动。

* notify - 此类型与 simple 类似,但只有在通过 sd_notify()函数发送通知消息后才启动相应单元。

* idle - 与 simple 类似,服务二进制文件的实际执行会延迟到所有作业完成前,这样可以避免将状态输出与服务的 shell 输出混合。

ExecStart

指定在启动该单元时要执行的命令或脚本。ExecStartPreExecStartPost 指定在 ExecStartPtart 之前和之后要执行的自定义命令。Type=oneshot 启用指定可按顺序执行的多个自定义命令。

ExecStop

指定在该单元停止时要执行的命令或脚本。

ExecReload

指定重新载入该单元时要执行的命令或脚本。

Restart

启用此选项后,服务会在进程退出后重新启动,但 systemctl 命令的干净停止除外。

RemainAfterExit

如果设置为 True,即使所有进程都退出,该服务也被视为活动状态。默认值为 False。这个选项在配置了 Type=oneshot 时特别有用。

[a] 在 [Service] 中可配置选项的完整列表

表 10.11. 重要 [Install] 部分选项

选项[a] 部分,请参阅 systemd.unit(5) 手册页。]描述

Alias

为这个单元提供空格分开的额外名称列表。除 systemctl enable 以外,多数systemctl 命令可使用别名而不是实际的单元名称。

RequiredBy

依赖于这个单元的单元列表。当启用此单元时,在 RequiredBy 中列出的单元会获得对这个单元的一个 Require 依赖项。

WantedBy

依赖于这个单元的单位列表。当启用这个单元时,在 WantedBy 中列出的单元会得到一个 Want 依赖项。

Also

指定要随这个单元一起安装或卸载的单元列表。

DefaultInstance

仅限于实例化单元,这个选项指定启用单位的默认实例。请查看 第 10.6.5 节 “使用 Instantiation 单元”

[a] 在 [Install)中可配置的完整选项列表

可用来微调单元配置的完整选项,例 10.17 “postfix.service 单元文件” 显示系统中安装的服务单元示例。另外,可以定义单元文件选项以支持动态创建单元文件,如 第 10.6.5 节 “使用 Instantiation 单元” 所述。

例 10.17. postfix.service 单元文件

以下是 /usr/lib/systemd/system/postfix.service 单元文件,当前由 postfix 软件包提供:

[Unit]
Description=Postfix Mail Transport Agent
After=syslog.target network.target
Conflicts=sendmail.service exim.service

[Service]
Type=forking
PIDFile=/var/spool/postfix/pid/master.pid
EnvironmentFile=-/etc/sysconfig/network
ExecStartPre=-/usr/libexec/postfix/aliasesdb
ExecStartPre=-/usr/libexec/postfix/chroot-update
ExecStart=/usr/sbin/postfix start
ExecReload=/usr/sbin/postfix reload
ExecStop=/usr/sbin/postfix stop

[Install]
WantedBy=multi-user.target

[Unit] 部分描述服务,指定排序依赖关系以及冲突的单元。在 [Service] 中,指定一系列自定义脚本,在单元激活、停止和重新加载期间执行。EnvironmentFile 指向服务环境变量的定义位置,PIDFile 为服务的主进程指定稳定的 PID。最后,[Install] 部分列出了依赖于该服务的单元。

10.6.2. 创建自定义单元文件

从头开始创建单元文件有几个用例:您可以运行自定义守护进程、创建一些现有服务的第二个实例(如 例 10.19 “创建 sshd 服务第二个实例”)或导入 SysV 初始化脚本(更多在 第 10.6.3 节 “将 SysV Init 脚本转换为单元文件”中)。另一方面,如果您只修改或扩展现有单元的行为,请使用 第 10.6.4 节 “修改现有单元文件” 中的说明。以下流程描述了创建自定义服务的一般过程:

  1. 使用自定义服务准备可执行文件。这可以是自定义创建的脚本,也可以是软件供应商提供的可执行文件。如果需要,准备 PID 文件来保存自定义服务主要进程的恒定 PID。也可以包含环境文件来存储该服务的 shell 变量。确保源脚本可执行(通过执行 chmod a+x)且不进行交互。
  2. /etc/systemd/system/ 目录中创建一个单元文件,并确定它有正确的文件权限。以 root 用户身份执行:

    touch /etc/systemd/system/name.service
    chmod 664 /etc/systemd/system/name.service

    使用要创建的服务的名称替换 name。请注意,该文件不需要可执行。

  3. 打开上一步中创建的 name.service 文件并添加服务配置选项。根据您要创建的服务类型,可以使用各种选项,请参阅 第 10.6.1 节 “了解单元文件结构”。以下是网络相关服务的单元配置示例:

    [Unit]
    Description=service_description
    After=network.target
    
    [Service]
    ExecStart=path_to_executable
    Type=forking
    PIDFile=path_to_pidfile
    
    [Install]
    WantedBy=default.target

    其中:

    • service_description 是一个说明性描述,在 journal 日志文件和 systemctl status 命令的输出中显示。
    • After 设置可确保仅在网络运行时启动该服务。添加以空格分隔的其他相关服务或目标列表。
    • path_to_executable 代表到实际可执行服务的路径。
    • type=forking 用于生成 fork 系统调用的守护进程。该服务的主要进程使用 path_to_pidfile 中指定的 PID 创建。在 表 10.10 “重要 [Service] 部分选项” 中查找其他启动类型。
    • WantedBy 指出该服务应该启动的目标。将这些目标视为旧运行级别概念的替代,详情请查看 第 10.3 节 “使用 systemd 目标”
  4. root 用户身份执行以下命令来通知 systemd 是否存在新 name.service 文件:

    systemctl daemon-reload
    systemctl start name.service
    警告

    在创建新的单元文件或修改现有单元文件后,始终运行 systemctl daemon-reload 命令。否则,systemctl startsystemctl enable 命令可能会因为 systemd 状态和磁盘上的实际服务单元文件不匹配而失败。

    name.service 单元现在可以与任何其他系统服务一样使用 第 10.2 节 “管理系统服务” 所述的命令进行管理。

例 10.18. 创建 emacs.service 文件

当使用 Emacs 文本编辑器时,在后台运行它通常更迅速且方便,而不是在每次编辑文件时启动新实例。以下步骤演示了如何为 Emacs 创建单元文件,这样它就可以像服务一样被处理。

  1. /etc/systemd/system/ 目录中创建一个单元文件,并确定它具有正确的文件权限。以 root 用户身份执行:

    ~]# touch /etc/systemd/system/emacs.service
    ~]# chmod 664 /etc/systemd/system/emacs.service
  2. 在文件中添加以下内容:

    [Unit]
    Description=Emacs: the extensible, self-documenting text editor
    
    [Service]
    Type=forking
    ExecStart=/usr/bin/emacs --daemon
    ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
    Environment=SSH_AUTH_SOCK=%t/keyring/ssh
    Restart=always
    
    [Install]
    WantedBy=default.target

    使用上述配置时,/usr/bin/emacs 可执行文件在服务启动时以守护进程模式启动。SSH_AUTH_SOCK 环境变量使用代表运行时目录的 "%t" 单元指定符进行设置。如果意外退出,服务还会重启 emacs 进程。

  3. 执行以下命令重新载入配置并启动自定义服务:

    ~]# systemctl daemon-reload
    ~]# systemctl start emacs.service

因为编辑器现在注册为 systemd 服务,您可以使用所有标准 systemctl 命令。例如:运行 systemctl status emacs 来显示编辑器的状态,或者 systemctl enable emacs 以便在系统引导时自动启动编辑器。

例 10.19. 创建 sshd 服务第二个实例

系统管理员通常需要配置并运行多个服务实例。这可以通过创建原始服务配置文件的副本并修改某些参数来避免与服务的主实例冲突。以下流程演示了如何创建 sshd 服务第二个实例:

  1. 创建第二个守护进程将使用的 sshd_config 文件副本:

    ~]# cp /etc/ssh/sshd{,-second}_config
  2. 编辑上一步中创建的 sshd-second_config 文件,为第二个守护进程分配不同的端口号和 PID 文件:

    Port 22220
    PidFile /var/run/sshd-second.pid

    有关 PortPidFile 选项的详情,请查看 sshd_config(5)手册页。请确定您选择的端口没有被其他服务使用。在运行该服务前,PID 文件不一定存在,它会在服务启动时自动生成。

  3. sshd 服务创建 systemd 单元文件副本:

    ~]# cp /usr/lib/systemd/system/sshd.service /etc/systemd/system/sshd-second.service
  4. 按如下方式更改上一步中创建的 sshd-second.service:

    1. 修改 Description 选项:

      Description=OpenSSH server second instance daemon
    2. 将 sshd.service 添加到 After 选项中指定的服务,因此第二实例仅在第一个实例启动后启动:

      After=syslog.target network.target auditd.service sshd.service
    3. sshd 的第一个实例包括密钥生成,因此删除ExecStartPre=/usr/sbin/sshd-keygen 行。
    4. sshd 命令添加 -f /etc/ssh/sshd-second_config 参数,以便使用其它配置文件:

      ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd-second_config $OPTIONS
    5. 在进行以上修改后,sshd-second.service 应该如下所示:

      [Unit]
      Description=OpenSSH server second instance daemon
      After=syslog.target network.target auditd.service sshd.service
      
      [Service]
      EnvironmentFile=/etc/sysconfig/sshd
      ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd-second_config $OPTIONS
      ExecReload=/bin/kill -HUP $MAINPID
      KillMode=process
      Restart=on-failure
      RestartSec=42s
      
      [Install]
      WantedBy=multi-user.target
  5. 如果使用 SELinux,请将第二个 sshd 实例的端口添加到 SSH 端口中,否则 sshd 的第二个实例将被拒绝绑定到端口:

    ~]# semanage port -a -t ssh_port_t -p tcp 22220
  6. 启用 sshd-second.service,以便在引导时自动启动:

    ~]# systemctl enable sshd-second.service

    使用 systemctl status 命令验证 sshd-second.service 是否在运行。另外,通过连接到该服务来验证是否正确启用了端口:

    ~]$ ssh -p 22220 user@server

    如果使用防火墙,请确定正确配置了防火墙以便允许到第二个 sshd 实例的连接。

要了解如何正确地选择自定义单元文件排序和依赖项的目标,请查看以下文章。

如需包含单元文件中排序和依赖项所触发的一些真实示例的其他信息,请参见以下文章: 是否存在有关编写单元文件的任何有用信息?

如果要为 systemd 启动的服务设置限制,请参阅红帽知识库文章 如何在 RHEL 7 和 systemd 中为服务设置限制。这些限制需要在服务的单元文件中设置。请注意,systemd 忽略 /etc/security/limits.conf/etc/security/limits.d/*.conf 配置文件中设定的限制。这些文件中定义的限制在启动登录会话时由 PAM 设置,但 systemd 启动的守护进程不使用 PAM 登录会话。

10.6.3. 将 SysV Init 脚本转换为单元文件

在花费时间将 SysV 初始化脚本转换为单元文件之前,请确保在别处尚未执行转换。Red Hat Enterprise Linux 7 中安装的所有核心服务都有默认的单元文件,许多第三方软件包也是如此。

将初始化脚本转换成单元文件需要分析脚本并从中提取所需信息。根据这个数据,您可以创建一个单元文件,如 第 10.6.2 节 “创建自定义单元文件” 所述。因为初始化脚本可能会有很大差异,具体取决于服务类型,因此您可能需要使用比本章中介绍的更多配置选项进行转换。请注意,systemd 单元不再支持 init 脚本提供的某些级别的自定义,请参阅 第 10.1.2 节 “兼容性更改”

脚本标题中提供了转换所需的大部分信息。以下示例显示了在 Red Hat Enterprise Linux 6 中启动 postfix 服务初始化脚本的打开部分:

#!/bin/bash
#
# postfix   Postfix Mail Transfer Agent
#
# chkconfig: 2345 80 30
# description: Postfix is a Mail Transport Agent, which is the program \
#       that moves mail from one machine to another.
# processname: master
# pidfile: /var/spool/postfix/pid/master.pid
# config: /etc/postfix/main.cf
# config: /etc/postfix/master.cf

### BEGIN INIT INFO
# Provides: postfix MTA
# Required-Start: $local_fs $network $remote_fs
# Required-Stop: $local_fs $network $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop postfix
# Description: Postfix is a Mail Transport Agent, which is the program that
#       moves mail from one machine to another.
### END INIT INFO

在上例中,只有以 # chkconfig# description 开头的行是强制的,因此您可能不会在不同的 init 文件中找到其他行。# BEGIN INIT INFO # END INIT INFO 行间包含的文本称为 Linux Standard Base(LSB)标头。如果指定,LSB 标头包含定义服务描述、依赖项和默认运行级别的指令。下面是一个分析任务概述,旨在收集新单元文件所需的数据。postfix init 脚本被用作示例,请参阅 例 10.17 “postfix.service 单元文件” 中生成的 postfix 单元文件。

查找服务描述

在以 #description 开头的行中找到有关脚本的描述性信息。将此描述与单元文件的 [Unit] 部分中的 Description 选项中的服务名称一同使用。LSB 标头可能会在 #Short-Description 和 #Description 行中包含类似的数据。

查找服务依赖项

LSB 标头可能包含一些在服务间组成相依性指令。大多数可以转换到 systemd 单元选项,请参阅 表 10.12 “LSB 标头中的依赖项选项”

表 10.12. LSB 标头中的依赖项选项

LSB 选项描述单元文件的对等

Provides

指定服务的引导工具名称,可在其他初始化脚本中引用(使用"$"前缀)。因为单元文件根据文件名指向其他单元,所以不再需要这个操作。

Required-Start

包含所需服务的引导工具名称。这被转换为排序依赖关系,引导工具名称替换为相应服务或所属服务的单元文件名。例如,如果是 postfix,$network 上的 Required-Start 依赖关系被转换为 network.target 上的 After 依赖关系。

After, Before

Should-Start

比 Required-Start 更弱的依赖项。Should-Start 依赖项失败不会影响服务的启动。

After, Before

required-Stop, Should-Stop

组成负依赖关系。

Conflicts

查找服务的默认目标

#chkconfig 开头的行包含三个数字值。最重要的是第一个代表启动该服务的默认运行级别的数字。使用 表 10.6 “SysV 运行级别与 systemd 目标比较” 将这些运行级别映射到对等的 systemd 目标。然后,在单元文件的 [ Install] 部分中列出这些目标。例如,postfix 以前 是在 运行级别 2、3、4 和 5 中启动的,它们转换为 Red Hat Enterprise Linux 7 中的 multi-user.target 和 graphical.target。请注意,graphical.target 依赖于 multiuser.target,因此不需要同时指定,如 例 10.17 “postfix.service 单元文件” 中所示。您可能会在 LSB 标头的 #Default-Start#Default-Stop 行中找到默认和禁止运行级别的信息。

#chkconfig 行中指定的另外两个值代表初始化脚本的启动和关闭优先级。如果 systemd 加载初始化脚本,则对这些值进行解释,但没有等效的单元文件。

查找服务使用的文件

初始化脚本需要从专用目录中载入功能库,并允许导入配置、环境和 PID 文件。环境变量在初始化脚本标头中以 #config 开始的行上指定,该行转换为 EnvironmentFile 单元文件选项。在 #pidfile init 脚本行中指定的 PID 文件通过 PIDFile 选项导入到单元文件中。

未包含在初始化脚本标头中的关键信息是该服务可执行文件的路径,以及该服务可能需要的一些其他文件。在之前的 Red Hat Enterprise Linux 版本中,init 脚本使用 Bash case 语句来定义服务对默认操作的行为,如 start、stoprestart,以及自定义定义的操作。以下摘录自 postfix 初始化脚本显示了要在 service 启动时执行的代码块:

conf_check() {
  [ -x /usr/sbin/postfix ] || exit 5
  [ -d /etc/postfix ] || exit 6
  [ -d /var/spool/postfix ] || exit 5
}

make_aliasesdb() {
	if [ "$(/usr/sbin/postconf -h alias_database)" == "hash:/etc/aliases" ]
	then
		# /etc/aliases.db might be used by other MTA, make sure nothing
		# has touched it since our last newaliases call
		[ /etc/aliases -nt /etc/aliases.db ] ||
			[ "$ALIASESDB_STAMP" -nt /etc/aliases.db ] ||
			[ "$ALIASESDB_STAMP" -ot /etc/aliases.db ] || return
		/usr/bin/newaliases
		touch -r /etc/aliases.db "$ALIASESDB_STAMP"
	else
		/usr/bin/newaliases
	fi
}

start() {
	[ "$EUID" != "0" ] && exit 4
	# Check that networking is up.
	[ ${NETWORKING} = "no" ] && exit 1
	conf_check
	# Start daemons.
	echo -n $"Starting postfix: "
	make_aliasesdb >/dev/null 2>&1
	[ -x $CHROOT_UPDATE ] && $CHROOT_UPDATE
	/usr/sbin/postfix start 2>/dev/null 1>&2 && success || failure $"$prog start"
	RETVAL=$?
	[ $RETVAL -eq 0 ] && touch $lockfile
    echo
	return $RETVAL
}

init 脚本的可扩展性允许指定从 start ()函数块调用的两个自定义功能:conf _check ()和 make_aliasesdb ( )。然后,上面的代码中提到几个外部文件和目录:主服务可执行文件 /usr/sbin/postfix/etc/postfix//var/spool/postfix/ 配置目录,以及 /usr/sbin/postconf/ 目录。

systemd 仅支持预定义的操作,但启用了通过 ExecStart、ExecStartPre、ExecStart Post、ExecStartPost 和Exec Reload 选项执行自定义可执行文件。如果红帽企业 Linux 7 上有 postfix,则 service start 将执行 /usr/sbin/postfix 以及支持脚本。请参考 例 10.17 “postfix.service 单元文件” 上的 postfix 单元文件。

转换复杂的 init 脚本需要了解脚本中的每个语句的用途。其中一些语句特定于操作系统版本,因此您不需要转换它们。另一方面,新环境中可能需要进行一些调整,无论是在单元文件还是服务可执行文件中,还是在支持文件中。

10.6.4. 修改现有单元文件

在系统中安装的服务会附带保存在 /usr/lib/systemd/system/ 目录中的默认单元文件。系统管理员不应该直接修改这些文件,因此任何自定义都必须仅限于 /etc/systemd/system/ 目录中的配置文件。根据所需更改的程度,选择以下方法之一:

  • /etc/systemd/system/unit.d/ 中创建一个附加配置文件的目录。我们推荐在大多数用例中使用这个方法。它启用了额外的功能来扩展默认配置,同时仍然引用原始的单元文件。因此,软件包升级引入的默认单元的更改会被自动应用。如需更多信息,请参阅 “扩展默认单元配置”一节
  • /etc/systemd/system/ 中创建原始单元文件 /usr/lib/systemd/system/ 的副本并在此进行修改。这个副本会覆盖原始文件,因此不会应用软件包更新带来的更改。这个方法对无论软件包更新都应保留的重要单元更改都很有用。详情请查看 “覆盖默认单元配置”一节

要返回这个单元的默认配置,请删除 /etc/systemd/system/ 中的自定义配置文件。要在不重启系统的情况下对单元文件应用更改,请执行:

systemctl daemon-reload

daemon-reload 选项会重新载入所有单元文件并重新创建依赖项树,这是立即将任何更改应用到单元文件所必需的。另外,您可以使用以下命令获得相同的结果:

init q

另外,如果修改后的单元文件属于正在运行的服务,则必须重启该服务以接受新设置:

systemctl restart name.service
重要

要修改由 SysV initscript 处理的服务的属性(如依赖项或超时),请不要修改 initscript 本身。相反,请为服务创建一个 systemd 置入配置文件,如 “扩展默认单元配置”一节“覆盖默认单元配置”一节 所述。然后,以与普通 systemd 服务相同的方式管理此服务。

例如:要扩展 network 服务的配置,不要修改 /etc/rc.d/init.d/network initscript 文件。反之,创建新目录 /etc/systemd/system/network.service.d/ 和一个 systemd drop-in 文件 /etc/systemd/system/network.service.d/my_config.conf。然后将修改的值放到 drop-in 文件中。注: systemd 知道 network 服务为 network.service,这就是为什么创建的目录必须名为 network.service.d

扩展默认单元配置

要使用附加配置选项扩展默认单元文件,请首先在 /etc/systemd/system/ 中创建配置目录。如果扩展服务单元,以 root 用户身份执行以下命令:

mkdir /etc/systemd/system/name.service.d/

使用您要扩展的服务的名称替换 name。以上语法适用于所有单元类型。

在上一步中创建的目录中创建配置文件。 请注意,文件名必须以.conf 后缀结尾。类型:

touch /etc/systemd/system/name.service.d/config_name.conf

使用配置文件的名称替换 config_name。这个文件遵循一般的单元文件结构,因此所有指令都必须在适当的部分指定,请参阅 第 10.6.1 节 “了解单元文件结构”

例如,要添加自定义依赖项,请使用以下内容创建配置文件:

[Unit]
Requires=new_dependency
After=new_dependency

这里的 new_dependency 代表这个单元被标记为依赖项。另一个例子是主进程退出后重新启动服务的配置文件,延迟 30 秒:

[Service]
Restart=always
RestartSec=30

建议您创建仅关注一项任务的小配置文件。这些文件可轻松地移动或者链接到其他服务的配置目录。

要应用对单位所做的更改,以 root 用户身份执行:

systemctl daemon-reload
systemctl restart name.service

例 10.20. 扩展 httpd.service 配置

要修改 httpd.service 单元,以便在启动 Apache 服务时自动执行自定义 shell 脚本,请执行以下步骤。首先,创建目录和自定义配置文件:

~]# mkdir /etc/systemd/system/httpd.service.d/
~]# touch /etc/systemd/system/httpd.service.d/custom_script.conf

如果想要用 Apache 自动启动的脚本位于 /usr/local/bin/custom.sh,在 custom_script.conf 文件中插入以下文本:

[Service]
ExecStartPost=/usr/local/bin/custom.sh

要应用单元更改,请执行:

~]# systemctl daemon-reload
~]# systemctl restart httpd.service
注意

/etc/systemd/system/ 配置文件中的配置文件优先于 /usr/lib/systemd/system/ 中的单元文件。因此,如果配置文件包含只能指定一次的选项,如 DescriptionExecStart,则此选项的默认值将被覆盖。请注意,在 systemd-delta 命令的输出中,“监控覆盖单元”一节 中描述的这些单元总是被标记为 [EXTENDED],即使在 sum 中,某些选项实际上会被覆盖。

覆盖默认单元配置

要在更新提供该单元文件的软件包后保留更改,首先要将该文件复制到 /etc/systemd/system/ 目录。要做到这一点,以 root 用户身份执行以下命令:

cp /usr/lib/systemd/system/name.service /etc/systemd/system/name.service

其中 name 代表您希望修改的服务单元的名称。以上语法适用于所有单元类型。

使用文本编辑器打开复制的文件,并进行必要的修改。要应用单元更改,以 root 用户身份执行:

systemctl daemon-reload
systemctl restart name.service

例 10.21. 更改超时限制

您可以为每个服务指定一个超时值,以防止出现故障的服务中断。否则,一般服务的超时时间会被默认设置为 90 秒,SysV 兼容的服务会被设置为 300 秒。

例如:要为 httpd 服务扩展超时限制:

  1. httpd 单元文件复制到 /etc/systemd/system/ 目录中:

    cp /usr/lib/systemd/system/httpd.service /etc/systemd/system/httpd.service
  2. 打开文件 /etc/systemd/system/httpd.service,并在 [Service] 部分指定 TimeoutStartSec 值:

    ...
    [Service]
    ...
    PrivateTmp=true
    TimeoutStartSec=10
    
    [Install]
    WantedBy=multi-user.target
    ...
  3. 重新载入 systemd 守护进程:

    systemctl daemon-reload
  4. Optional.验证新的超时值:

    systemctl show httpd -p TimeoutStartUSec
注意

要全局更改超时限制,在/etc/systemd/system.conf 中输入 DefaultTimeoutStartSec。请参阅 第 10.1 节 “systemd 简介”

监控覆盖单元

要显示覆盖或修改的单元文件概述,请使用以下命令:

systemd-delta

例如,以上命令的输出结果如下:

[EQUIVALENT] /etc/systemd/system/default.target → /usr/lib/systemd/system/default.target
[OVERRIDDEN] /etc/systemd/system/autofs.service → /usr/lib/systemd/system/autofs.service

--- /usr/lib/systemd/system/autofs.service   2014-10-16 21:30:39.000000000 -0400
+++ /etc/systemd/system/autofs.service 2014-11-21 10:00:58.513568275 -0500
@@ -8,7 +8,8 @@
 EnvironmentFile=-/etc/sysconfig/autofs
 ExecStart=/usr/sbin/automount $OPTIONS --pid-file /run/autofs.pid
 ExecReload=/usr/bin/kill -HUP $MAINPID
-TimeoutSec=180
+TimeoutSec=240
+Restart=Always

 [Install]
 WantedBy=multi-user.target

[MASKED]   /etc/systemd/system/cups.service → /usr/lib/systemd/system/cups.service
[EXTENDED]  /usr/lib/systemd/system/sssd.service → /etc/systemd/system/sssd.service.d/journal.conf

4 overridden configuration files found.

表 10.13 “systemd-delta 差别类型” 列出 systemd-delta 输出中可能出现的覆盖类型。请注意,如果文件被覆盖, systemd-delta 默认会显示与 diff 命令输出类似的总揽。

表 10.13. systemd-delta 差别类型

类型描述

[MASKED]

屏蔽的单元文件,请参阅 第 10.2.7 节 “禁用服务” 以了解单元屏蔽的信息。

[EQUIVALENT]

未修改的副本会覆盖原始文件,但在内容上没有变化,通常是符号链接。

[REDIRECTED]

重定向到另一个文件的文件。

[OVERRIDEN]

覆盖和更改文件。

[EXTENDED]

使用 /etc/systemd/system/ unit.d / 目录中的 .conf 文件扩展的文件。

[UNCHANGED]

只有在使用 --type=unchanged 选项时才会显示未修改的文件。

最好在系统更新后运行 systemd-delta,以检查是否对当前由自定义配置覆盖的默认单元进行了更新。也有可能将输出限制在某种差别类型。例如,要仅查看覆盖的单元,请执行:

systemd-delta --type=overridden

10.6.5. 使用 Instantiation 单元

可以在运行时使用单一模板配置文件实例化多个单元。"@"字符用于标记模板并与其关联。实例化的单元可以从另一个单元文件(使用 Requires 或者 Wants 选项)或者 systemctl start 命令启动。以下列方式命名实例化服务单元:

template_name@instance_name.service

其中 template_name 代表模板配置文件的名称。将 instance_name 替换为单元实例的名称。多个实例可以指向带有通用于单元所有实例的配置选项的同一个模板文件。模板单元名称具有以下格式:

unit_name@.service

例如,单位文件中的以下 Wants 设置:

Wants=getty@ttyA.service,getty@ttyB.service

首先为给定服务单元进行 systemd 搜索。如果没有找到这样的单元,"@" 和类型后缀之间的部分将被忽略,systemd 搜索 getty@.service 文件,从中读取配置并启动服务。

通配符字符(称为单元指定符 )可用于任何单元配置文件中。单元指定符替换某些单元参数,并在运行时解释。表 10.14 “重要单元指定符” 列出对模板单元特别有用的单元指定符。

表 10.14. 重要单元指定符

单元指定符含义描述

%n

完整单元名称

代表完整的单元名称,包括类型后缀。%N 具有相同的含义,但也将禁止的字符替换为 ASCII 代码。

%p

前缀名称

代表删除了类型后缀的单元名称。对于实例化单元 %p 代表"@"字符前面的单元名称的一部分。

%i

实例名称

是"@"字符和类型后缀之间的实例化单元名称的一部分。%I 具有相同的含义,但也会替换 ASCII 代码的禁止字符。

%H

主机名

代表在载入单元配置时的运行系统的主机名。

%t

运行时目录

代表运行时目录,对于 root 用户是 /run,对于非特权用户是 XDG_RUNTIME_DIR 变量的值。

有关单元指定符的完整列表,请参见 systemd.unit(5) 手册页。

例如, getty@.service 模板包含以下指令:

[Unit]
Description=Getty on %I
...
[Service]
ExecStart=-/sbin/agetty --noclear %I $TERM
...

当 getty@ttyA.service 和 getty@ttyB.service 实例化为上述模板时,Description= Getty on ttyA 和 Getty on ttyB 解析Getty on ttyB