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)时,会被同时激活的其他 UnitWantedBy=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 slicesystem.slice
the default place for all system servicesuser.slice
the default place for all user sessionsmachine.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
# Systemd 支持通过 --user 参数管理用户级别的后台程序,但当用户退出登录后,属于该用户的后台服务会被终止。如果希望用户退出后仍然保持服务的运行,可以使用下面的命令启用用户的逗留状态
$ loginctl enable-linger $USER