# systemctl 针对 service 类型的配置
以前需要建立系统服务,需要在 /etc/init.d/
创建对应的 bash shell script 来处理,如今在 systemd 环境下,该怎么设置相关的服务启动环境?
# 配置文件相关目录简介
systemd 的配置文件大部分在 /usr/lib/systemd/system/
目录内,Red Hat 官方文件指出,该目录的文件主要是原本软件所提供的设置,建议不要修改。修改的位置在 /etc/systemd/system/
目录内
比如:想要额外修改 vsftpd.service ,建议放到的位置如下:
/usr/lib/systemd/vsftpd.service
:官方的预设配置文件/etc/systemd/system/vsftp.service.d/custom.conf
:建立同名并已
.d
后缀结尾的目录,该目录下的文件会「累加其他设置」进入/usr/lib/systemd/vsftpd.service
意思应该是:这里是配置会覆盖掉
/usr/lib/systemd/vsftpd.service
中的同名配置/etc/systemd/system/vsftpd.service.wants/*
此目录内的文件为链接文件,设置相依服务的链接。作用是启动了 vsftpd.service 后,最好再加上该目录下的建议服务
/etc/systemd/system/vsftpd.service.requires/*
此目录内的文件为链接文件,设置相依服务的链接。作用是,在启动 vsftpd.service 之前,需要事先启动哪些服务
基本上在配置文件中,你可以自由设置相依服务的检查,并且设置加入到哪些 target 里。就是建议不要修改原始的配置文件,在上述建议目录下去操作修改
# 配置文件的设置项目简介
这次通过 sshd.service 的配置文件来讲解配置文件的内容
[root@study ~]# cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service
[Service] # 该项目于实际执行的指令参数有关
Type=notify
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install] # 此 unit 要挂载到哪个 target 下
WantedBy=multi-user.target
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
分析上述文件,大概分为三个部分:
[Unit]
unit 本身的说明,以及其他相依 daemon 的设置,包括在什么服务之后才启动此 unit 之类的设置
[Service]、[Socket][Timer][Mount][Path]...
不同的 unit type 需要使用对应的设置项目。对于 service 来说,主要在规范服务启动脚本、环境配置文件名、重新启动的方式等
[Install]
:将此 unit 安装到哪个 target 里面
配置文件内有些设置规则如下:
设置项目通常是可以重复的
例如可以设置两个 After 在配置文件中,不过,后面的设置会取代前面的,可以使用
After=
的方式,将该项清空归零如果设置参数需要有「是/否 boolean 类型值」的项目,可以使用 1、yes、true、on 代表启动,0、no、false、off 代表关闭
空白行、开头为
#
或;
都表示批注信息
每个部分的说明如下:
# [unit]
部分
Description
使用 systemctl list-units 时,展示出来的简易说明。systemctl status 中输出的服务说明也是该值
Documentation
提供管理员能够进一步的文件查询功能,提供的文件可以是如下的资料
=http://www..
=man:sshd(8)
=file:/etc/ssh/sshd_config
After:说明此 unit 是在哪个 daemon 启动之后才能启动。
基本上仅是说明启动顺序,并无强制要求里面的服务一定要启动后此 unit 才能启动。它与 Requires 的设置含义是有差异的。
Before:在什么服务启动前,启动本服务
同样,并非强制性的
Rrquires:相依性配置
明确定义此 unit 需要在哪个 daemon 启动后才能启动。如果依赖的服务没有启动,则该服务不会被启动
Wants:
此 unit 启动之后,还需要启动哪些服务。不是强制性的,只是希望建立让使用者比较好的操作环境
Conflicts:冲突服务
如果这里申明的服务已经启动,那么本 unit 就不能启动。如果我们的 unit 有启动,那么这里定义的服务不能启动。
# [service]
部分
Type:启动方式,会影响到 ExecStart。一般有如下几种
simple:默认值,主要由 ExecStart 定义的指令串来启动,启动后常驻于内存中
forking:
由 ExecStart 启动的程序通过 spawns 延伸出其他子程序来作为此 daemon 的主要服务。原生的父程序在启动结束后就会终止运行。传统的 unit 服务大多属于这种项目。
例如:httpd 这个 www 服务,当 httpd 的程序因为运行过久因此即将终结了,则 systemd 会再重新生出另一个子程序持续运行后,再将父程序删除。据说这样的效率比较好
oneshot:与 simple 类似,不过该程序工作完毕后就结束了,不会常驻内存中
dbus:与 simple 类似,但是这个 daemon 必须要在取得一个 D-Bus 的名称后,才会继续运行,因此设置该项目时,通常也要设置 BusName 才行
idle:与 simple 类似,要执行这个 daemon 必须要所有的工作都顺利执行完成后,才会执行,这里的 daemon 通常是开机到最后才执行的服务
比较重要的 simple、forking 与 oneshot,很多服务需要子程序 forking,还有很多服务只需要在开机的时候执行一次 oneshot
EnvironmentFile:指定启动脚本的环境配置文件
例如:sshd.service 的配置文件写入到
/etc/sysconfig/sshd
中,也可以直接在该项后面用多个不同的 Shell 变量来设置ExecStart:实际执行此 daemon 的指令或脚本程序
也可以使用 ExecStartPre(之前) 以及 ExecStartPost(之后)在实际启动服务前后进行额外的指令行为。但是仅支持「指令 参数 参数...」格式,不能接受
< > >> | &
等特殊字符,很多 bash 语法也不支持,如果需要这些特殊字符的时候,最好直接写到指令脚本里面。还有一种方式可以支持完整的语法;将 Type=oneshot 就可以了ExecStop:与 systemctl stop 的执行有关,关闭此服务时所执行的指令
ExecReload:与 systemctl reload 有关的指令行为
Restart:
当设置 Restart=1 时,当此 daemon 服务终止后,会再次启动该服务
除非使用 systemctl 强制将此服务关闭,否则该功能会一直生效
RemainAfterExit
当值设置为 1 时, 当该 daemon 所属的所有程序都终止后,此服务会再尝试启动。这对于 Type=oneshot 的服务有帮助
TimeoutSec:若这个服务在启动或是关闭时,因为某些缘故导致无法顺利「正常启动或正常结束」的情况下,则需要等待多久才进入「强制结束」的状态
KillMode
可以是 process、control-group、none 其中的一种:
- process:终止时,只会终止主要的程序(ExecStart 后面指令串启动的)
- control-group:则由此 daemon 所产生的其他 control-group 的程序,也会被关闭
- one:没有程序会被关闭
RestartSec:
与 Restart 有相关性,如果该服务被关闭,需要重新启动时,大概要 sleep 多少时间再重新启动。预设是 100ms
# [Install]
部分
WantedBy:该 unit 本身是附挂在哪一个 target unit 下
该项设置大部分都是
*.target unit
。大多数服务性质的 unit 都是附挂在 multi-user.target 下Also:相依性 enable
当前 unit 本身被 enable 时,Also 声明的 unit 也 enable
Alias:别名
当 systemctl enable 相关的服务时,此服务会进行连接文件的建立,以
multi-user.target
为例,它是用来预设操作环境 default.target 的规划,因此当设置用 default.target 时,/etc/systed/system/default.target
就会连结到/usr/lib/systemd/system/multi-user.target
配置讲解之后,下面用这些知识点来练习
# 两个 vsftpd 运行的实例
在上一章将 vsftpd 的 port 修改成了 555。这里再运行一个端口为 21 的 vsftpd 程序,需要两个配置文件以及两个启动脚本来设置了:
- port 21:
/etc/vsftpd/vsftpd.conf
配置文件/usr/lib/systemd/system/vsftpd.service
启动脚本
- port 555:
/etc/vsftpd/vsftpd2.conf
配置文件/usr/lib/systemd/system/vsftpd2.service
启动脚本
# 1. 建立好需要的配置文件
[root@study ~]# cd /etc/vsftpd/
[root@study vsftpd]# ll
total 20
-rw-------. 1 root root 125 Oct 31 2018 ftpusers
-rw-------. 1 root root 361 Mar 17 21:59 user_list
-rw-------. 1 root root 5199 Mar 17 23:02 vsftpd.conf
-rwxr--r--. 1 root root 338 Oct 31 2018 vsftpd_conf_migrate.sh
[root@study vsftpd]# cp vsftpd.conf vsftpd2.conf
[root@study vsftpd]# vim vsftpd.conf
# listen_port=555
# 把端口注释掉,他默认的端口是 21.
[root@study vsftpd]# diff vsftpd.conf vsftpd2.conf
131c131
< # listen_port=555
---
> listen_port=555
# 通过对比,两个文件只有端口号不同
# 2. 开始处理启动脚本设置
[root@study vsftpd]# cd /etc/systemd/system/
[root@study system]# ll | grep vsftp
# 由于我们没有额外修改过启动脚本,所以该目录下是没有 vsftp 相关的脚本的
# 从原始的启动脚本目录复制一份过来
[root@study system]# cp /usr/lib/systemd/system/vsftpd.service vsftpd2.service
[root@study system]# vim vsftpd2.service
[Unit]
Description=Vsftpd2 ftp daemon
After=network.target
[Service]
Type=forking
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd2.conf
[Install]
WantedBy=multi-user.target
# 重点只是修改 ExecStart 后面的配置文件
# 3. 重新加载 systemd 的脚本配置文件内容
# systemctl daemon-reload
[root@study system]# systemctl list-unit-files --all | grep vsftpd
vsftpd.service enabled
vsftpd2.service disabled # 已经能找到了
vsftpd@.service disabled
vsftpd.target disabled
[root@study system]# systemctl status vsftpd2.service
* vsftpd2.service - Vsftpd2 ftp daemon
Loaded: loaded (/etc/systemd/system/vsftpd2.service; disabled; vendor preset: disabled)
Active: inactive (dead)
# 由于之前直接修改的 vsftp 的配置文件,所以 vsftp 也需要重新启动
[root@study system]# systemctl restart vsftpd.service vsftpd2.service
[root@study system]# systemctl enable vsftpd.service vsftpd2.service
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd2.service to /etc/systemd/system/vsftpd2.service.
[root@study system]# systemctl status vsftpd.service vsftpd2.service
* vsftpd.service - Vsftpd ftp daemon
Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2020-03-19 16:50:22 CST; 27s ago
Main PID: 5986 (vsftpd)
CGroup: /system.slice/vsftpd.service
`-5986 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Stopped Vsftpd ftp daemon.
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Starting Vsftpd ftp daemon...
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Started Vsftpd ftp daemon.
* vsftpd2.service - Vsftpd2 ftp daemon
Loaded: loaded (/etc/systemd/system/vsftpd2.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2020-03-19 16:50:22 CST; 27s ago
Main PID: 5987 (vsftpd)
CGroup: /system.slice/vsftpd2.service
`-5987 /usr/sbin/vsftpd /etc/vsftpd/vsftpd2.conf
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Stopped Vsftpd2 ftp daemon.
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Starting Vsftpd2 ftp daemon...
Mar 19 16:50:22 study.centos.mrcode systemd[1]: Started Vsftpd2 ftp daemon.
[root@study system]# netstat -tlnp | grep vsftp
tcp6 0 0 :::555 :::* LISTEN 5987/vsftpd
tcp6 0 0 :::21 :::* LISTEN 5986/vsftpd
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
这样一个启动脚本的完成了
# 多重的重复设置方式:以 getty 为例
CentOS 7 开机后,有 6 个终端机可以使用(tty1 ~ tty6),是由 agetty 指令搞定的。终端机的功能涉及很多层面,主要管理的是 getty.target 这个 target unit,实际产生 tty1~tty6 的则是由 getty@.service 所提供的。
通过这个 getty@.service 的启动脚本来获取 @ 是啥含义
[root@study system]# cat /usr/lib/systemd/system/getty@.service
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service
# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes
# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty0
[Service]
# the VT is cleared by TTYVTDisallocate
ExecStart=-/sbin/agetty --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
[Install]
WantedBy=getty.target
DefaultInstance=tty1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
重要项: ExecStart,通过 man agetty 时发现它的语法应该是 agetty --noclear tty1
(笔者没有看懂里面哪里写的有这个,内容有点多),那么启动 6 个的话,就需要有 6 个 启动配置来分别配置启动指令 tty1~tty6,是在很繁琐,这就出现了 getty@.service
中的 @ 。先来看看他的上游 Before=getty.target
[root@study system]# systemctl show getty.target
Id=getty.target
Names=getty.target
Wants=getty@tty1.service
WantedBy=multi-user.target
Conflicts=shutdown.target
Before=multi-user.target
After=getty@tty1.service
# 这里,笔者的环境上与书上不一致了,书上是 getty@tty1.service getty@tty2.service 等 6 个
# 注意,这里笔者发现了为什么不一样
# cat /usr/lib/systemd/system/getty.target 里面内容很少,没有上面这些定义
# 但是用 show 可以显示出来,这个书上目前没有讲解
# 但是,通过切换到 tty2 tty3 上去的时候,使用 systemctl show getty.target 查看状态时,After 后面就会出现已经切换过之后的 tty。和书上展示是一样的效果
2
3
4
5
6
7
8
9
10
11
12
13
书上说,上面的 After 的配置,当 执行完 getty.target 之后,会持续要求 getty@tty1.service 等 6 个服务继续启动。那么 systemd 就会如下做:
- 先查找
/usr/lib/systemd/system/
、/etc/systemd/system/
有无getty@tty1.service
的设置,若有就启动,没有则执行下一步 - 找到
getty@.service
的设置,则将 @ 后面的数据代入成%I
的变量,进入 gett@.service 执行
也就是说 getty@tty1.service
实际上是不存在的,主要是通过 getty@.service
来执行,来简化多个执行的启动设置,他的命名方式如下:
- 源文件:执行服务名称@.service
- 执行文件:执行服务名称@范例名称.service
getty@.service
中的启动指令ExecStart=-/sbin/agetty --noclear %I $TERM
,根据 getty.target 的信息输出来看,getty@tty1.service
的 %I
的值是 tty1,因此执行脚本会变成 /sbin/agetty --noclear tty1
# 将 tty 的数量由 6 个降低到 4 个
这个配置是在 /etc/systemd/logind.conf
里面配置的。
# 1. 打开注释信息,并修改成 4 个
[root@study system]# vim /etc/systemd/logind.conf
[Login]
NAutoVTs=4
ReserveVT=0
# 2. 如果你切换到 tty5 和 tty6 的话,请将他们关闭后重启 getty.target
[root@study ~]# systemctl stop getty@tty5.service
[root@study ~]# systemctl stop getty@tty6.service
[root@study ~]# systemctl restart systemd-logind.service
2
3
4
5
6
7
8
9
当你回到桌面环境下,再切换到 tty5 和 tty6 时就切换不过去了。
到这里笔者貌似迷糊了,getty.target 只是一个定义,类似组,里面并没有定义什么,被其他的 unit 附加。有点不太明白和这里启动 1~6 个有什么关系?这里启动的是 systemd-logind.service ,而且切换只能在桌面环境下,也就是说 tty1~tty6 的切换是这个指令的功能,只是实际启动时用 getty@.service 启动的。但是具体怎么关联上的,是在是迷糊,看不懂
那么其实可以通过指令方式直接启动一个 tty。而无需其他的配置文件
systemctl start getty@tty8.service
如果只单独看这里的演示的话,笔者能明白的就是 getty@.service 是一个功能,可以通过 @ 传递参数给服务
# 暂时新增 vsftpd 到 2121 端口
vsftpd 也提供了 @ 的服务方式
[root@study ~]# ll /usr/lib/systemd/system | grep vsftpd
-rw-r--r--. 1 root root 171 Oct 31 2018 vsftpd.service
-rw-r--r--. 1 root root 89 Oct 31 2018 vsftpd.target
-rw-r--r--. 1 root root 184 Oct 31 2018 vsftpd@.service # 这里
2
3
4
[root@study ~]# cat /usr/lib/systemd/system/vsftpd@.service
[Unit]
Description=Vsftpd ftp daemon
After=network.target
PartOf=vsftpd.target
[Service]
Type=forking
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/%i.conf
[Install]
WantedBy=vsftpd.target
2
3
4
5
6
7
8
9
10
11
12
这里使用了 %i
,也就是说大小写的的变量都可以,这里启动指令拼接了一个配置文件路径 /etc/vsftpd/%i.conf
那么先创建这个配置文件,然后通过 @ 方式启动
# 1. 制作一个 vsftpd3.conf ,并吧端口修改为 2121
[root@study ~]# cd /etc/vsftpd/
[root@study vsftpd]# ll
total 28
-rw-------. 1 root root 125 Oct 31 2018 ftpusers
-rw-------. 1 root root 361 Mar 17 21:59 user_list
-rw-------. 1 root root 5201 Mar 19 16:35 vsftpd.conf
-rw-------. 1 root root 5199 Mar 19 16:35 vsftpd2.conf
-rwxr--r--. 1 root root 338 Oct 31 2018 vsftpd_conf_migrate.sh
[root@study vsftpd]# cp vsftpd.conf vsftpd3.conf
[root@study vsftpd]# vim vsftpd3.conf
listen_port=2121
# 2. 暂时启动,不要开机启动
[root@study vsftpd]# systemctl start vsftpd@vsftpd3.service
[root@study vsftpd]# systemctl status vsftpd@vsftpd3.service
* vsftpd@vsftpd3.service - Vsftpd ftp daemon
Loaded: loaded (/usr/lib/systemd/system/vsftpd@.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2020-03-19 17:48:57 CST; 5s ago
Process: 7536 ExecStart=/usr/sbin/vsftpd /etc/vsftpd/%i.conf (code=exited, status=0/SUCCESS)
Main PID: 7538 (vsftpd)
CGroup: /system.slice/system-vsftpd.slice/vsftpd@vsftpd3.service
`-7538 /usr/sbin/vsftpd /etc/vsftpd/vsftpd3.conf
Mar 19 17:48:57 study.centos.mrcode systemd[1]: Starting Vsftpd ftp daemon...
Mar 19 17:48:57 study.centos.mrcode systemd[1]: Started Vsftpd ftp daemon.
[root@study vsftpd]# netstat -tlnp | grep vsftpd
tcp6 0 0 :::2121 :::* LISTEN 7538/vsftpd
tcp6 0 0 :::555 :::* LISTEN 5987/vsftpd
tcp6 0 0 :::21 :::* LISTEN 5986/vsftpd
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
可以看到通过这种 @ 的方式,能让我们制作启动脚本的时候更为灵活一点
TIP
有一件事情,这次使用了 2121 端口,却不用修改 SELinux?因为默认启动小于 1024 以下的端口时,需要使用到 root 的权限,大于 2014 的相对来说对系统的影响可能小一些,就忽略了 SELinux 的限制了
笔者看这句话,貌似记不起有关 SELinux 端口限制的知识了?懵逼状态
# 自己的服务自己做
下面模拟自己做一个服务:制作一个可以备份自己系统的服务,这脚本放在 /backups
下,内容如下
[root@study ~]# mkdir /backups # 下面脚本,上层目录不存在,都无法保存的
[root@study ~]# vim /backups/backup.sh
#!/bin/bash
source="/etc /home /root /var/lib /var/spool/{cron,at,mail}"
target="/backups/backup-system-$(date +%Y-%m-%d).tar.gz"
[ ! -d /backups ] && mkdir /backups
tar -zcvf ${target} ${source} $> /backups/backup.log
[root@study ~]# chmod a+x /backups/backup.sh
[root@study ~]# ll /backups/backup.sh
-rwxr-xr-x. 1 root root 222 Mar 20 09:24 /backups/backup.sh
2
3
4
5
6
7
8
9
10
11
12
13
14
准备 backup.service 的启动脚本
[root@study ~]# vim /etc/systemd/system/backup.service
[unit]
Description=backup my server
Requires=atd.service
[Service]
Type=simple
ExecStart=/bin/bash -c " echo /backups/backup.sh | at now"
[Install]
WantedBy=multi-user.target
# 因为 ExecStart 里面用到了 at 指令,因此 atd.service 是一定需要的服务
[root@study ~]# systemctl daemon-reload
[root@study ~]# systemctl start backup.service
[root@study ~]# systemctl status backup.service
* backup.service - backup my server
Loaded: loaded (/etc/systemd/system/backup.service; disabled; vendor preset: disabled)
Active: inactive (dead)
Mar 20 09:30:56 study.centos.mrcode systemd[1]: Started backup my server.
Mar 20 09:30:56 study.centos.mrcode bash[17748]: job 8 at Fri Mar 20 09:30:00 2020
# 可以看到服务在执行了,状态是 inactive ,这不是一个驻留内存的服务,执行完成就退出了
# 这里笔者看不懂为啥用 echo /backups/backup.sh | at now; 而不是直接直接给定脚本路径,而且貌似脚本里面的内容执行也有问题
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26