Systemd学习和应用

简要:Systemd服务的基本操作 以及 单元文件的编写。

0x00 Systemd简介

Systemd是常用的Linux服务管理组件,其负责Linux中各种服务的调度和管理,如timedatectl可管理系统时间,journalctl管理服务日志,systemctl管理服务和系统等。

0x01 Systemctl基本操作

# 运行服务
systemctl start sshd
# 停止服务
systemctl stop sshd
# 重启服务
systemctl restart sshd
# 重新加载服务(对于优化的服务,可平滑加载配置文件而不重启其程序)
systemctl reload sshd
# 杀死服务(将直接杀死服务的进程和子进程)
systemctl kill sshd
# 服务开机启动
systemctl enable sshd
# 服务开机不启动
systemctl disable sshd

0x02 服务单元文件的编写

Systemd单元文件的默认目录在/etc/systemd/system//usr/lib/systemd/system/,/etc的具有更高的优先级。

打开/usr/lib/systemd/system/目录,可以看到一大堆.service.socket.timer.target结尾的文件和.target.wants结尾的目录。这些都是systemd需要使用到的服务单元。service表示服务单元,socket表示网络通信单元,timer表示定时执行单元、target表示一组服务。

首先我们来看看sshd服务单元的文件内容:( /usr/lib/systemd/system/sshd.service )

[Unit]
Description=OpenSSH Daemon
Wants=sshdgenkeys.service
After=sshdgenkeys.service
After=network.target

[Service]
ExecStart=/usr/bin/sshd -D
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=always

[Install]
WantedBy=multi-user.target

这便是一个service文件的基本格式。[Unit]定义该单元的基本信息和依赖关系;[Service]定义该单元开始、停止等的执行命令;[Install]定义该单元在哪个阶段自动启动。下面我们来认识一下吧。

[Unit]

Wants表示这个单元的依赖关系,After表示该单元需要在这些服务启动后才能启动。此外还有Before,表示要在这个单元之前启动。如果只有Wants而不定义After等顺序,那么这两个服务将同时启动。

启动失败的关系:若该单元Wants的服务启动失败,那么该单元也启动失败。如网络启动失败,则sshd也启动失败。

[Service]

ExecStart表示这个单元启动的命令。如在执行systemctl start sshd后将执行/usr/bin/sshd -D,此外还可定义ExecStop、ExecReload、ExecRestart。

启动方式

Systemd还支持多种启动方式,启动方式用Type定义。一些用法如下:

Type=simple 默认,进程启动后一直保持运行,systemd会根据该进程的pid等信息判断服务是否启动成功。若该进程一直存在,使用systemctl status xxx查询时会显示“Running”
Type=forking 服务开始后启动子程序,并且其父程序立即退出。可以指定PIDFile=/xxx以让systemd监视服务状态。
Type=oneshot 适用于执行脚本,表示服务启动后会立即退出。使用RemainAfterExit=yes让systemd认为其始终处于运行状态。

还有DBus、idle、notify等启动方式,有兴趣的可以参考https://wiki.archlinux.org/index.php/Systemd#Service_types

重试和重启行为

如果服务启动失败,可定义一些参数配置systemd重试的行为:

Restart=always 表示服务进程一退出便立即重启;
Restart=on-failure 表示服务启动失败时重启。

RestartSec=5 定义每次重启重试的间隔时间。

StartLimitInterval=5 表示启动间隔限制,可避免启动失败后重试过于频繁。

StartLimitBurst=10 限制启动失败重试的次数,若超过这个次数将不再尝试启动该服务。

[Install]

Install节主要需要定义WantedBy属性。表示开机启动此服务的时机。上面sshd的例子multi-user.target表示一个启动阶段。所有的启动阶段如下:

运行阶段systemd Target备注
0runlevel0.target, poweroff.targetHalt the system.
1, s, singlerunlevel1.target, rescue.targetmulti-user.target系统处于单用户模式时
2, 4runlevel2.target, runlevel4.target, multi-user.target用户定义的的target
3runlevel3.target, multi-user.target图形化界面尚未启动的多用户模式,用户可通过网络登陆多个控制台了。
5runlevel5.target, graphical.target多用户模式,已经显示图形化界面的登录界面。(通常包含阶段3的所有服务和一个图形化登陆界面)
6runlevel6.target, reboot.target重启模式
emergencyemergency.target急救模式
(参考 https://wiki.archlinux.org/index.php/Systemd#Mapping_between_SysV_runlevels_and_systemd_targets

一般来说,使用multi-user.target已经足够。要调整各个服务的依赖关系的话,可通过[Unit]中的Wants和After等属性更改。

0x03 编写一个服务单元

本节介绍一些编写自定义服务的技巧和提示。

被更新覆盖的单元文件

如果使用软件包管理器更新软件时,可能会将其自带的服务单元文件直接复制至/usr/lib/systemd/下,导致所做的更改丢失。所以建议在优先级更高的目录/etc/systemd/下创建并修改服务目录文件。

当然,也不建议将/usr/lib/systemd/下的单元文件复制到/etc/systemd/下修改,这样更新软件后,新的单元文件被复制到/usr/lib/systemd/下,却被优先级更高的/etc/systemd/下的文件顶替,导致新的单元文件没有被应用。所以,若要修改一个已经存在并且被软件包管理器管理的单元文件,我们可以使用另一种方式修改:

/etc/systemd/相应位置创建xxx.xxx.d目录,然后再在该目录下创建相应的.conf文件,systemd就会自动应用这些.conf文件内的属性值,应用顺序是按字典排序顺序。

如要修改sshd的运行方式,使其在每次服务启动时均输出一行信息:

首先在/etc/systemd/system/下创建一文件夹sshd.service.d

然后在该目录下新建一文件如10-override_exec.conf,内容写入如下:

[Service]
ExecStart=echo "Starting sshd..." && /usr/bin/sshd -D

即可完成属性的重载。

注:在Linux中,配置目录一般是/etc,直接修改/usr/share下的配置文件可不是一个好习惯,因为这个目录中的软件一般是由软件包管理器管理的,随着更新这些文件将被新版本的覆盖,而且大多数软件将其视为较低优先级的配置甚至是默认配置。

by 橙汁

模板使用

在启用一个服务时,你可能会接触到形如systemctl start foo@bar.service的情形。这里使用到了systemd的单元模板。下面让我们来看看怎么做到这一点。

在模板中,使用%i表示@后面.service之前的内容。systemd中称为instance(进程)。可以查看OpenVPN客户端的单元文件:

➜  cat /usr/lib/systemd/system/openvpn-client@.service 
[Unit]
Description=OpenVPN tunnel for %I
After=syslog.target network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO

[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/client
ExecStart=/usr/bin/openvpn --suppress-timestamps --nobind --config %i.conf
User=openvpn
Group=network
AmbientCapabilities=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
KillMode=process

[Install]
WantedBy=multi-user.target

如果单元正常启动,那么将在/etc/systemd/下相应位置创建openvpn@bar.service软链接,链接至/usr/lib/systemd/下的openvpn@.service,所以在成功执行了一次该单元相关的命令后,可方便地补全相应服务进程。

参考文献和链接

systemd配置文件及管理方法详解https://blog.csdn.net/weixin_33812433/article/details/92656944
systemd.unit 中文手册 译者:金步国
http://www.jinbuguo.com/systemd/systemd.unit.html#
systemd.service 中文手册 译者:金步国(推荐阅读)
http://www.jinbuguo.com/systemd/systemd.service.html

发表评论