乐趣区

关于systemd:Systemd详解与使用

背景

  • 不太适宜进入 docker,须要间接部署在宿主机下面,领有root 权限,操作批改零碎,读取零碎的一些信息等
  • 保障开机启动以及异样重启
  • 有一套欠缺的日志零碎
  • 反对 Before/After 的启动机制
  • 反对基于 cgroup 的限度
  • 以最低的零碎开销实现该目标
  • 不便打包为 deb 包进行装置,参考各种服务的装置,比方 apt install nginx docker postgresqldeb包背地的逻辑与实现
  • 兼容根本全副的 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-genericimg-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 年) 当前的零碎大多都是采纳 SystemdUbuntu15 开始应用systemd

为什么各大支流 linux 零碎采纳systemd

  • 改良启动项的效率,防止频繁的过程创立,内核 / 用户切换,为零碎的启动和治理提供一套残缺的解决方案
  • 利用 Dbus 过程间通信与 socket 激活机制,解决工作启动时的依赖问题,实现启动并行化
  • 实现工作 daemons 的准确管制,应用内核的 cgroup 机制,不依赖 pid 来追踪过程,过程屡次fork 之后生成的过程也不会脱离 systemd 的管制
  • 对立工作定义,用户不须要编写 shell 脚本,而是应用 systemd 制订的 unit 规定
  • systemd有两大设计理念,启动更少的服务,更多地并行启动服务,最终实现用户能够疾速进入零碎
  • systemd应用 C 编写,用于替换传统的脚本 shell 启动,shell中调用系统命令的操作会导致新过程的生成,比方 awk, sed, grep 等都会生成新过程,产生了很大的开销,systemd的运行开销比 SysVUpStart小很多,速度也更快

systemd做了什么

解决启动项的依赖性

  • Socket依赖:例如服务 A 依赖服务 Bsocket,然而服务 B 还未启动,所以 systemd 创立了一个长期 socket 用于接管服务 A 的申请与数据,当 B 真正起来的时候再把长期 socket 缓存的数据以及描述符替换为 Bsocket
  • D-Bus(Desktop Bus)依赖:是一个用来在过程间通信的服务,该服务反对 Bus Activation 个性,即服务 A 要通过 D-Bus 服务和 B 通信,然而 B 没有启动,那么 D-Bus 能够把 B 起来,在 B 启动的过程中,D-Bus 缓存数据,systemd应用这个个性并行启动 AB
  • 文件系统依赖: 系统启动过程中,systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统自身初始化两者能够并发工作,监测到某个文件系统挂载点真正被拜访到的时候才触发挂载操作

提供了一个零碎和服务管理器

  • 利用 Linuxcgroups监督过程
  • 反对快照和零碎复原
  • 保护挂载点和主动挂载点
  • 各服务间基于依赖关系进行精细管制
  • 基于 journald 的服务日志治理
  • 管制根底系统配置
  • 保护登陆用户列表以及零碎账户
  • 运行时目录和设置
  • 运行容器和虚拟机
  • 简略的管理网络配置
  • 网络工夫同步
  • 日志转发

systemd的管控范畴曾经远超作为零碎启动项的角色,能够了解为曾经大一统治理 linux 各项性能了,职责和最后的 sysvupstart有所脱离,变得极其简单宏大,这个也是最后 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

systemdUnit 单元的文件类型

零碎初始化须要很多操作,比方启动后盾服务,挂载文件系统,配置网络等,过程中的每一步都被 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文件的组成

次要由三块 UnitServiceInstall 组成

具体版本或者全副字段解释执行如下命令查看

$ 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 之前启动
  • OnFailureUnit 启动失败时,主动启动列出的每个 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.servicesshd.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 详解

linuxinit 程序倒退历史

Linux PID 1Systemd

Docker根底技术:Linux CGroup

archlinuxsystemd wiki 文档

itwire于 2014 发表的 Linus Torvalds 对于 systemd 的访谈

systemd的作者 Lennert 发表的对于 PID 1 的再思考

systemdunit 配置

退出移动版