systemd服务和systemd相关命令
systemd介绍
systemd即为system daemon,是linux下的一种init软件。它在Centos7系统中是PID为1的进程。
在CentOS5和CentOS6的启动是采取SystemV init来管理的
先说一下SystemV它其实就是利用一系列脚本来启动服务的,依赖一个特定的启动顺序每次只能启动执行一个启动任务。
这些都是通过一个核心配置文件tab(/etc/init)和一组启动脚本以及符号链接集执行的,本质上为系统提供了合理的启动顺序,
支持不同的运行级别。
他的好处是依赖关系简单,任务之间泾渭分明的一个一个启动,即使某个基础服务出了错也便于排查。但也正因为如此,他的启动性能很不好。服务无法并行启动不说,而且只能按照预先规定的顺序启动服务。如果你安装了新的硬件或者新服务,他不提供及时支持的标准方法。最重要的是如果其中有一步没有执行成功可能造成系统无法启动,如常见的开机挂载磁盘,如果磁盘找不到可能造成系统无法启动的问题。
这是一个网上的图片,从中可以看到用户空间init的服务分别有JobA、JobB等,它必须一个接一个的启动,因此启动时间是他们的执行时间总和。
它的改进版Upstart在此基础上改成了互不相关的服务可以并行启动,这样启动总时间就等于时间消耗最大的一组服务,而不是所有服务之和。systemd
在并行启动上采取了比Upstart更加激进的方案
如果服务之间有依赖关系,如A依赖于B的启动,那么A在启动完毕B还没有启动完毕时,A先发请求给B,B会将A的请求缓存,当B启动完毕后在进行处理。
当然这样做也有不好的地方,它增加的出现问题时的排查难度,你不能很快的知道哪些服务起来了哪些没起来
它违反"keep simple, keep stupid"的Unix 哲学,使systemd与其他服务成了强耦合
systemd架构图
Systemd 是通过unit 及其配置文件来管理控制系统服务、监听的socket、快照及其他与init相关的信息的。
systemd启动步骤
- systemd加载配置信息,在上一文的启动过程中有具体说明
- 判断启动级别,即default.target所指向的级别
- 判断启动目标的依赖关系
- 激活依赖服务,启动目标
- 响应系统消息,激活其他组件
systemd中的单元
systemd除了负责处理进程和服务,还能挂载文件系统、监控网络套接字、快照和管理环境变量等等。systemd中所有服务和功能都被抽象成一个个单元(Unit),根据功能不同,单元类型也不同。systemd正是通过配置这些单元来开关、管理服务的。
单元的后缀多数为.service
Unit单元类型
以下单元类型支持自定义
systemd单元类型 | ||
---|---|---|
单元类型 | 扩展名 | 说明 |
Service | .service | 描述一个系统服务 |
Socket | .socket | 描述一个进程间通信的套接字 |
Device | .device | 描述一个内核识别的设备文件(硬件) |
Mount | .mount | 描述一个文件系统的挂载点 |
Automount | .automount | 描述一个文件系统的自动挂载点 |
Swap | .swap | 描述一个内存交换设备或交换文件 |
Path | .path | 描述一个文件系统中文件或目录 |
Timer | .timer | 描述一个定时器(用于实现类似cron的调度任务) |
Snapshot | .snapshot | 用于保存一个systemd的状态 |
Scope | .scope | 用systemd的总线接口以编程的方式创建外部进程(即不是systemd管理的外部进程) |
Slice | .slice | 描述居于Cgroup的一组通过层次组织的管理系统进程(进程组) |
Target | .target | 描述组systemd的 单元 |
Unit配置文件
每一个 Unit 都有一个配置文件,告诉 Systemd 怎么启动这个 Unit
unit文件通常被放置于/usr/lib/systemd/system、/etc/systemd/system、/run/systemd/system处
Systemd默认从目录/etc/systemd/system/读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录/usr/lib/systemd/system/,真正的配置文件存放在这个目录。
默认的配置文件后缀名是.service,即不指定后缀为其他的后缀如.socket、.mount等,那么系统认为它的后缀就是.service.
systemd文件一般由:[Unit] 、[Service] 、 [Install]三部分组成
官方定义为[Unit] 、 [unit type] 、 [Install]
下面举一个例子如sshd服务,
注意:容都是以键值对的形式存在且大小写敏感,以及等式两侧不可以有空格
cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
[Unit]区块
key | value |
---|---|
Description | 简短描述 |
Documentation | 文档地址 |
Requires | 当前 Unit 依赖的其他 Unit,如果它们没有运行,当前 Unit 会启动失败 |
Wants | 与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败 |
BindsTo | 与Requires类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行 |
Before | 如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动 |
After | 如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动 |
Conflicts | 这里指定的 Unit 不能与当前 Unit 同时运行 |
Condition… | 当前 Unit 运行必须满足的条件,否则不会运行 |
Assert… | 当前 Unit 运行必须满足的条件,否则会报启动失败 |
[Unit type]区块
这里以service为例,其他的不是很常用
key | value |
---|---|
Type | 定义启动时的进程行为。它有simple、forking、oneshot、dbus、notify、idle几种值。 |
User | 设置服务运行的用户 |
Group | 设置服务运行的用户组 |
PIDFile | 存放PID的文件路径,读取服务启动后守护程序主进程的PID。服务关闭会删除该文件(如果文件还存在) |
ExecStart | 启动当前服务的命令 |
ExecStartPre | 启动当前服务之前执行的命令 |
ExecStartPost | 启动当前服务之后执行的命令 |
ExecReload | 重启当前服务时执行的命令 |
ExecStop | 停止当前服务时执行的命令 |
ExecStopPost | 停止当其服务之后执行的命令 |
RestartSec | 自动重启当前服务间隔的秒数,默认值0.1(s) |
StartLimitInterval | 无限次重启,默认是10秒内如果重启超过5次则不再重启,设置为0表示不限次数重启 |
Restart | 定义何种情况 Systemd 会自动重启当前服务,可能的值包括always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog |
RestartPreventExitStatus | 符合某些退出状态时不进行重启 |
TimeoutStartSec | 等待启动的时间。 守护进程服务没有在配置时间内发送启动完成的信号,认为该服务失败服务退出。 以秒为单位, “0”来禁用。 默认使用DefaultTimeoutStartSec,若使用Type=oneshot,则该模式默认情况下超时是禁用的 |
TimeoutStopSec | 等待关闭的超时时间 |
TimeoutSec | 快速配置TimeoutStartSec和TimeoutStopSec时间 |
Environment | 指定环境变量 |
KillMode | control-group、mixed、none |
PrivateTmp=True | 表示给服务分配独立的临时空间 注意:[Service]部分的启动、重启、停止命令必须使用绝对路径,使用相对路径则会报错 |
KillSignal | 服务的所有进程根据 KillSignal的设置被立即全杀死 |
type几种类型的解释
simple:默认值,执行ExecStart指定的命令,启动主进程
forking:以fork方式从父进程创建子进程,创建后父进程立即退出
oneshot:一次性进程,Systemd会等当前服务退出,再继续执行
dbus:当前服务通过D-Bus启动
notify:当前服务启动完毕,会通知Systemd,再继续往下执行
idle:若有其他任务执行完毕,当前服务才会运行
Restart的几种类型解释
no(默认值):退出后不会重启
on-success:只有正常退出时(退出状态码为0),才会重启
on-abnormal:只有被信号终止和超时,才会重启
on-abort:只有在收到没有捕捉到的信号终止时,才会重启
on-watchdog:超时退出,才会重启
always:不管是什么退出原因,总是重启
KillMode的几种类型解释
control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
process:只杀主进程
mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
none:没有进程会被杀掉,只是执行服务的 stop 命令。
下面举一个不是service类型的Unit type
cat /usr/lib/systemd/system/tmp.mount
[Unit]
Description=Temporary Directory
Documentation=man:hier(7)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
ConditionPathIsSymbolicLink=!/tmp
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
[Mount]
What=tmpfs
Where=/tmp
Type=tmpfs
Options=mode=1777,strictatime
# Make 'systemctl enable tmp.mount' work:
[Install]
WantedBy=local-fs.target
[Install]区块
key | value |
---|---|
WantedBy | 依赖该服务的服务列表 |
RequiredBy | 表示该服务所在的Target |
Alias | 当前 Unit 可用于启动的别名 |
Also | 当前 Unit 激活(enable)时,会被同时激活的其他 Unit |
编写一个自己的.service文件
cat /usr/lib/systemd/system/myshell.service
[Unit]
Description=my test service
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
PIDFile=/tmp/my.pid
ExecStartPre=/usr/bin/rm -f /tmp/my.pid
ExecStart= /usr/sbin/test.sh
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
#[Install]
#WantedBy=multi-user.target
cat test.sh
#!/bin/bash
while true
do
echo `date`,"ok" >>/tmp/result.log
done
#!/bin/bash是必须的,否则会提示203错误异常
简单的自启动示例
[Unit]
Description=mytest
[Service]
Type=simple
ExecStart=/root/mytest.sh
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
当业务因受到OOM而退出时,不希望重新启动,其他情况下自动启动
[Unit]
Description=mytest
[Service]
Type=simple
ExecStart=/root/mem
Restart=always
RestartSec=5
StartLimitInterval=0
RestartPreventExitStatus=SIGKILL
[Install]
WantedBy=multi-user.target
#/root/mem.c(不断消耗内存直到OOM)
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main ()
{
char *c = NULL;
int count = 1;
while(1){
c = (char *)malloc(1024*2048*50);
if(!c){
printf("malloc error!n");
return -1;
}
memset(c, 0, 1024*2048*50);
printf("malloc %dM memoryn", 100*count++);
usleep(500000);
}
}
gcc -o /root/mem /root/mem.c
systemctl daemon-reload
systemctl start mytest
测试结果
[root@fzxiaomange ~]# systemctl status mytest
● mytest.service - mytest
Loaded: loaded (/usr/lib/systemd/system/mytest.service; disabled; vendor preset: disabled)
Active: failed (Result: signal) since Sat 2018-10-20 23:32:24 CST; 45s ago
Process: 10555 ExecStart=/root/mem (code=killed, signal=KILL)
Main PID: 10555 (code=killed, signal=KILL)
Oct 20 23:31:55 fzxiaomange.com systemd[1]: Started mytest.
Oct 20 23:31:55 fzxiaomange.com systemd[1]: Starting mytest...
Oct 20 23:32:24 fzxiaomange.com systemd[1]: mytest.service: main process exited, code=killed, status=9/KILL
Oct 20 23:32:24 fzxiaomange.com systemd[1]: Unit mytest.service entered failed state.
Oct 20 23:32:24 fzxiaomange.com systemd[1]: mytest.service failed.
重点看上面第6行 MainPID:10555(code=killed,signal=KILL),这行表示主进程的状态,常见有2种情况
code=exited, status=143:表示systemd认为主进程自行退出的,exit code为143
code=killed, signal=KILL:表示systemd认为主进程是被kill的,接收到的信号是SIGKILL
等待5秒后,并没有自动重启,符合预期
此时将RestartPreventExitStatus=SIGKILL改为RestartPreventExitStatus=SIGTERM
执行systemctl restart mytest,再进行一次观察,等待5秒后,服务自动重启,符合预期
服务状态列表
状态 | 描述 |
---|---|
loaded | 系统服务已经初始化完成,加载过配置 |
actvie(running) | 正常运行 |
actvie(exited) | 正常结束的服务, |
active(waitting) | 正在执行当中, 等待其他的事件才继续处理 |
inactive | 服务关闭 |
enabled | 服务开机启动 |
disabled | 服务开机不自启 |
static | 服务开机启动项不可被管理 |
falied | 系统配置错误 |
systemd中的系统管理
Systemd并不是一个命令,而是一组命令,涉及许多系统管理
systemctl
systemctl功能特别多,有系统管理、服务管理等
系统管理
# 检查系统是否完全运行
# running 系统完全可控;
# degraded 系统可控,但存在单元失败;
# maintenance 营救模式启动;
# stopping 管理器停止
systemctl is-system-running
# 设置一个或多个环境变量
systemctl set-environment NAME=VALUE
# 取消设置一个或多个环境变量
systemctl unset-environment NAME
# 重启系统
systemctl reboot
# 关闭系统,切断电源
systemctl poweroff
# CPU停止工作
systemctl halt
# 暂停系统
systemctl suspend
# 让系统进入冬眠状态
systemctl hibernate
# 让系统进入交互式休眠状态
systemctl hybrid-sleep
# 启动进入救援状态(单用户状态)
sudo systemctl rescue
# 进入紧急模式
systemctl emergency
服务管理
# 显示单个 Unit 的状态
sysystemctl status httpd.service
# 显示远程主机的某个 Unit 的状态
systemctl -H root@test.com status httpd.service
# 立即启动一个服务
systemctl start httpd.service
# 立即停止一个服务
systemctl stop httpd.service
# 重启一个服务
systemctl restart httpd.service
# 尝试重启某个服务
systemctl try-restart httpd
# 杀死一个服务的所有子进程
systemctl kill httpd.service
# 重新加载一个服务的配置文件
systemctl reload httpd.service
# 设置一个服务开机启动
systemctl enable httpd.service
# 设置一个服务开机不启动
systemctl disable httpd.service
# 屏蔽某个服务(屏蔽后start无法启动)
systemctl mask httpd
# 显示某个服务(可以启动)
systemctl unmask httpd
# 显示某个 Unit 是否正在运行
systemctl is-active application.service
# 显示某个 Unit 是否处于启动失败状态
systemctl is-failed application.service
# 显示某个 Unit 服务是否建立了启动链接,即是否为开机启动
systemctl is-enabled application.service
#查看一个服务的依赖关系(如果依赖target需要展开加--all)
systemctl list-dependencies nginx.service
# 列出正在运行的 Unit
systemctl list-units
# 列出所有Unit,包括没有找到配置文件的或者启动失败的
systemctl list-units --all
# 列出所有没有运行的 Unit
systemctl list-units --all --state=inactive
# 列出所有加载失败的 Unit
systemctl list-units --failed
systemctl --failed
# 列出所有正在运行的、类型为 service 的 Unit
systemctl list-units --type=service
# 显示系统状态
systemctl status
配置文件管理
# 列出所有配置服务, -t 指定类型
systemctl list-unit-files
systemctl list-unit-files -t socket
sysemctl list-unit-files --type=service
systemctl list-unit-files -t mount
#查看一个服务的unit配置文件
systemctl cat httpd.service
# 显示某个 Unit 的所有配置参数细节
systemctl show httpd.service
# 显示某个 Unit 的指定属性的值
systemctl show -p CPUShares httpd.service
# 设置某个 Unit 的指定属性
systemctl set-property httpd.service CPUShares=500
# 重载所有修改过的配置文件
systemctl daemon-reload
运行级别管理
# 查看启动时的默认 Target
systemctl get-default
# 设置启动时的默认 Target
systemctl set-default multi-user.target
# 关闭前一个 Target 里面所有不属于后一个 Target 的进程
# 临时切换运行级别
systemctl isolate multi-user.target
journalctl
日志管理
Systemd Journal用二进制格式保存所有日志信息,用户使用journalctl命令来查看日志信息。相较于syslog无需再进行字符串分析处理。
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
journalctl
# 查看内核日志(不显示应用日志)
journalctl -k
# 查看系统本次启动的日志
journalctl -b
journalctl -b -0
# 查看上一次启动的日志(需更改设置)
journalctl -b -1
# 查看指定时间的日志
journalctl --since="2020-08-20 12:00:00"
journalctl --since "20 min ago"
journalctl --since yesterday
journalctl --since "2020-08-23" --until "2020-08-24 12:00"
journalctl --since 09:00 --until "1 hour ago"
# 显示尾部的最新10行日志
journalctl -n
# 显示尾部指定行数的日志
journalctl -n 20
# 实时滚动显示最新日志
journalctl -f
# 查看指定服务的日志
journalctl /usr/lib/systemd/systemd
# 查看指定进程的日志
journalctl _PID=1
# 查看某个路径的脚本的日志
journalctl /usr/bin/bash
journalctl /usr/sbin/crond
journalctl /usr/bin/passwd
# 查看指定用户的日志
journalctl _UID=1 --since today
# 查看某个 Unit 的日志
journalctl -u httpd.service
journalctl -u httpd.service --since today
# 实时滚动显示某个 Unit 的最新日志
journalctl -u httpd.service -f
# 合并显示多个 Unit 的日志
journalctl -u httpd.service -u mysqld.service --since today
# 查看指定优先级(及其以上级别)的日志,共有8级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
journalctl -p err -b
# 日志默认分页输出,--no-pager 改为正常的标准输出
journalctl --no-pager
# 以 JSON 格式(单行)输出
journalctl -b -u httpd.service -o json
# 以 JSON 格式(多行)输出,可读性更好
journalctl -b -u httpd.service -o json-pretty
# 显示日志占据的硬盘空间
journalctl --disk-usage
# 指定日志文件占据的最大空间
journalctl --vacuum-size=1G
# 指定日志文件保存多久
journalctl --vacuum-time=1years
systemd-analyze
启动相关的耗时
# 查看启动耗时
systemd-analyze
# 查看每个服务的启动耗时
systemd-analyze blame
# 显示瀑布状的启动过程流
systemd-analyze critical-chain
# 显示指定服务的启动流
systemd-analyze critical-chain atd.service
hostnamectl
主机的一些信息设置
# 显示当前主机的信息
hostnamectl
# 设置主机名。
hostnamectl set-hostname wzp
# 设置平台类型
hostnamectl set-chassis vm
# 设置主机icon名
hostnamectl set-icon-name computer-vm
localectl
用于控制系统的本地化与键盘布局
# 查看本地化设置
localectl
# 列出所有可用的 locale
localectl list-locales
# 设置字符集
localectl set-locale LANG=en_GB.utf8
# 列出所有可用的列出所有可用的控制台键盘映射
localectl list-keymaps
# 设置控制台的键盘映射(中国人应设为 "us")
localectl set-keymap us
# 列出所有可用的X11键盘的 Layout
localectl list-x11-keymap-models
# 设置默认的X11键盘布局(中国人应设为 "us")
localectl set-x11-keymap us
timedatectl
时间相关设置
# 显示系统的当前时间和日期
timedatectl status
RTC time是硬件时钟的时间。
# 查看所有可用的时区:
timedatectl list-timezones
# 根据地理位置找到本地的时区:
timedatectl list-timezones | grep "Asia/S.*"
# 在Linux中设置本地时区,使用set-timezone开关:
timedatectl set-timezone “Asia/Shagnhai”
# 可以使用timedatectl命令设置系统的日期和时间:
timedatectl set-time 15:58:30
#只设置时间的话可以使用set-time开关以及HH:MM:SS(小时,分,秒)的时间格式。
# 只设置日期的话可以使用set-time开关以及YY:MM:DD(年,月,日)的日期格式。
timedatectl set-time 20200824
# 设置日期和时间:
timedatectl set-time '16:10:40 2020-08-24'
# 设置硬件时钟为UTC可以使用 set-local-rtc boolean-value选:
timedatectl | grep local #首先确定硬件时钟是否设置为本地时区:
timedatectl set-local-rtc 1 #将硬件时钟设置为本地时区:
timedatectl set-local-rtc 0 #将硬件时钟设置为UTC:
loginctl
查看当前登录的用户
# 列出当前session
loginctl list-sessions
# 列出当前登录用户
loginctl list-users
# 列出显示指定用户的信息
loginctl show-user ruanyf
参考文献:http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html