systemd
是 Linux
系统的一种 中央化系统及设置管理程序(init)
,包括有守护进程
、程序库
以及应用软件
组成。目标是解决系统服务间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低系统的开销。
历史
在旧的 Linux
中,服务启动一直采用 init
进程。如:
$ /etc/init.d/apache2 start
$ service apache2 start
缺点如下:
- 启动时间长:串行启动进程
- 启动脚本复杂:
init
进程只是执行启动脚本,不管其他事情,因此脚本需要处理各种异常情况
介绍
- Systemd 是 Linux 的系统启动器之一
- 在
Unix
中通常以 d
作为系统 守护进程(daemon)
的后缀标识。Systemd
就是它要守护整个系统的进程,因此其 pid
为 1
。
$ systemctl --version
systemd 215
+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN
$ pstree 1
systemd─┬─AliSecGuard───6*[{AliSecGuard}]
├─AliYunDun───23*[{AliYunDun}]
...
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 12:07 ? 00:00:00 /lib/systemd/systemd
$ ls -lhart /sbin/init
lrwxrwxrwx 1 root root 20 Nov 21 21:10 /sbin/init -> /lib/systemd/systemd
常见 systemd unit 分类
systemctl -t help
查看 systemd 能管理的所有 unit
类型
$ systemctl -t help
Available unit types:
service
mount
swap
socket
target
device
automount
timer
path
slice
scope
.service
Service units记录由 systemd 控制和监督的进程的信息
- 查看命令
systemctl list-unit-files --type service
man systemd.service
.mount
系统的文件系统的挂载点
.swap
标识 swap 设备
.socket
Socket units 定义进程通信用到的套接字,套接字与进程是分离的
- 包括 IPC 或
网络套接字 Socket
或 文件系统FIFO
的信息,用于 socket 的激活,由 systemd 控制和监督,如 systemctl cat docker.socket
.target
Target units 是一组相关进程,定义系统启动的级别标签(用于单元分组和启动时作为同步点),systemd 没有运行级别的概念,创建标签只是为了兼容老版本
multi-user.target
多用户模式,目录 /usr/lib/systemd/system/multi-user.target.wants
中通过快捷键指定启动的服务
.device
定义系统启动时内核识别的文件
- systemd 提供了设备的管理功能,
/dev
下的设备由 /etc/udev/
下的配置文件与 .device
共同定制
.automount
定义文件系统的自动挂载点
.timer
Timer units 定时器
man systemd.timer
systemctl list-unit-files --type timer
.path
用于定义文件系统中的一个文件或目录使用
.slice
Slice units 定义资源的分配组,进程组,是对一组进程的资源进行分级管理的概念
systemctl list-unit-files --type slice
slice
是一组按层级排列的单位
slice
并不包含进程,但会组建一个层级,并将 scope
和 service
都放置其中
- 真正的进程包含在
scope
或 service
中
- 在这一被划分层级的树中,每一个
slice
单位的名字对应通向层级中一个位置的路径
- 小横线(
-
)起分离路径组件的作用
.scope
Scope Unit(作用域单元)不是通过单元配置文件配置的,而是仅使用 systemd 的总线接口以编程方式创建
- 它们的命名与文件名类似,名称以
.scope
结尾的单元指的是 Scopes Units
- Scopes units 管理一组系统进程。与 Service Units 不同,Scopes units 管理外部创建的进程,不会自行分叉进程
- 所有的单元文件:
systemctl list-unit-files [-t xxx]
systemctl list-units --all --state=inactive
unit 文件路径
/etc/systemd/system
存放系统启动的及启动的 unit 的软连接,优先级最高
/lib/systemd/system
默认 units 文件路径,优先级次之
/usr/lib/systemd/system
用户自己定义的单元文件,优先级最低
unit 文件定义
systemctl cat <UnitName>
查看 unit 文件,它通常由 3 段组成:
[Unit]
...
[<Unit 类型,如 Service Target Socket>]
...
[Install]
...
说明:
[Unit]
字段如下
Description
描述信息
Documentation
文档或帮助文档,如 Documentation=man:sshd(8) man:sshd_config(5)
After
表明需要依赖的服务,作用决定启动顺序,多个使用空格隔开 After=network.target auditd.service
Before
表明被依赖的服务
Requles
依赖到的其他unit,强依赖,即依赖的unit启动失败,该unit不启动
Wants
依赖到的其他unit,弱依赖,即依赖的unit启动失败,该unit继续启动
Conflicts
定义冲突关系
Condition...
运行必须满足的条件,否则不会运行
ConditionPathExists
条件 ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
Assert...
运行必须满足的条件,否则会报启动失败
[<Unit 类型>]
参考
[Service]
以 systemctl cat sshd.service
为例
Type
启动时关系的定义
simple
ExecStart 启动的进程就是主进程
forking
ExecStart 启动的进程生成的其中一个子进程成为主进程,启动完成后,旧的主进程会退出
ontshot
一次性进程,Systemd 会等当前服务退出,再继续往下执行
dbus
当前服务通过D-Bus启动
notify
当前服务启动完毕,会通知Systemd,再继续往下执行
idle
若有其他任务执行完毕,当前服务才会运行
PIDFile=/var/run/sshd.pid
EnvironmentFile=-/etc/default/ssh
环境变量的定义文件
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
启动 unit 需要执行的命令
ExecStartPre=/usr/sbin/sshd -t
启动 unit 服务之前执行的命令
ExecStartpost
启动 unit 服务之后执行的命令
ExecStop
停止 unit 需要执行的命令
ExecStopPost
停止当其服务之后执行的命令
Restart
定义什么情况 Systemd 会自动重启当前服务,值可以为:
no(default)
退出后不会重启
always
总是重启
on-success
只有正常退出时(退出状态码为0),才会重启
on-failure
非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启
on-abnormal
只有被信号终止和超时,才会重启
on-abort
只有在收到没有捕捉到的信号终止时,才会重启
on-watchdog
ExecReload=/usr/sbin/sshd -t
重启当前服务时执行的命令
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
只停止主进程,不停止 sshd
的子进程,即子进程打开的 SSH session 仍然保持连接,参考
process
只杀主进程
control-group(default)
杀掉当前控制组里面的所有子进程,对于 .service
单元,先执行 ExecStop=
动作
mixed
向主进程发送 SIGTERM
信号量,在向该.service
单元 cgroup 内的所有子进程发送 SIGKILL
信号量
none
不杀掉任何进程,只是执行服务的 ExecStop=
动作,可能残留 cgroup 中的其他进程
KillSignal
设置杀死进程的信号量
RestartPreventExitStatus=255
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
RestartSec=30s
[Install]
服务启动或禁用时的一些选项
Alias=sshd.service
当前 Unit 可用于启动的别名
Also
当前 Unit 激活(enable)时,会被同时激活的其他 Unit
WantedBy=multi-user.target
值是一个或多个 Target
,当前 Unit 激活时(enable)符号链接会放入 /etc/systemd/system
目录下面以 <targetName>.wants
的子目录中
RequlredBy
同上,也是 Target
- 最后,修改 unit 文件后需要执行
systemctl daemon-reload
重载生效
Target
Target
就是一个 Unit
组,包含许多相关的 Unit
。解决一次批量启动多个 Unit 服务
# 查看默认启动的 Target
systemctl get-default
# 查看某个 Target 包含的所有 Unit
systemctl list-dependencies multi-user.target
# 查看当前系统的所有 Target
systemctl list-unit-files --type=target
# 设置启动时的默认 Target
sudo systemctl set-default multi-user.target
- Target 与 传统 RunLevel 的对应关系如下。
Traditional runlevel |
New target name -> Symbolically linked to… |
Runlevel 0 |
runlevel0.target -> poweroff.target |
Runlevel 1 |
runlevel1.target -> rescue.target |
Runlevel 2 |
runlevel2.target -> multi-user.target |
Runlevel 3 |
runlevel3.target -> multi-user.target |
Runlevel 4 |
runlevel4.target -> multi-user.target |
Runlevel 5 |
runlevel5.target -> graphical.target |
Runlevel 6 |
runlevel6.target -> reboot.target |
slice
slice
:A group of hierarchically organized units that manage system process. 一组分级管理系统进程的单位
- 启动启动时默认的 四 种 slice,采用 systemd-cgls 查看层级关系:
-.slice
the root slice
system.slice
the default place for all system services
user.slice
the default place for all user sessions
machine.slice(maybe)
the default place for all virtual machines and Linux containers.
systemctl
管理命令
# 显示所有 Unit
systemctl --all
# 启动
systemctl start <UnitName>
systemctl -H root@<ip> status <UnitName>
# 条件式重启,服务之前是启动的则进行重启,如果服务没有启动则不进行操作
systemctl try-restart <UnitName>
# 关闭
systemctl stop <UnitName>
# 重启
systemctl restart <UnitName>
# 重载或重启:首先进行重载,如果重载不成功则进行重启
systemctl reload-or-restart <UnitName>
# 重载或条件式重启:
systemctl reload-or-try-restart <UnitName>
# 查看状态
systemctl status <UnitName>
# 开机启动
# 本质是建立软连接 sudo ln -s '/usr/lib/systemd/system/xxx.service' '/etc/systemd/system/multi-user.target.wants/xxx.service'
systemctl enable <UnitName>
# 禁止开机启动
# 本质是删除上述软连接
systemctl disable <UnitName>
# 设置服务是否可以被用户设置开机启动状态
# 取消禁止
systemctl unmask <UnitName>
# 禁止
systemctl mask <UnitName>
# 查看服务的当前激活状态
systemctl is-active <UnitName>
# 查看所有已经激活的服务
# -t 指定显示的unit类型
# -a, --all 显示更加详细的信息列表
systemctl list-units
systemctl list-units -a
systemctl list-units -t service
systemctl list-units -t service -a
systemctl list-units -t slice
$ systemctl list-
list-automounts list-jobs list-paths list-timers list-units
list-dependencies list-machines list-sockets list-unit-files
# 查看服务是否开机自启
systemctl is-enabled <UnitName>
# 查看服务的依赖关系
systemctl list-dependencies <UnitName>
# 重载所有修改过的配置文件
systemctl daemon-reload
# 杀死进程
systemctl kill <UnitName>
# 显示某个 Unit 的所有底层参数
systemctl show <UnitName>
# 查看 docker 的 OOM 策略
systemctl show docker | grep -i oom OOMPolicy=continue
# 查看 unit 配置
systemctl cat <UnitName>
# 重启
systemctl reboot
#
systemctl halt
systemctl poweroff
# -a, --all 查看所有服务的状态
# -t, --type 指定查看的 unit 类型
systemctl list-unit-files
status 列说明:
enabled
开机启动
disabled
开机不启动
static
配置文件没有 [Install]
部分(无法执行),只能作为其他配置文件的依赖
generated
enabled-runtime
masked
被禁止建立启动链接
set-property
- 查看更多细节
man systemd.resource-control
在支持的情况下,在运行时设置指定的单元属性。这允许在运行时更改配置参数属性,如资源控制设置。并非所有属性都可以在运行时更改,但许多资源控制设置(主要是 systemd.resource-control(5)
中的设置)可以更改。这些更改会立即应用,并存储在磁盘上,供未来启动时使用,除非通过了 --runtime
设置,在这种情况下,设置只会应用到下一次重启。属性赋值的语法与单元文件中的赋值语法非常相似。
示例:systemctl set-property foobar.service CPUWeight=200
如果指定的单元似乎处于非活动状态,则更改只会存储在磁盘上,如前所述,因此它们将在单元启动时生效。
请注意,该命令允许同时更改多个属性,这比单独设置要好。
示例:systemctl set-property foobar.service CPUWeight=200 MemoryMax=2G IPAccounting=yes
与单元文件配置设置一样,分配空设置通常会将属性重置为默认值。
示例:systemctl set-property avahi-daemon.service IPAddressDeny=
在 206 版中添加。
# 显示某个 Unit 的指定属性的值
systemctl show -p CPUShares nginx.service
# 设置某个 Unit 的指定属性,限定 nginx.service 的 CPU 和内存占用量
sudo systemctl set-property nginx.service CPUShares=500 MemoryLimit=500M
# 临时更改,添加 --runtime 选项
systemctl set-property --runtime httpd.service CPUShares=600 MemoryLimit=500M
# CPUQuota
systemd 资源控制概念
Systemd 提供了三种有助于控制资源的单元类型:
service 服务
封装了 systemd 根据配置启动和停止的大量进程。服务的命名方式与 quux.service 相同。
scope 作用域
封装了大量进程,这些进程由任意进程通过 fork() 启动和停止,然后在运行时以 PID1 注册。作用域的命名方式与 wuff.scope 相同。
slice 片段
可用于将多个服务和作用域以层次树的形式组合在一起。片段本身不包含进程,但其中包含的服务和片段却包含进程。切片的命名方式是 foobar-waldo.slice,切片在树中的位置路径在名称中以"-“作为路径分隔符进行编码(因此,foobar-waldo.slice 是 foobar.slice 的子切片)。还有一个特殊的片段定义,即-.slice,它是所有片段的根片段(foobar.slice 因此是-.slice 的子片段)。这与普通文件路径中的”/“表示根目录类似。
服务、作用域和切片单元直接映射到 cgroups 树中的对象。当这些单元被激活时,它们都会直接映射到根据单元名称建立的 cgroups 路径上(在某些字符转义的情况下)。例如,foobar-waldo.slice 片中的服务 quux.service 可以在 cgroups foobar.slice/foobar-waldo.slice/quux.service/ 中找到。
服务、作用域和片段可由管理员自由创建,也可由程序员动态创建。使用 systemd-cgls 查看层级
systemd-analyze
# 查看启动耗时
$ systemd-analyze
# 查看每个服务的启动耗时
$ systemd-analyze blame
# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain
# 显示指定服务的启动流
$ systemd-analyze critical-chain atd.service
loginctl
# 查看当前登录的用户
$ loginctl list-sessions
$ loginctl list-users
$ loginctl show-user root