Systemd 从入门到实战

  • 作者:约克
  • 原文地址:
  • 文章版权归作者所有,转载请注明出处!

blog/20201121161159_d4edd487437ce0a1d3aa6e92f01ef349.png

前言

本文主要包含内容如下:

  • Systemd 是什么?
  • Systemd 常用命令。
  • Systemd 系统资源 Unit (单位)。
  • Systemd 系统资源管理 - 动手配置,实现开机自启动 Node.js 服务。

一,Systemd 概述

1.1. 基本介绍

Systemd (System Daemon),根据 Linux 命名惯例,字母 d 是守护进程 daemon 的缩写。如 Systemd 名字的含义一样,它作为 PID 1 进程,守护整个系统。Systemd 是一系列工具的集合,其作用也远远不仅是启动操作系统,它还接管了后台服务、结束、状态查询,以及日志归档、设备管理、电源管理、定时任务等许多职责。

systemdLennart Poettering 带头开发,并在 LGPL 2.1 及其后续版本许可证下开源发布。systemd 已纳入众多 Linux 发行版的软件源中。

1.2. 特点介绍

缺点是体系庞大,非常复杂。Systemd 的优点是功能强大,使用方便。其优点介绍如下:

  • 更少的进程
    Systemd 提供了服务按需启动的能力。

  • 允许更多的进程并行启动
    SysV-init 时代,将每个服务项目编号依次执行启动脚本。而 Systemd 通过 Socket 缓存、DBus 缓存和建立临时挂载点等方法进一步解决了启动进程之间的依赖,做到了所有系统服务并发启动。

  • 使用 CGroup 跟踪和管理进程的生命周期
    Systemd 提供通过 CGroup 跟踪进程关系。CGroup 不仅能够实现服务之间访问隔离,限制特定应用程序对系统资源的访问配额,还能更精确地管理服务的生命周期。

  • 统一管理服务日志
    Systemd 提供了一个专用的系统日志管理服务:Journald。其使用二进制格式保存所有的日志信息,因而日志内容很难被手工伪造。

1.3. 架构图

下图 是 Systemd 的架构图(来自网络)
blog/20201121233602_a705b252a0dbb255d9c589dfd4da545d.png

二,Systemd 常用命令

Systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面,是一系列工具的集合。

2.1. systemctl

systemctlSystemd 的主要命令,用于系统与服务管理。点击查看命令描述文档

  • 命令语法

    $ systemctl [OPTIONS...] COMMAND [UNIT...]
  • 常用命令
    下述命令以 Unit nginx.service 为列,后续会详细介绍 Unit。

    # 重启系统
    $ systemctl reboot
    
    # 启用一个或多个单元或单元实例
    $ systemctl enable nginx.service
    
    # 禁用一个或多个单位
    $ systemctl disable nginx.service
    
    # 重新启用一个或多个单位
    $ systemctl reenable nginx.service
    
    # 重新载入 unit 配置
    $ systemctl daemon-reload
    
    # 启动 unit 资源  
    $ systemctl start nginx.service
    
    # 重启一个服务
    $ systemctl restart nginx.service
    
    # 重新加载一个服务的配置文件 
    $ systemctl reload nginx.service
    
    # 停止 unit 资源
    $ systemctl stop nginx.service
    
    # 查看 unit 资源 状态
    $ systemctl status nginx.service
    
    # 杀死一个服务的所有子进程
    $ systemctl kill nginx.service
    
    # 命令行输出 unit 配置
    $ systemctl cat nginx.service
    
    # 显示 systemd Manager 环境块
    $ systemctl show-environment
    
    # 列出 systemd 当前在内存中的 unit
    $ systemctl list-units
    
    # 查看 Unit 存在依赖关系
    $ systemctl list-dependencies nginx.service
    
    # 显示某个 Unit 的所有底层参数
    $ systemctl show nginx.service

2.2. systemd-analyze

