CentOS7-systemd

systemd是CentOS7所使用的全新的init程序.

Systemd:

POST -> Boot Sequence -> Bootloader -> kernel + initramfs(initrd) -> rootfs -> /sbin/init

systemd的架构图:

systemd

Systemd

之前就已经说过, CnetOS5,6上的运行级别这个概念在systemd上已经失去了意义.但是systemd依然可以兼容之前的SysV init脚本, 但是即使不使用脚本, systemd仍然可以进行服务的启动.但是, 当然, 使用脚本的启动速度是很慢的.

接下来说一下Systemd的新特性有哪些:

  • 实现系统引导的时候服务的并行启动
  • 可以实现按需激活进程, 也就是说在需要这个进程的时候才去激活它, 这样就比较节省资源
  • 支持系统状态快照 ( 蛤?这也可以?没用过呢…)
  • 基于依赖关系来定义服务控制逻辑

因此到了CentOS7上, systemd已经完全取代了之前的init.

这么说来, CentOS6所使用的Startup init是十分短命的, 而当时Systemd的开发者就已经建议RH使用Systemd instead of StartUp了, 然而我们都知道RH以安全著称, 因此并没有使用. 当时的新版Fedora就使用了(小白鼠).

对于Systemd而言有一个核心概念 – unit 也就是单元

而这个unit的配置主要是依靠其配置文件进行标示和配置: 文件主要包括了系统服务,监听的套接字, 保存的系统快照以及其他和init相关的信息. 每一个unit都有一个配置文件. 这些配置文件都保存在这些位置:

/usr/lib/systemd/system && /run/systemd/system&&/etc/systemd/system

目前我们可以简单的把unit当做之前的一个一个服务脚本, 负责进行服务的启动, 重载, 终止, 重启等等… 但要明确的是, 决不能把unit和这些服务脚本画上等号. 这是因为, unit除了进行这些服务的管理之外还有别的功能.归根结底: unit是有类型的

Unit的类型

其中最多最重要的就是这个服务类型的unit,叫做Service Unit 这些文件的扩展名就是*.service 比如: httpd.service sshd.service crond.service 等等

在之前说init的时候, 我们可以在/etc/rc.d/init.d/里面看到许许多多的的服务脚本 而在systemd来看这些类似的东西都放在/usr/lib/systemd/system 里面, 这个目录里还有很多其他扩展名的东西: path target wants …

而且如你所见, 这些文件不需要执行权限 因为他们仅仅是systemd的配置文件.

除开service, 第二重要的配置文件就是target类型的, 叫做目标单元. 主要用于模拟实现”运行级别”

第三种叫做device unit, 文件扩展名就是device, 这个文件主要用来负责定义内核识别的设备.

接着还有进行文件系统挂载的mount unit, 也就是用来定义文件系统挂载点

还有socket unit, 后缀名.socket, 用来标识进程间进行通信的socket文件.

另外, 还有snapshot unit, 后缀名就叫做.snapshot, 用于管理系统快照.

下一个swap unit, 后缀.swap, 用来干什么也很容易就知道了: 表示和管理swap设备.

最后还有两项, 一个后缀是叫.automount, 用来定义文件系统的自动挂载点, 以及Path unit, 后缀名就是.path, 用来定义文件系统中与一个文件或者一个目录. 仅在某些特定的场景下才会使用.

以上这些就是unit的类型, 用一张图来说明就是:

Unit

systemd的关键特性

基于socket的激活机制

在系统引导的时候systemd对所有支持这种模式的服务, 分别创建需要监听的套接字, 并在服务启动之后, 立即将这个套接字传递给它.

这样做的好处是: 可以实现多个服务的并行启动, 而且如果用不到这个服务的时候我就不启动, 反正套接字创建好了(我不关心服务的启动与否),这样当有人访问套接字, systemd就会收到, 接着就可以去激活相应的进程服务, 也就是按需激活. 这就像CentOS5,6上的超级守护进程和瞬时启动进程.

其实也就是: 1. socket和服务程序分离, 由一个统一的systemd来进行启动. 2. 只要我套接字建立完毕,那么其他依赖服务都可以进行启动(因为认定已经启动), 从而实现并行启动.

基于bus的激活机制

D-bus是一个最早见于Linux的IPC实现, 用于桌面. 其实就是一个用于桌面应用的消息系统/通讯机制.

systemd可以使得所有使用D-Bus的服务在第一次访问的时候按需激活.

基于Device的激活机制

就像是在Windows上插入U盘之后就会有相应的进程被激活一样, 这一机制使得当我们有硬件上的更新(可用)的时候有systemd有能力激活相应服务.(就比如说automount)

基于Path的激活机制

