[toc]
一、Systemd 是什么?
systemd 是一个属于用户空间的系统服务管理程序,在红帽 RHEL7 上采用,替代了原来 RHEL6 上的 systemVinit。 其作用是,在内核启动完成后,初始化用户空间的进程,进程号为 1 、管理操作系统的运行级别,系统的服务启动和关闭,系统的挂载点。
systemd 的特性:
- 支持服务并行启动,实现快速开机
- 按需启动守护进程
- 支持快照和系统恢复
- 各服务间依赖关系管理
- 同时采用socket式与D-Bus总线式激活服务
- 维护挂载点和自动挂载点
- 可以通过 Linux 的 cgroups 监控运行的进程
二、Systemd 管理系统的运行级别
查看当前默认的启动级别
systemctl get-default
切换到多用户级别
systemctl isolate multi-user.target
或
systemctl isolate runlevel3.target
设置系统默认启动的运行级别
systemctl set-default multi-user.target
运行级别对应表
systemd target | 解释 |
---|---|
runlevel0.target, poweroff.target | 关闭系统 —> 0 |
runlevel1.target, rescue.target | 单用户模式 —> 1 |
runlevel2.target, runlevel4.target, multi-user.target | 用户定义/域特定运行级别。默认等同于 3 —> 2,4 |
runlevel3.target, multi-user.target | 最常用模式,非图形化多用户 —> 3 |
runlevel5.target, graphical.target | 多用户图形化 —> 5 |
runlevel6.target, reboot.target | 重启 —> 6 |
systemd 运行级别配置目录
/etc/systemd/system/
三、Systemd 管理系统服务
认识 Systemd 下的 unit 文件
在 Systemd 下 unit 文件用于配置对资源的控制,每种资源对应不同的 unit 文件类型,比如后台服务(service)、套接字(socket)、设备(device)、挂载点(mount) 等。 所有 unit 文件都包含两个通用段 [Unit] 、[Install] 和一个资源类型段,如 [Service] 就表示后台服务。
unit 类型如下:
-
service :守护进程的启动、停止、重启和重载是此类 unit 中最为明显的几个类型。
-
socket :此类 unit 封装系统和互联网中的一个socket。当下,systemd支持流式, 数据报和连续包的AF_INET,AF_INET6,AF_UNIXsocket 。也支持传统的 FIFOs 传输模式。每一个 socket unit 都有一个相应的服务 unit 。相应的服务在第一个“连接”进入 socket 或 FIFO 时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
-
device :此类 unit 封装一个存在于 Linux设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备 unit 出现。udev 的属性设置可以作为配置设备 unit 依赖关系的配置源。
-
mount:此类 unit 封装系统结构层次中的一个挂载点。
-
automount :此类 unit 封装系统结构层次中的一个自挂载点。每一个自挂载 unit 对应一个已挂载的挂载 unit (需要在自挂载目录可以存取的情况下尽早挂载)。
-
target :此类 unit 为其他 unit 进行逻辑分组。它们本身实际上并不做什么,只是引用其他 unit 而已。这样便可以对 unit 做一个统一的控制。(例如:multi-user.target 相当于在传统使用 SysV 的系统中运行级别5);bluetooth.target 只有在蓝牙适配器可用的情况下才调用与蓝牙相关的服务,如:bluetooth 守护进程、obex 守护进程等)
- snapshot :与 targetunit 相似,快照本身不做什么,唯一的目的就是引用其他 unit 。
编写后台服务的 unit 文件
对应到 centos6 其实就是 /etc/init.d/ 下面的服务脚本,只是到了 systemd 里面叫做 service unit 文件,命名必须以 .service 结尾。
-
service unit(服务脚本) 文件在系统中的存放路径
/usr/lib/systemd/system
- 先来看一个简单的示例
[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
PIDFile=/alidata/server/nginx/logs/nginx.pid
ExecStart=/alidata/server/nginx/sbin/nginx -c /alidata/server/nginx/conf/nginx.conf
ExecReload=/alidata/server/nginx/sbin/nginx -s reload
ExecStop=/alidata/server/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
整个文件分为三个部分,[Unit]、[Service]、[Install]
[Unit] :记录文件的通用信息,如描述,开机启动顺序
[Service] :记录 unit 文件类型,服务控制的相关指令
[Install] :安装信息
Unit 常用配置指令
-
Description:对本service的描述。
-
Before, After:定义启动顺序,Before=xxx.service,代表本服务在xxx.service启动之前启动。After=xxx.service,代表本服务在xxx之后启动。
-
Requires: 这个单元启动了,那么它“需要”的单元也会被启动; 它“需要”的单元被停止了,它自己也活不了。但是请注意,这个设定并不能控制某单元与它“需要”的单元的启动顺序(启动顺序是另外控制的),即 Systemd 不是先启动 Requires 再启动本单元,而是在本单元被激活时,并行启动两者。于是会产生争分夺秒的问题,如果 Requires 先启动成功,那么皆大欢喜; 如果 Requires 启动得慢,那本单元就会失败(Systemd 没有自动重试)。所以为了系统的健壮性,不建议使用这个标记,而建议使用 Wants 标记。可以使用多个 Requires。
-
Wants:推荐使用。本单元启动了,它“想要”的单元也会被启动。但是启动不成功,对本单元没有影响。
- Conflicts:一个单元的启动会停止与它“冲突”的单元,反之亦然。
Service 常用配置指令
-
Type:service的种类,包含下列几种类型:
- simple 默认,这是最简单的服务类型。意思就是说启动的程序就是主体程序,这个程序要是退出那么一切都退出。
- forking 表示 ExecStart= 进程将会在启动过程中使用 fork() 系统调用,启动成功以后,父进程将会退出,而子进程将作为该服务的主进程继续运行。 在启动守护进程时使用这个类型。
- oneshot 用于一次性执行服务,比如清理临时文件,该类型可以设置多个 ExecStart= 指令,它们按照出现的顺序依次执行。一旦遇到错误,就会立即停止,不再继续执行,同时服务也将进入 failed 状态。 在指定的 ExecStart= 指令执行结束前,该服务一直处于 “启动中”(activating) 状态,而一旦执行结束,该服务又立即变为停止(inactive)状态。也就是说该服务不存在 活动(active) 状态。 这意味着如果再一次启动服务,将会再一次执行服务定义的动作,先后顺序会晚于该服务单元,将会一直等到该服务变成"停止"(inactive)状态后,才开始启动。 该类型常和 RemainAfterExit 指令同时使用。
-
RemainAfterExit :当该服务的所有进程全部退出之后, 是否依然将此服务视为活动(active)状态。 默认值为 no
-
PIDFile :指定守护进程的 PID 文件,强烈建议在 Type=forking 的情况下明确设置此选项。因为有些服务启动之后会派生多个进程,systemd 将会不知道哪一个才是该服务的主进程,指定 PID 文件有助于 Systemd 准确的找到服务的主进程。 systemd 不会写入此文件, 但会在此服务停止后删除它。 该指令要放在 ExecStart 上面。
-
ExecStart :启动服务时需要执行的命令,可以用 $变量名 引用 Environment 设置的进程环境变量 和 操作系统的环境变量 如 $USER
-
ExecStop :可选指令,用于设置服务被要求停止前所执行的指令,此处指令执行完后,该服务剩余的进程都会被 kill 掉。 如果该指令没填写,在停止服务时会直接 kill 该服务下的所有进程。
-
ExecStartPre :设置在 ExecStart 指令执行之前执行的命令
-
ExecStartPost :设置在 ExecStart 指令执行成功之后执行的命令
-
ExecStopPost :设置该服务停止之后执行的命令
-
Environment :设置进程的环境变量, 值是一个空格分隔的 VAR=VALUE 列表。 可以多次使用此选项以增加新的变量或者修改已有的变量 (同一个变量以最后一次的设置为准)。 若设为空, 则表示清空先前所有已设置的变量。 注意: (1)不会在字符串内部进行变量展开(也就是"$"没有特殊含义); (2)如果值中包含空格或者等号, 那么必须在字符串两边使用双引号(")界定。 例如: Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" 设置了 "VAR1", "VAR2", "VAR3" 三个变量,其值分别为 "word1 word2", "word3", "$word 5 6"
- EnvironmentFile :与 Environment= 类似, 不同之处在于此选项是从文本文件中读取环境变量的设置。 文件中的空行以及以分号(;)或井号(#)开头的行会被忽略, 其他行的格式必须符合 VAR=VALUE 的shell变量赋值语法。 行尾的反斜杠()将被视为续行符, 这与shell语法类似。 若想在变量值中包含空格, 则必须在值的两端加上双引号(")界定。文件必须用绝对路径表示(可以包含通配符)。 但可在路径前加上"-"前缀表示忽略不存在的文件。 可以多次使用此选项, 以从多个不同的文件中读取设置。 若设为空, 则表示清空所有先前已经从文件中读取的环境变量。
Install 常用配置指令
-
Alias : 启用时使用的别名,可以设为一个空格分隔的别名列表。 每个别名的后缀(也就是单元类型)都必须与该单元自身的后缀相同。systemctl enable 命令将会为每个别名创建一个指向该单元文件的软连接。 注意,因为 mount, slice, swap, automount 单元不支持别名,所以不要在这些类型的单元中使用此选项。
- WantedBy,RequiredBy : 接受一个空格分隔的单元列表, 表示在使用 systemctl enable 启用此单元时, 将会在每个列表单元的 .wants/ 或 .requires/ 目录中创建一个指向该单元文件的软连接。 这相当于为每个列表中的单元文件添加了 Wants=此单元 或 Requires=此单元 选项。 这样当列表中的任意一个单元启动时,该单元都会被启动。
应用示例
1、创建一个简单服务
[Unit]
Description=简单的Foo服务
[Service]
ExecStart=/usr/sbin/foo-daemon
[Install]
WantedBy=multi-user.target
该单元文件创建了一个运行 /usr/sbin/foo-daemon 守护进程的服务,未设置 Type= 等价于 Type=simple 默认设置。 注意,本例中的 /usr/sbin/foo-daemon 必须在启动后持续运行到服务被停止。 如果该进程只是为了派生守护进程,那么应该使用 Type=forking
2、一次性服务
[Unit]
Description=清理老旧的 Foo 数据
[Service]
Type=oneshot
ExecStart=/usr/sbin/foo-cleanup
[Install]
WantedBy=multi-user.target
Type=oneshot 用于只需要执行一次,不需要持久运行的单元,例如文件系统检查或者清理磁盘文件。此单元会启动后一直等待指定的动作完成, 然后再回到停止状态。 在 /usr/sbin/foo-cleanup 执行结束前, 该服务一直处于"启动中"(activating)状态,而一旦执行结束,该服务又立即变为"停止"(inactive)状态。 也就是说,对于 Type=oneshot 类型的服务,不存在"活动"(active)状态。 这意味着,如果再一次启动该服务,将会再一次执行该服务定义的动作。 注意,在先后顺序上晚于该服务的单元, 将会一直等到该服务变成"停止"(inactive)状态后, 才会开始启动。
Type=oneshot 是唯一可以设置多个 ExecStart= 指令的服务类型。 多个 ExecStart= 指令将按照它们出现的顺序依次执行, 一旦遇到错误,就会立即停止,不再继续执行, 同时该服务也将进入"失败"(failed)状态。
3、可停止的一次性服务
[Unit]
Description=简单的静态防火墙
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop
[Install]
WantedBy=multi-user.target
有时候,单元需要执行一个程序以完成某个设置(启动), 然后又需要再执行另一个程序以撤消先前的执行(停止)。 而第一次执行之后,该单元应该处于 活动(active) 状态,但实际上并无任何进程在运行。网络配置服务就是一个典型的例子,只需要执行一次的服务。
可以通过设置 RemainAfterExit=yes 来满足这种需求。 在这种情况下,systemd 将会在启动成功后将该单元视为处于"活动"(active)状态(而不是"停止"(inactive)状态)。 RemainAfterExit=yes 虽然可以用于所有 Type= 类型, 但是在实践中主要用于 Type=oneshot 和 Type=simple 类型。 对于 Type=oneshot 类型, systemd 一直等到服务启动成功之后,才会将该服务置于"活动"(active)状态。 所以,依赖于该服务的其他单元必须等待该服务启动成功之后,才能启动。 但是对于 Type=simple 类型, 依赖于该服务的其他单元无需等待,将会和该服务同时并行启动。
4、编写守护进程单元 nginx.service
[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
PIDFile=/alidata/server/nginx/logs/nginx.pid
ExecStart=/alidata/server/nginx/sbin/nginx -c /alidata/server/nginx/conf/nginx.conf
ExecReload=/alidata/server/nginx/sbin/nginx -s reload
ExecStop=/alidata/server/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
PrivateTmp 设为 yes 表示在进程的文件系统名字空间中挂载私有的 /tmp 与 /var/tmp 目录, 也就是不与名字空间外的其他进程共享临时目录。 这样做会增加进程的临时文件安全性,但同时也让进程之间无法通过 /tmp 或 /var/tmp 目录进行通信。 同时,当服务停止之后,所有先前在临时目录中创建的文件都将被删除。
5、编写守护进程单元 php7-fpm.service
[Unit]
Description=PHP7.0.8 start file
[Service]
Type=forking
PIDFile=/alidata/server/php7.0.8/var/run/php-fpm.pid
ExecStart=/alidata/server/php7.0.8/sbin/php-fpm --daemonize --fpm-config /alidata/server/php7.0.8/etc/php-fpm.conf --pid /alidata/server/php7.0.8/var/run/php-fpm.pid
ExecStartPost=/usr/bin/chown www.www /alidata/server/php7.0.8/var/run/php7-fpm.sock
StandardOutput=syslog
StandardError=inherit
[Install]
WantedBy=multi-user.target
- StandardOutput
设置进程的标准输出(STDOUT)。 可设为 inherit, null, tty, journal, syslog, kmsg, journal+console, syslog+console, kmsg+console, socket, fd 之一。 syslog 表示 syslog 日志服务。 注意,此时所有日志都会隐含的复制一份到 journal 中。
- StandardError
设置进程的标准错误(STDERR)。 取值范围及含义与 StandardOutput= 相同。 但有如下例外:(1) inherit 表示使用 StandardOutput= 的值。 (2) fd 的文件描述符名称的默认值为 "stderr" 此值默认为 inherit
6、编写守护进程单元 filebeat.service
[Unit]
Description=Filebeat sends log files to Logstash or directly to Elasticsearch.
Documentation=https://www.elastic.co/products/beats/filebeat
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/share/filebeat/bin/filebeat -c /etc/filebeat/filebeat.yml -path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat -E serverip=$serverip
Restart=always
[Install]
WantedBy=multi-user.target
注意,在 Centos7 使用 Systemd 启动 filebeat 时是不会传递系统环境变量的。 filebeat 如果在配置文件中引用了系统的环境变量,那么会导致服务启动失败。 报错大概是找不到那个引用了系统变量自定义的那个字段。 有两个解决方法:
1、在 unit 文件中使用 Environment 相关参数,定义要使用的环境变量,在 ExecStart 启动命令时引用。 -E serverip=$serverip
2、在 /etc/profile.d/ 目录下创建定义系统环境变量的文件,里面的内容类似
export serverip=ifconfig eth0 | grep 'inet' | awk '{print $2}' | cut -d':' -f2
,然后在启动命令时直接用 $变量名 引用系统环境变量。
7、编写守护进程单元 Logstash
# /usr/lib/systemd/system/logstash.service
# 自定义环境变量的写法
[Unit]
Description=Logstash start program
[Service]
Type=simple
Environment=LOGDIR=/alidata/log/logstash CONF=/alidata/logstash-6.5.2/ngx_app_api_log_analyze.conf DATA=/alidata/logstash-6.5.2/data
ExecStartPre=/usr/bin/rm -rf $DATA
ExecStartPre=/usr/bin/rm -rf $LOGDIR
ExecStart=/alidata/logstash-6.5.2/bin/logstash -f $CONF -l $LOGDIR
[Install]
WantedBy=multi-user.target
原生RPM包内置写法
# /etc/systemd/system/logstash.service
[Unit]
Description=logstash
[Service]
Type=simple
User=logstash
Group=logstash
# Load env vars from /etc/default/ and /etc/sysconfig/ if they exist.
# Prefixing the path with '-' makes it try to load, but if the file doesn't
# exist, it continues onward.
EnvironmentFile=-/etc/default/logstash
EnvironmentFile=-/etc/sysconfig/logstash
ExecStart=/usr/share/logstash/bin/logstash "--path.settings" "/etc/logstash"
Restart=always
WorkingDirectory=/
Nice=19
LimitNOFILE=16384
[Install]
WantedBy=multi-user.target
变量定义文件,EnvironmentFile
# /etc/default/logstash
LS_HOME="/usr/share/logstash"
LS_SETTINGS_DIR="/etc/logstash"
LS_PIDFILE="/var/run/logstash.pid"
LS_USER="logstash"
LS_GROUP="logstash"
LS_GC_LOG_FILE="/var/log/logstash/gc.log"
LS_OPEN_FILES="16384"
LS_NICE="19"
SERVICE_NAME="logstash"
SERVICE_DESCRIPTION="logstash"
JAVA_HOME="/usr/java/jdk1.8.0_162"
systemctl 服务管理命令
'''查看系统所有已安装的服务项'''
systemctl list-unit-files --type=service
'''查看系统所有运行的服务项'''
systemctl list-units --type=service
'''查看系统所有开机自启动的服务项'''
systemctl list-unit-files --type=service | grep enabled
'''查看出错的服务'''
systemctl list-units --type=service --state=failed
'''启动服务'''
systemctl start nginx.service
'''停止服务'''
systemctl stop nginx.service
'''重载服务的配置文件'''
systemctl reload nginx.service
'''查看服务的状态'''
systemctl status nginx.service
'''设置服务开机启动'''
systemctl enable nginx.service
'''禁用服务的开机启动'''
systemctl disable nginx.service
'''禁止服务的启动,用于防止服务被其他服务间接启动,也无法通过 start 或 restart 命令来启动'''
systemctl mask nginx.service
'''取消对服务的启动禁止'''
systemctl unmask nginx.service
'''重新读取所有服务项,修改或添加服务单元文件之后需要执行'''
systemctl daemon-reload
'''查看系统启动耗时'''
systemd-analyze
'''查看各项服务启动耗时'''
systemd-analyze blame | grep .service
参考文档:http://www.jinbuguo.com/systemd/systemd.index.html