systemd-analyze 分析和调试系统管理。点击查看命令描述文档

  • 命令语法

    $ systemd-analyze [OPTIONS...] [time]
    
    $ systemd-analyze [OPTIONS...] blame
    
    $ systemd-analyze [OPTIONS...] critical-chain [UNIT...]
  • 常用命令

    # 查看启动耗时
    $ systemd-analyze time
    
    # 查看每个服务的启动耗时
    $ systemd-analyze blame
    
    # 显示瀑布状的启动过程流
    $ systemd-analyze critical-chain
    
    # 显示指定服务的启动流
    $ systemd-analyze critical-chain atd.service

2.3. hostnamectl

hostnamectl 可用于查询和更改系统主机名和相关设置。点击查看命令描述文档

  • 命令语法

    $ hostnamectl [OPTIONS...] {COMMAND}
  • 常用命令

    # 查看主机的信息
    $ hostnamectl status
    
    # 设置主机名
    $ hostnamectl set-hostname york

2.4. journalctl

journalctl 查询系统日志。点击查看命令描述文档

  • 命令语法

    $ journalctl [OPTIONS...] [MATCHES...]
  • 常用命令

    # 查看所有日志
    $ journalctl
    
    # 查看指定 unit 日志
    $ journalctl _SYSTEMD_UNIT=httpd.service
    
    # 查看内核日志(不显示应用日志)
    $ journalctl -k
    
    # 查看系统本次启动的日志
    $ journalctl -b
    $ journalctl -b -0
    
    # 查看上一次启动的日志(需更改设置)
    $ journalctl -b -1
    
    # 显示尾部的最新10行日志
    $ journalctl -n
    
    # 显示尾部指定行数的日志
    $ journalctl -n 20
    
    # 实时滚动显示最新日志
    $ journalctl -f
    
    # 查看指定服务的日志
    $ journalctl /usr/lib/systemd/systemd
    
    # 查看指定进程的日志
    $ journalctl _PID=1
    
    # 查看某个路径的脚本的日志
    $ journalctl /usr/bin/bash
    
    # 查看指定用户的日志
    $ journalctl _UID=33 --since today

2.5. loginctl

loginctl 系统登陆管理器。点击查看命令描述文档

  • 命令语法

    $ loginctl [OPTIONS...] {COMMAND} [NAME...]
  • 常用命令

    # 列出当前登录用户
    $ loginctl list-users
    
    # 列出显示指定用户的信息
    $ loginctl show-user root
    
    # 查看 sission 详情
    $ loginctl show-session  130423 559 59

2.6. timedatectl

timedatectl 系统时间和日期查看与管理。点击查看命令描述文档

  • 命令语法

    $ timedatectl [OPTIONS...] {COMMAND}
  • 常用命令

    # 查看当前时区设置
    $ timedatectl
    
    # 显示所有可用的时区
    $ timedatectl list-timezones                                      
    
    # 设置当前时区
    $ timedatectl set-timezone America/New_York

三,Systemd 资源

Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)。

点击查看 systemd.unit 描述文档
点击查看 systemd.service 描述文档

3.1. Unit 种类

Unit 文件是 ini 风格的纯文本文件。统一了过去各种不同系统资源配置格式,例如服务的启/停、定时任务、设备自动挂载、网络配置、虚拟内存配置等。而 Systemd 通过不同的文件后缀(如 .service)来区分这些配置文件。支持的 11 种 Unit 文件类型:

# 服务,封装守护进程的启动、停止、重启和重载操作,是最常见的一种 Unit 文件
service.service

# 套接字,监控来自于系统或网络的数据消息,用于实现基于数据自动触发服务启动
socket.socket

# 设备,对于 /dev 目录下的设备,主要用于定义设备之间的依赖关系
device.device

# 挂载点,定义系统结构层次中的一个挂载点,可以替代过去的 /etc/fstab 配置文件
mount.mount

# 自动挂载点,用于控制自动挂载文件系统,相当于 SysV-init 的 autofs 服务
automount.automount

# 交换分区或交换文件,定义一个用户做虚拟内存的交换分区
swap.swap

# 启动目标,用于对 Unit 文件进行逻辑分组,引导其它 Unit 的执行。它替代了 SysV-init 运行级别的作用,并提供更灵活的基于特定设备事件的启动方式
target.target