也就是某个目录下文件发生变动的时候, 激活相应的程序.说白了就是特定文件路径的监控从而激活.

系统快照

就像我们所使用的vmware等虚拟化应用的暂停功能一样. systemd能够将当前系统的各个unit的临时数据(当前状态信息)进行持久化(写入磁盘..), 说白了就是保存状态. 然后再下次开机的时候载入这些文件从而使得系统恢复之前的状态.

向后兼容sysV init脚本

这个大家再熟悉不过了, 最起码systemd可以完美兼容start, stop, status, restart这样的操作脚本. 而CentOS7 也保留了这种风格.

以上就是systemd的关键特性, 现在我们说说他和sysV不兼容(不同)的地方以及一些point

  • systemd对之前sysV的系统运行级别的模拟和兼容是有限的, 0,6,3,5, 事实上systemd用不到这个东西, 他只是通过target unit来进行模拟.
  • target unit和之前的运行级别是不能做对等的, 因为target unit有十几个之多.
  • systemd使用systemctl来进行服务操作的命令, 其支持的子命令不仅仅是上述4个. 在sysV时代, 用户可以进行拓展和自定义脚本, 但在systemd中, 子命令是固定不变的.
  • systemd只能与systemd相通信的应用通信, 也就是说非systemd的应用他没法管.(比如service启动的)
  • 我们知道在init的时候, 当我们进行运行级别的切换的时候, 会执行/etc/rcX.d/init.d/对应级别的脚本, 所有K开头的都会执行stop操作, 而S的进行start操作. 但是这样存在的问题就是, 如果我的服务没有启动,还是会执行一遍stop操作, 这就是多余操作. 因此systemd就不会这样了, systemd会动态进行关闭操作, 仅关闭需要关闭以及当前正在运行的服务和程序.同理,S开头的也是.
  • 系统服务不会读取来自标准输入的数据,systemd启动时不会和标准输入进行交互,他自行控制着所以服务的管理.
  • 我们的系统服务不会从用户那里继承任何信息, 包括PATH等环境变量等等. 因此建议在使用应用程序使用绝对路径 这样每个服务都会在最纯净的运行环境中, 而和用户没有任何关系.
  • 每个服务operation的timeout都设置成5min

管理系统服务

使用service unit, 也可以兼容早期的运行脚本. 运行的命令叫做: systemctl 完整的一个命令是: systemctl COMMAND name.service

比如我们现在以久负盛名,大名鼎鼎的httpd.service来做个实验:

1
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
[root@WWW ~]# rpm -q httpd
httpd-2.4.6-45.el7.centos.4.x86_64 # 系统已经安装此软件包
# 默认可以省略.service, 也就是说 你可以直接写应用的名字
[root@WWW ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: inactive (dead)
Docs: man:httpd(8)
man:apachectl(8)
# 显然此时服务是非激活的, 也就是未启动.
[root@WWW ~]# systemctl start httpd
# 启动服务了, 现在来看一下当前服务的状态
[root@WWW ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled) # disabled 表示该服务不会随着开机启动
Active: active (running) since Sun 2017-09-03 08:42:26 EDT; 2s ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 2553 (httpd)
Status: "Processing requests..."
# 刚启动的状态, 处理请求
CGroup: /system.slice/httpd.service
├─2553 /usr/sbin/httpd -DFOREGROUND
├─2554 /usr/sbin/httpd -DFOREGROUND
├─2555 /usr/sbin/httpd -DFOREGROUND
├─2556 /usr/sbin/httpd -DFOREGROUND
├─2557 /usr/sbin/httpd -DFOREGROUND
└─2558 /usr/sbin/httpd -DFOREGROUND

Sep 03 08:42:26 WWW systemd[1]: Starting The Apache HTTP Server...
Sep 03 08:42:26 WWW httpd[2553]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.168.56.101. Set the 'ServerNa...his message
Sep 03 08:42:26 WWW systemd[1]: Started The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.
[root@WWW ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: active (running) since Sun 2017-09-03 08:42:26 EDT; 6min ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 2553 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec"
# 状态出现了更新, 没有请求, 流量为0
CGroup: /system.slice/httpd.service
├─2553 /usr/sbin/httpd -DFOREGROUND
├─2554 /usr/sbin/httpd -DFOREGROUND
├─2555 /usr/sbin/httpd -DFOREGROUND
├─2556 /usr/sbin/httpd -DFOREGROUND
├─2557 /usr/sbin/httpd -DFOREGROUND
└─2558 /usr/sbin/httpd -DFOREGROUND

Sep 03 08:42:26 WWW systemd[1]: Starting The Apache HTTP Server...
Sep 03 08:42:26 WWW httpd[2553]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.168.56.101. Set the 'ServerNa...his message
Sep 03 08:42:26 WWW systemd[1]: Started The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.
# 现在停止服务
[root@WWW ~]# systemctl stop httpd

在旧的sysV时代, 我们的status只有一句干巴巴的说明, 服务已经启动/服务没有启动.. 现在的systemd可以提供非常详细的信息来让我们进行debug或者查看相关信息.

现在我们来列举一下systemctl

  • 启动: systemctl start name.service == service name start
  • 停止: systemctl stop name.service == service name stop
  • 重启: systemctl restart name.service == service name restart
  • 状态: systemctl status name.service == service name status
  • 条件式重启: systemctl try-restart name.service == service name condrestart # 条件式重启是指: 如果这个服务启动了, 那么就停掉他再进行启动操作, 如果没有启动就不作操作.
  • 查看某服务当前是否激活: systemctl is-active name.service
  • 查看所有已经激活的服务: systemctl list-units
    • 查看所有的 在后面加上 –all, 可以组合
    • 查看特定的类型的 在后面加上 –type={target,service…}, 可以组合.

与chkconfig的对应关系:

设定某服务开机自启/关闭: chkconfig name on/off ==> systemctl enable/disable name.service

比disable更强大的禁止自启是: systemctl mask name.service 这将会连接到/dev/null, 从而使启动这些变成不可能. 翻过来就是systemctl umask name.service 可以取消mask的效果.

为了查看服务是否开机自启, 上面也已经展示了, 使用systemctl status name.service就可以了.

如果要查看所有开机自启的服务, 在chkconfig时代, 还是使用: chkconfig –list

但是systemctl就提供了非常清晰的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@WWW ~]# systemctl list-unit-files --type=service
# 这样展示的是所有的unit, 其中有disabled的, 有enabled的, 有static的
# 如果仅仅想要列出特定STATE的, 就在后面加上筛选条件就行了
UNIT FILE STATE
arp-ethers.service disabled
atd.service enabled
auditd.service enabled
autovt@.service enabled
blk-availability.service disabled
brandbot.service static
chrony-dnssrv@.service static
...(omitted)
[root@WWW ~]# systemctl list-unit-files --type=service --state=enabled
# 别忘了要加d
UNIT FILE STATE
atd.service enabled
auditd.service enabled
autovt@.service enabled
chronyd.service enabled
crond.service enabled
dbus-org.fedoraproject.FirewallD1.service enabled
dbus-org.freedesktop.NetworkManager.service enabled
dbus-org.freedesktop.nm-dispatcher.service enabled
...(omitted)

现在我们就来谈谈这个target unit.

我们说过这个是用来实现运行级别的模拟的. 因此所有以target结尾的文件就是用来将需要启动的服务定义在一起, 使得他们能够一起启动.

/usr/lib/systemd/system这个目录下, 我们可以看到:

1
2
3
4
5
6
7
lrwxrwxrwx. 1 root root  15 Jun 25 08:38 runlevel0.target -> poweroff.target
lrwxrwxrwx. 1 root root 13 Jun 25 08:38 runlevel1.target -> rescue.target
lrwxrwxrwx. 1 root root 17 Jun 25 08:38 runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 Jun 25 08:38 runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 Jun 25 08:38 runlevel4.target -> multi-user.target
lrwxrwxrwx. 1 root root 16 Jun 25 08:38 runlevel5.target -> graphical.target
lrwxrwxrwx. 1 root root 13 Jun 25 08:38 runlevel6.target -> reboot.target

这些就是在模拟不同的运行级别, 可以很清晰的看到2,3,4都是一样的. 我们在使用init的时候, 切换运行等级的命令是init N. 而在systemd中使用的是以子命令的形式: systemctl isolate name.target 另外建议在使用systemd的机器上就不再要用runlevel来查看运行等级了, 因为意义不大.当然使用也是可以使用的. 那么应该使用什么命令来查看. 在上面已经说过了!其实就是systemctl list-units --type target 这会展示当前已经加载的unit. 如果是获取默认的运行级别, 在过去使用的/etc/inittab, 现在使用的命令是systemctl get-default 如果是修改默认的运行级别呢? 过去我们要修改iinittab这个文件, 但是现在封装的更友好, 我们直接使用systemctl set-default name.target就行了. 其实这一步骤做的操作就是:

1
2
3
4
5
[root@WWW system]# systemctl get-default
multi-user.target
[root@WWW system]# systemctl set-default multi-user.target
Removed symlink /etc/systemd/system/default.target. # 删除符号链接
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target. # 创建一个新的符号链接

所以默认的target就取决于这个软链接的指向.

systemd可以非常快速的切换到救急模式和紧急模式, 命令如下:

1
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
[root@WWW ~]# systemctl emergency
PolicyKit daemon disconnected from the bus.
We are no longer a registered authentication agent.

Broadcast message from root@WWW on pts/0 (Mon 2017-09-04 07:51:33 EDT):

The system is going down to emergency mode NOW!
# 会有一个广播, 告知正在使用这台机器的用户.
# 来看一下现在机器的状态.
[root@WWW ~]# systemctl list-units --type target
UNIT LOAD ACTIVE SUB DESCRIPTION
emergency.target loaded active active Emergency Mode

LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.

1 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
# 仅仅只载入了一个target文件
[root@WWW ~]# systemctl rescue
Error getting authority: Error initializing authority: Could not connect: Connection refused (g-io-error-quark, 39)
[root@WWW ~]# systemctl list-units --type target
UNIT LOAD ACTIVE SUB DESCRIPTION
cryptsetup.target loaded active active Encrypted Volumes
local-fs-pre.target loaded active active Local File Systems (Pre)
local-fs.target loaded active active Local File Systems
rescue.target loaded active active Rescue Mode
swap.target loaded active active Swap
sysinit.target loaded active active System Initialization

LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.

6 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
# 进入了rescue模式
[root@WWW ~]# systemctl isolate multi-user.target
Error getting authority: Error initializing authority: Could not connect: Connection refused (g-io-error-quark, 39)
# 现在就切换回来吧

那么emergency和rescue的区别是什么呢? 从加载的target模块就可以看出来了. emergency更加彻底.

在CentOS6上, 我们也可以使用emergency模式, 只需要在grub的e 编辑模式下, 传递emergency这个参数就可以了.

最后我们再来说一下其他的常用命令:

关机, hhh没有想到这个我也会说吧~ 熟知的shutdown当然也可以使用, 但是你也可以使用systemct halt. 同时也可以使用systemctl poweroff 重启就是systemctl restart 我们还可以挂起系统: 使用systemctl suspend 如果你是虚拟机在实验, 那么这个暂停我是不知道怎么恢复..如果你知道还请告知.使用systemctl hibernate 来使得机器休眠. 其实这个休眠就是systemd的快照功能, 他可以保持当前的工作状态, 当你回头重启的时候还可以继续下去. 当然, 如果不是仅仅做个快照, 还同时挂起, 就使用systemdctl hybrid-sleep 简单的说就是一个是重启, 一个是关闭啦 需要注意的是, 你的登录状态会保留, 所以还是要小心使用.

如果你的服务不能正常启动是因为依赖的话, 那么你可以使用systemctl的子命令来查看:

1
[root@WWW ~]# systemctl list-dependencies httpd

dependencies

所依赖的会使用树形结构表示出来, 还会注明当前的激活情况.

systemd的子命令实在是太多了. 在启用之初曾被人说, 违背了UNIX的simple原则. 当然他确实强大 方便.

Unit设定文件及自定义

关于此项, 暂时忽略. 后续补充.

其他

我们知道在Linux下找回密码只要能够进入并且挂载/, 再重新设定root的密码就可以了. 以前我们使用rescue模式/单用户来操作.但在systemd上这就没有用了.

在新版的systemd 的管理机制中,预设的rescue 模式是无法直接取得root 权限的!还是得要使用root 的密码才能够登入rescure 环境!所以我们透过一个名为rd.break的核心参数来处理!只是需要注意的是, rd.break 是在Ram Disk 里面的作业系统状态,因此你不能直接取得原本的linux 系统操作环境。所以,还需要chroot 的支援. 更由于SELinux 的问题,你可能还得要加上某些特殊的流程才能顺利的搞定root 密码的重新设置. 这样说有些不清楚. 我们来走一个流程.

首先在grub界面使用e进入编辑模式, 在标有linux16的那一行的最后加上rd.break这个核心参数.

rd

之后就可以执行了,使用ctrl+x,这时开机你会看到这样的界面

rd

这个时候我们不需要输入密码就可以进入系统! 但是注意这个时候我们进入的位置是/sysroot 并且使用mount查看当前挂载情况的时候, 你会发现当前的系统挂载了/sysroot这个目录,并且是仅仅可读不可写的.所以我们要先把他重新挂载成可读写.

1
switch_root:/# mount -o remount, rw /sysroot

接着我们进行根切换, 注意这是暂时切换.

1
2
switch_root:/# chroot /sysroot
sh-4.2#

接着就可以进行密码更改了. 你会发现这时的/sys /proc都是空的.

更改完毕后, 别忘了要先退出这个根. 然后在重新启动就可以了.

关于这些的原理和其他的一些, 在后面的GRUB2中会说明. 后面的GRUB2中会说明.