共计 10579 个字符,预计需要花费 27 分钟才能阅读完成。
背景
- 不太适宜进入
docker
,须要间接部署在宿主机下面,领有root
权限,操作批改零碎,读取零碎的一些信息等 - 保障开机启动以及异样重启
- 有一套欠缺的日志零碎
- 反对
Before/After
的启动机制 - 反对基于
cgroup
的限度 - 以最低的零碎开销实现该目标
- 不便打包为
deb
包进行装置,参考各种服务的装置,比方apt install nginx docker postgresql
等deb
包背地的逻辑与实现 - 兼容根本全副的
linux
发行版本
尽管之前应用过的 supervisor
也能够满足上述大部分需要,然而打工人的世界总是须要去谋求一下极致的,于是决定好好学习一下systemd
Systemd
根本介绍
- 内核加载到内存之后启动的第一个用户空间程序,
pid
是 1,是所有过程的父过程,会托管所有僵尸过程,如果这个程序挂了,零碎也就间接关机了 systemd
不是内核的一部分,然而却成为了Debian/Ubuntu
系和Fedora/Centos/Redhat/RockyLinux
系的零碎初始化过程
Linux
启动流程
systemd
是系统启动流程中初始化阶段的次要角色,有必要介绍一下 linux
系统启动流程
硬件疏导启动阶段(第一阶段)
机器电源接通时,存储在主板芯片下面的固件初始化,POST(Power On Self Test)
上电自检-
BIOS
阶段- 初始化硬件,内存,磁盘,显卡等
- 查找启动介质(个别是硬盘),查找
MBR
或者EFI
分区的第一阶段疏导程序,并且移交控制权
MBR
或者EFI
程序阶段,查找第二阶段的GRUB(GRand Unified Boot)
疏导程序并装置到BootLoder
BootLoader
启动疏导阶段(第二阶段)
stage1
: 执行BootLoader
的主程序 (GRUB
程序),开始启动stage1.5
stage1.5
: 疏导文件系统中的stage2
stage2
: 加载GRUB
外围映像-
grub.conf/grub.cfg
阶段- 解析
grub.conf/grub.cfg
配置文件,加载默认内核镜像和initrd
(初始磁盘镜像,对应于/boot/initrd.img
文件)镜像到内存中,当所有镜像筹备好后,即跳转到内核镜像,移交零碎控制权给内核,接下去进入到内核阶段 - 文件后缀名称在
Ubuntu22
下面看到的是.cfg
,可能其余零碎是.conf
结尾 - 该配置文件门路是
/grub/grub.cfg
或者/boot/grub/grub.cfg
,门路具体位置取决于零碎装置时候是否宰割了一个/boot
分区,之前装零碎的时候/boot
分区都是必须调配的
- 解析
内核疏导阶段(第三阶段)
-
/boot/kernel and Kernel parameter
- 内核读取
/boot
上面的文件,执行加载 -
查看
/boot
分区的内容如下,能够看到内核保留了两个版本的镜像(img-5.15.0-53-generic
和img-5.15.0-56-generic
),其中新镜像是目前零碎失常运行时候加载的镜像,每次apt
更新零碎的时候如果有新版本内核开释,更新时候会保留一份老版本镜像,等下一次新镜像更新到时候才会删除,一份冗余$ ll /boot 总用量 165M -rw-r--r-- 1 root root 256K 十月 18 02:36 config-5.15.0-53-generic -rw-r--r-- 1 root root 256K 十一月 22 23:08 config-5.15.0-56-generic drwx------ 3 root root 4.0K 一月 1 1970 efi drwxr-xr-x 5 root root 4.0K 十二月 3 09:44 grub lrwxrwxrwx 1 root root 28 十二月 2 09:45 initrd.img -> initrd.img-5.15.0-56-generic -rw-r--r-- 1 root root 65M 十二月 1 09:01 initrd.img-5.15.0-53-generic -rw-r--r-- 1 root root 65M 十二月 4 09:04 initrd.img-5.15.0-56-generic lrwxrwxrwx 1 root root 28 十二月 2 09:45 initrd.img.old -> initrd.img-5.15.0-53-generic drwx------ 2 root root 16K 五月 26 2021 lost+found -rw-r--r-- 1 root root 179K 二月 7 2022 memtest86+.bin -rw-r--r-- 1 root root 181K 二月 7 2022 memtest86+.elf -rw-r--r-- 1 root root 181K 二月 7 2022 memtest86+_multiboot.bin -rw------- 1 root root 6.0M 十月 18 02:36 System.map-5.15.0-53-generic -rw------- 1 root root 6.0M 十一月 22 23:08 System.map-5.15.0-56-generic lrwxrwxrwx 1 root root 25 十二月 2 09:45 vmlinuz -> vmlinuz-5.15.0-56-generic -rw------- 1 root root 12M 十月 18 02:41 vmlinuz-5.15.0-53-generic -rw------- 1 root root 12M 十一月 23 01:07 vmlinuz-5.15.0-56-generic lrwxrwxrwx 1 root root 25 十二月 2 09:45 vmlinuz.old -> vmlinuz-5.15.0-53-generic
- 内核读取
-
/boot/initrd
- 疏导
initrd
解压载入 - 在内存中加载内核应用的
root
文件系统,执行initrd
文件系统中的init
程序,实现加载其余驱动模块 -
执行
/sbin/init
过程发现
/sbin
是指向/usr/sbin
的一个软链接$ ll / |grep bin lrwxrwxrwx 1 root root 7 五月 26 2021 bin -> usr/bin lrwxrwxrwx 1 root root 8 五月 26 2021 sbin -> usr/sbin
查看
init
发现init
是指向systemd
的一个软链接$ ll /usr/sbin |grep init lrwxrwxrwx 1 root root 20 九月 10 02:47 init -> /lib/systemd/systemd -rwxr-xr-x 1 root root 13K 三月 15 2022 mkinitramfs lrwxrwxrwx 1 root root 14 九月 10 02:47 telinit -> /bin/systemctl -rwxr-xr-x 1 root root 6.8K 二月 9 2022 update-initramfs
- 疏导
Systemd
初始化阶段(第四阶段)
- 内核启动第一个用户空间应用程序,零碎控制权移交给
systemd
- 该阶段会加载执行级别,加载服务,启动
shell
和图形化界面 - 初始化阶段有 3 个类型,
SysV
初始化,UpStart
(Ubuntu
开发的用于替换SysV
的初始化程序)和Systemd
(由Redhat
工程师开发),很多老零碎个别都是SysV
作为初始化程序,从centos7
(大略是 2014 年) 当前的零碎大多都是采纳Systemd
,Ubuntu15
开始应用systemd
为什么各大支流 linux
零碎采纳systemd
- 改良启动项的效率,防止频繁的过程创立,内核 / 用户切换,为零碎的启动和治理提供一套残缺的解决方案
- 利用
Dbus
过程间通信与socket
激活机制,解决工作启动时的依赖问题,实现启动并行化 - 实现工作
daemons
的准确管制,应用内核的cgroup
机制,不依赖pid
来追踪过程,过程屡次fork
之后生成的过程也不会脱离systemd
的管制 - 对立工作定义,用户不须要编写
shell
脚本,而是应用systemd
制订的unit
规定 systemd
有两大设计理念,启动更少的服务,更多地并行启动服务,最终实现用户能够疾速进入零碎systemd
应用C
编写,用于替换传统的脚本shell
启动,shell
中调用系统命令的操作会导致新过程的生成,比方awk, sed, grep
等都会生成新过程,产生了很大的开销,systemd
的运行开销比SysV
和UpStart
小很多,速度也更快
systemd
做了什么
解决启动项的依赖性
Socket
依赖:例如服务A
依赖服务B
的socket
,然而服务B
还未启动,所以systemd
创立了一个长期socket
用于接管服务A
的申请与数据,当B
真正起来的时候再把长期socket
缓存的数据以及描述符替换为B
的socket
D-Bus(Desktop Bus)
依赖:是一个用来在过程间通信的服务,该服务反对Bus Activation
个性,即服务A
要通过D-Bus
服务和B
通信,然而B
没有启动,那么D-Bus
能够把B
起来,在B
启动的过程中,D-Bus
缓存数据,systemd
应用这个个性并行启动A
和B
- 文件系统依赖: 系统启动过程中,
systemd
参考了autofs
的设计思路,使得依赖文件系统的服务和文件系统自身初始化两者能够并发工作,监测到某个文件系统挂载点真正被拜访到的时候才触发挂载操作
提供了一个零碎和服务管理器
- 利用
Linux
的cgroups
监督过程 - 反对快照和零碎复原
- 保护挂载点和主动挂载点
- 各服务间基于依赖关系进行精细管制
- 基于
journald
的服务日志治理 - 管制根底系统配置
- 保护登陆用户列表以及零碎账户
- 运行时目录和设置
- 运行容器和虚拟机
- 简略的管理网络配置
- 网络工夫同步
- 日志转发
- …
systemd
的管控范畴曾经远超作为零碎启动项的角色,能够了解为曾经大一统治理 linux
各项性能了,职责和最后的 sysv
和upstart
有所脱离,变得极其简单宏大,这个也是最后 redhat/centos
系列和 debian/ubuntu
系列采纳 systemd
引起的微小波澜
在此举荐浏览一下 itwire
于 2014 发表的 Linus Torvalds
对于 systemd
的访谈文章(Linus
保持中立意见,如果这个人没有开喷的话,阐明是曾经获得微小的胜利了)
https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd
也能够浏览一下 systemd
的作者 Lennert
发表的对于 PID 1
的再思考Rethinking PID 1
http://0pointer.de/blog/projects/systemd.html
systemd
的 Unit
单元的文件类型
零碎初始化须要很多操作,比方启动后盾服务,挂载文件系统,配置网络等,过程中的每一步都被 Systemd
形象为一个配置单元,即 Unit
,具体蕴含的类型如下
type | name | 作用 |
---|---|---|
Service unit |
.service |
封装一个后盾服务过程,比方sshd ,dockerd |
Target unit |
.target |
将多个单元在逻辑上组合在一起。 |
Device unit |
.device |
定义内核辨认的设施,在 sysfs(5) 外面作为 udev(7) 设施树展现 |
Socket unit |
.socket |
用于标识过程间通信用到的 socket 文件 |
Snapshot unit |
.snapshot |
管理系统快照 |
Swap unit |
.swap |
用于标识 swap 文件或设施 |
Mount unit |
.mount |
封装一个文件系统挂载点 (也向后兼容传统的 /etc/fstab 文件) |
Automount unit |
.automount |
封装一个文件系统主动挂载点 |
Path unit |
.path |
依据文件系统上特定对象的变动来启动其余服务。 |
Time unit |
.timer |
封装一个基于工夫触发的动作。取代传统的 crond 等工作打算服务 |
Slice unit |
*.slice |
管制特定 CGroup 内所有过程的总体资源占用。 |
Systemd Unit
文件的地位与执行优先级
不同发行版本的门路是不同的,当三个目录上面存在同名文件的时候,优先级最高目录下的文件会被优先应用
/usr/lib/systemd/system
或/lib/systemd
: 应用包管理器装置的软件的systemd unit
的理论配置文件的寄存地位,在Ubuntu22
下面/lib
门路是指向/usr/lib
的一个软链接,优先级最低/run/systemd/system
:在运行时创立的systemd unit
文件,该目录优先于已装置服务单元文件的目录/etc/systemd/system
: 优先级最高,由systemctl
命令创立的systemd unit
文件以及为扩大服务而增加的unit
文件都将启用,系统管理员装置的单元
Systemd Unit
文件的组成
次要由三块 Unit
,Service
,Install
组成
具体版本或者全副字段解释执行如下命令查看
$ man systemd.unit
$ man systemd.service
$ man systemd.exec
....
或者拜访地址https://www.freedesktop.org/software/systemd/man/
或者第三方中文版http://www.jinbuguo.com/systemd/systemd.index.html
常见字段解释如下
Unit
块上面的字段
Requires
: 强依赖,依赖的其它Unit
列表,列在其中的Unit
会在这个Unit
启动时的同时被启动,如果其中任意一个Unit
启动失败,这个Unit
也会启动失败Wants
: 弱依赖,与Requires
类似,但只是在以后Unit
启动时,触发启动列出的Unit
,而不思考这些Unit
启动是否胜利After
:与Requires
类似,该字段列出的所有Unit
全副启动后,才会启动以后的Unit
Before
:与After
相同,以后Unit
应该在列出的Unit
之前启动OnFailure
:Unit
启动失败时,主动启动列出的每个Unit
Conflicts
: 与这个Unit
有抵触的模块,如果列出的Unit
曾经在运行时,以后Unit
不能启动,反之亦然
Service
块上面的字段
EnvironmentFile
: 指定以后服务的环境变量定义文件,该文件外部的key=value
键值对,能够用$key
的模式,在以后.service
文件中援用ExecStart
: 定义启动过程时执行的命令-
ExecStartPre
: 启动以后服务之前执行的命令ExecStartPost:
启动以后服务之后执行的命令
ExecStop
: 进行服务时执行的命令-
ExecStopPost
: 进行服务之后执行的命令ExecReload
: 重启服务时执行的命令
-
Type
: 启动类型simple
: 默认值,执行ExecStart
指定的命令,启动主过程forking
: 以fork
形式从父过程创立子过程,创立后父过程会立刻退出oneshot
: 一次性过程,Systemd
会等以后服务退出,再持续往下执行dbus
: 以后服务通过D-Bus
启动notify
: 以后服务启动结束,会告诉Systemd
,再持续往下执行-
idle
: 若有其余工作执行结束,以后服务才会运行WorkingDirectory
: 指定服务的工作目录RootDirectory
: 指定服务过程的根目录,如果配置了这个参数,服务将无法访问指定目录以外的文件
-
User
: 指定运行服务的用户Group
: 指定运行服务的用户组KillMode
: 定义Systemd
如何进行服务,可选项如下control-group
: 默认值, 以后控制组外面的所有子过程,都会被杀掉process
: 只杀主过程mixed
: 主过程将收到SIGTERM
信号,子过程收到SIGKILL
信号none
: 没有过程会被杀掉,只是执行服务的stop
命令
install 块上面的字段
WantedBy
值为一个或多个Target
,以后Unit
激活 (enable
,即开机启动) 时符号链接会放入/usr/lib/systemd/system
目录下以<Target>.wants
后缀形成的子目录中,该参数常见的值是multi-user.target
,这样,当该target
中的任意一个Unit
启动的时候,本Unit
都会跟着一起启动,这就是配置开机自启动的要害Also
以后Unit enable/disable
时,同时enable/disable
的其余Unit
Alias
以后Unit
可用于启动的别名
systemd Unit
的配置与应用
最好的样例还是参考操作系统曾经有的内容
比方当初要编写一个自启动服务,能够参考 docker.service
和sshd.service
$ systemctl cat docker
$ systemctl cat sshd
如果要编写一个定时工作,能够参考apt-dayly.timer
$ systemctl cat apt-daily.timer
systemctl
应用
管理系统和服务的命令行工具
设施启动,关机等操作,能够看到设施运行操作命令是指向 systemctl
的软链接
$ ll /usr/sbin |grep system
lrwxrwxrwx 1 root root 14 九月 10 02:47 halt -> /bin/systemctl
lrwxrwxrwx 1 root root 20 九月 10 02:47 init -> /lib/systemd/systemd
lrwxrwxrwx 1 root root 14 九月 10 02:47 poweroff -> /bin/systemctl
lrwxrwxrwx 1 root root 14 九月 10 02:47 reboot -> /bin/systemctl
lrwxrwxrwx 1 root root 14 九月 10 02:47 runlevel -> /bin/systemctl
lrwxrwxrwx 1 root root 14 九月 10 02:47 shutdown -> /bin/systemctl
lrwxrwxrwx 1 root root 14 九月 10 02:47 telinit -> /bin/systemctl
systemd
罕用操作
动作 | 命令 | 正文 |
---|---|---|
剖析零碎状态 | ||
显示零碎状态 | systemctl status |
|
列出正在运行的 单元 | systemctl or systemctl list-units |
|
列出失败的 单元 | systemctl --failed |
|
列出已装置的 单元 | systemctl list-unit-files |
|
Show process status for a PID | systemctl status {pid} |
cgroup slice, memory and parent |
查看单元状态 | ||
显示单元的 手册页 | systemctl help {unit} |
如果单元反对 |
显示单元的 状态 | systemctl status {unit} |
包含其是否在运行 |
查看 单元是否配置为主动启动 | systemctl is-enabled {unit} |
|
启动、重新启动和从新加载单元 | ||
立刻 启动 单元 | systemctl start {unit} |
|
立刻 进行 单元 | systemctl stop {unit} |
|
重新启动 单元 | systemctl restart {unit} |
|
从新加载 单元及其配置 | systemctl reload {unit} |
|
从新加载 systemd 配置 | systemctl daemon-reload |
扫描单元的变动 |
启用单元 | ||
开机主动 启用 单元 | systemctl enable {unit} |
|
开机主动 启用 单元,并立刻 启动 它 | systemctl enable --now {unit} |
|
勾销 开机主动启用单元 | systemctl disable {unit} |
|
从新启用 单元 | systemctl reenable {unit} |
先勾销启用,再启用 |
禁用单元 | ||
禁用 单元,使其无奈启动 | systemctl mask {unit} |
|
勾销禁用 单元 | systemctl unmask {unit} |
systemd
其余常用命令模块
journalctl
-
查问
systemd
服务的日志信息$ journalctl
loginctl
-
systemd
的登入管理器,能够查看目前登录到以后计算机的用户信息$ sudo loginctl SESSION UID USER SEAT TTY 2 1000 gong seat0 tty2 640 1000 gong pts/17 641 1000 gong pts/19
如果应用
ssh {用户名}@localhost
再次登录到本地计算机,应用loginctl
的时候会看到多了一个session
这个时候能够应用如下命令,指定一个
session id
的形式敞开某个用户的拜访$ sudo loginctl kill-session 641
systemd-analyze
-
剖析系统启动过程中的耗时,敞开或者调整某些服务能够优化启动速度
$ sudo systemd-analyze blame 1min 55.462s fstrim.service 39.372s fwupd-refresh.service 33.827s apt-daily.service 31.949s apt-daily-upgrade.service 16.637s systemd-networkd-wait-online.service 6.193s plymouth-quit-wait.service 5.070s docker.service 1.551s snapd.service 1.540s netfilter-persistent.service 1.318s snapd.seeded.service 1.196s vmware.service 1.129s freeradius.service 1.049s gpu-manager.service .....
systemd service
应用示例
编写一个文件 /usr/lib/systemd/system/test-boot.service
如下
[Unit]
Description=test boot
# 示意在网络模块启动之后才启动本 Unit
After=network.target
[Service]
# 该命令是阻塞的,每隔 1 秒会打印以后日期
ExecStart=/bin/sh -c "while true; do date; sleep 1; done"
# 配置查问的工作门路,该门路须要存在,否则会报错
WorkingDirectory=/tmp/
[Install]
# 该参数示意此 Unit 是开机启动时候关联到 multi-user.target
# 当 multi-user.target 上面的任意一个 Unit 启动都会触发本 Unit 的启动
# 即 enable 状态的时候会创立一个链接到 /etc/systemd/system/multi-user.target.wants/ 目录上面
WantedBy=multi-user.target
执行命令如下
$ sudo systemctl daemon-reload
查看以后服务状态
$ sudo systemctl status test-boot
○ test-boot.service - test boot
Loaded: loaded (/lib/systemd/system/test-boot.service; disabled; vendor preset: enabled)
Active: inactive (dead)
配置开机启动,留神察看软链接的地位是配置在
$ sudo systemctl enable test-boot
Created symlink /etc/systemd/system/multi-user.target.wants/test-boot.service → /lib/systemd/system/test-boot.service.
接下去启动服务并查看状态
$ sudo systemctl start test-boot
$ sudo systemctl status test-boot
● test-boot.service - test boot
Loaded: loaded (/lib/systemd/system/test-boot.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2023-03-16 17:32:13 CST; 20s ago
Main PID: 940646 (sh)
Tasks: 2 (limit: 38197)
Memory: 364.0K
CGroup: /system.slice/test-boot.service
├─940646 /bin/sh -c "while true; do date; sleep 1; done"
└─940825 sleep 1
三月 16 17:32:24 gong sh[940765]: 2023 年 03 月 16 日 星期四 17:32:24 CST
三月 16 17:32:25 gong sh[940769]: 2023 年 03 月 16 日 星期四 17:32:25 CST
三月 16 17:32:26 gong sh[940778]: 2023 年 03 月 16 日 星期四 17:32:26 CST
三月 16 17:32:27 gong sh[940782]: 2023 年 03 月 16 日 星期四 17:32:27 CST
三月 16 17:32:28 gong sh[940786]: 2023 年 03 月 16 日 星期四 17:32:28 CST
三月 16 17:32:29 gong sh[940795]: 2023 年 03 月 16 日 星期四 17:32:29 CST
三月 16 17:32:30 gong sh[940799]: 2023 年 03 月 16 日 星期四 17:32:30 CST
三月 16 17:32:31 gong sh[940803]: 2023 年 03 月 16 日 星期四 17:32:31 CST
三月 16 17:32:32 gong sh[940814]: 2023 年 03 月 16 日 星期四 17:32:32 CST
最初能够重启机器校验一下是否能够开机自启动
参考浏览
Linux
系统启动流程
Linux
的小伙伴 systemd
详解
linux
的 init
程序倒退历史
Linux PID 1
和 Systemd
Docker
根底技术:Linux CGroup
archlinux
的 systemd wiki
文档
itwire
于 2014 发表的 Linus Torvalds
对于 systemd
的访谈
systemd
的作者 Lennert
发表的对于 PID 1
的再思考
systemd
的 unit
配置