# 被监视的路径,用于监控指定目录或文件的变化,并触发其它 Unit 运行
path.path

# 任务计划,用于配置在特定时间触发的任务,替代了 Crontab 的功能
timer.timer

# 资源控制组,用于表示一个 CGroup 的树,通常用户不会自己创建这样的 Unit 文件 
slice.slice

# 一组外部创建的进程,这种 Unit 文件不是用户创建的,而是 Systemd 运行时产生的,描述一些系统服务的分组信息
scope.scope

3.2. Unit 文件结构

下面这个 foo.service Unit。

# [Unit]区块包含与单元类型无关的通用信息
[Unit]
Description=Foo

# [Service]区块包含服务启动信息
[Service]
ExecStart=/usr/sbin/foo-daemon

# [Install]区块包含单元的启用信息
[Install]
WantedBy=multi-user.target
  • 从上面的输出可以看到,配置文件分成几个区块。每个区块的第一行,是用方括号表示的区别名,比如:[Unit]
  • 区块内容
    每个区块内部是一些等号连接的键值对。
    [Service]
    ExecStart=/usr/sbin/foo-daemon
    ExecStop=xxx
  • 配置文件注释使用 # 开头
  • 注意事项
    配置文件的区块名和字段名,都是大小写敏感的
    键值对的等号两侧不能有空格。

3.3. [Unit] 区块

Unit 文件中的 [Unit] 区块包含与 Unit 类型无关的通用信息。可用的如下配置指令如下:

Description:有利于人类阅读的、对单元进行简单描述的字符串。

Documentation:一组用空格分隔的文档URI列表, 这些文档是对此单元的详细说明。

Requires:设置此单元所必须依赖的其他单元。

Wants:此选项是 Requires= 的弱化版。

BindsTo:与 Requires= 类似,但是依赖性更强

Before, After:强制指定单元之间的先后顺序,接受一个空格分隔的单元列表。

Conflicts:指定单元之间的冲突关系。表明该单元不能与列表中的任何单元共存

3.4. [Service] 区块

[Service] 区块用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。包含服务启动信息。可用的如下配置指令如下:

Type:设置进程的启动类型。取值为 simple, exec, forking, oneshot, dbus, notify, idle 之一。

RemainAfterExit:当该服务的所有进程全部退出之后, 是否依然将此服务视为活动(active)状态。 默认值为 no

PIDFile:该服务PID文件的路径(一般位于 /run/ 目录下)。 强烈建议在 Type=forking 的情况下明确设置此选项。

BusName:设置与此服务通信 所使用的 D-Bus 名称。

ExecStart:在启动该服务时需要执行的 命令行(命令+参数),命令行必须以一个可执行文件(要么是绝对路径、要么是不含任何斜线的文件名)开始, 并且其后的那些参数将依次作为"argv[1] argv[2] …"传递给被执行的进程。

ExecReload:这是一个可选的指令, 用于设置当该服务 被要求重新载入配置时 所执行的命令行。 

ExecStop:这是一个可选的指令, 用于设置当该服务被要求停止时所执行的命令行。 

Restart:当服务进程 正常退出、异常退出、被杀死、超时的时候, 是否重新启动该服务。所谓"服务进程" 是指 ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, ExecReload= 中设置的进程。

Environment:指定环境变量

User:指定运行服务的用户

Group:指定运行服务的用户组

WorkingDirectory:指定服务的工作目录

3.5. [Install] 区块

[Install] 区块包含单元的启用信息。 当执行命令 systemctl enable 与 disable 操作 Unit 时才会使用此小节。

Alias:启用时使用的别名,可以设为一个空格分隔的别名列表。

WantedBy:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中

RequiredBy:它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入/etc/systemd/system目录下面以 Target 名 + .required后缀构成的子目录中

Also:设置此单元的附属单元,表示当使用 systemctl enable 启用 或 systemctl disable 停用 此单元时, 也同时自动的启用或停用附属单元。

3.6. Unit 文件加载

Systemd 将会从一组在编译时设定好的 Unit 目录 中加载单元文件,目录拥有不同的优先级。高优先级目录中的文件, 将会覆盖低优先级目录中的同名文件。

  • 当 systemd 以系统实例(–system)运行时,加载单元的先后顺序(较前的目录优先级较高)
系统单元目录描述
/run/systemd/transient动态配置的临时单元(系统与全局用户共用)
/etc/systemd/system本地配置的系统单元
/run/systemd/system运行时配置的系统单元
/usr/lib/systemd/system本地软件包安装的系统单元
/usr/lib/systemd/system发行版软件包安装的系统单元
  • 当 systemd 以用户实例(–user)运行时,加载单元的先后顺序(较前的目录优先级较高)
系统单元目录描述
/etc/systemd/user本地配置的全局用户单元
/run/systemd/user运行时配置的全局用户单元
/usr/lib/systemd/user本地软件包安装的全局用户单元
/usr/lib/systemd/user发行版软件包安装的全局用户单元

3.7. 设置开机启动

可使用 systemctl enable 命令激活 Unit 开机启动。

Systemd 默认从目录 /etc/systemd/system/ 读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录 /usr/lib/systemd/system/,真正的配置文件存放在这个目录。

systemctl enable 命令用于在上面两个目录之间,建立符号链接关系。

# 激活开机启动
$ systemctl enable koa.service

# 等同于
$ ln -s '/usr/lib/systemd/system/koa.service' '/etc/systemd/system/multi-user.target.wants/koa.service'

四,Systemd 资源管理

该节使用 Systemd 管理 Node.js 应用,实现应用的开机自启动。

4.1. Node.js 应用

新建文件 /data/app/node.js/node-systemd-demo/app.js,其内容如下。

const http = require('http');

http.createServer((request, response) => {
    response.write('Systemd 从入门到实战');
    response.end();
}).listen(8091);

4.2. 配置 Unit

配置 Systemd 的 Unit 文件,用于管理 Node.js 应用。新建文件 /usr/lib/systemd/system/node-demo.service,其内容如下:

#!/bin/bash
# 服务说明
[Unit]
# 服务描述
Description=node-systemd-demo
# 前置需要启动的服务
After=sysinit.target

# 服务启动行为
[Service]
# 服务类型
Type=simple
ExecStart=/usr/local/services/nodejs/bin/node /data/app/node.js/node-systemd-demo/app.js 
# 设置预加载变量
Environment="PATH=/usr/local/services/nodejs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/.ft:/root/bin"
# 执行用户
User=root
# 执行用户组
Group=root
WorkingDirectory=/data/app/node.js/node-systemd-demo

# 服务开机启动安装目标
[Install]
# 表示该服务所在的 Target
WantedBy=multi-user.targe

4.3. 启动应用

使用 Systemd 启动运行 node-demo.service,执行下命令:

# 设置可执行权限
$ chmod +x /usr/lib/systemd/system/node-demo.service

# 重新载入 Unit 配置
$ systemctl daemon-reload

# 启动 Unit 服务
$ systemctl start node-demo.service

# 查看 Unit 服务状态
$ systemctl status node-demo.service

# 请求 node-systemd-demo 服务
$ curl -v http://127.0.0.1:8091

blog/20201122153009_a36ee43dd7aeefc8076aa79a0a182a12.png

blog/20201122153133_9d90332a78ac2abb8a0b6f82763239ff.png

4.4. 设置开机启动

使用 Systemd 配置 node-demo.service 服务开机启动,执行下命令:

# 激活开机启动
$ systemctl enable node-demo.service

# 或者 ln 激活开机启动
ln -s /usr/lib/systemd/system/node-demo.service /etc/systemd/system/multi-user.target.wants/

# 重启 linux  
$ reboot

激活开机启动后,会在 /etc/systemd/system/multi-user.target.wants/ 目录中添加 node-demo.service 软链接。
blog/20201122154404_8d8fd04ca2203ed78344cf0f2a5c96f2.png

执行 reboot 后,执行命令 systemctl status node-demo.service 查看服务状态。
blog/20201122154551_6963d54009d7a5d941869d84de77b663.png

参考