systemd可以用来做什么
systemd可以在后台以daemon的形式运行程序,并可以控制和监视进程。当进程被意外终止,可以自动拉起进程;当机器重启后,可以自动启动进程。从而可以保证后台服务的稳定性,不用在写一堆crontab来拉起进程。
如何使用systemd
使用systemd通过以下步骤来实现:
- 编写xxx.service文件,并放置于/etc/systemd/system目录下;
- 执行systemctl daemon-reload命令,让systemd重新加载service;(每次service文件由更改,必须执行该命令)
- 如果需要开机自动启动服务,则需要执行命令systemctl enable xxx;(xxx为service的名字) 这样,一个通过systemd监管的服务就完成了。
如何编写service文件
使用systemd最重要的就是编写service文件了,service文件主要分为Unit、Service和Install三部分,各部分介绍如下:
[Unit]控制单元描述
unit块描述了service与其他服务之间的依赖,以及启动顺序等service之间的关系。Unit包含以下字段:
- Description:服务单元的描述;
- Requires:这个单元启动了,那么它“需要”的单元也会被启动; 它“需要”的单元被停止了,它自己也活不了。但是请注意,这个设定并不能控制某单元与它“需要”的单元的启动顺序(启动顺序是另外控制的),即 Systemd 不是先启动 Requires 再启动本单元,而是在本单元被激活时,并行启动两者。于是会产生争分夺秒的问题,如果 Requires 先启动成功,那么皆大欢喜; 如果 Requires 启动得慢,那本单元就会失败(Systemd 没有自动重试)。所以为了系统的健壮性,不建议使用这个标记,而建议使用 Wants 标记。
- RequiresOverridable:跟 Requires 很像。但是如果这条服务是由用户手动启动的,那么 RequiresOverridable 后面的服务即使启动不成功也不报错。跟 Requires 比增加了一定容错性,但是你要确定你的服务是有等待功能的。另外,如果不由用户手动启动而是随系统开机启动,那么依然会有 Requires 面临的问题。
- Requisite:强势版本的 Requires。要是这里需要的服务启动不成功,那本单元文件不管能不能检测等不能等待都立刻就会失败。
- Wants:推荐使用。本单元启动了,它“想要”的单元也会被启动。但是启动不成功,对本单元没有影响。
- Conflicts:一个单元的启动会停止与它“冲突”的单元,反之亦然。注意这里和后面的启动顺序是“正交”的。
- OnFailure:OnFailure:很明显,如果本单元失败了,那么启动什么单元作为折衷。
- Before/After:要是一个服务 Before 另一个服务,那么在并行启动时(Systemd 总是用进程 0 并行启动所有东西,然后通过这两个标记来二次等待排序),那另一个服务这时就会等这个服务先启动并返回状态,注意是先启动而不是启动成功,因为失败也是一种状态,一定要成功才启动另一个服务是通过依赖关系定义的。反之 After 亦然。
[Service]服务本体定义
Service块用来定义服务的启动\停止\重启命令等等。各字段的含义如下:
-
Type:定义service的类型,主要有以下类型:
- simple:默认类型,启动的程序就是主体程序,这个程序要是退出那么一切皆休。因为 simple 类型不存在主进程退出的情况也就不存在有返回状态的情况,所以它一旦启动就认为是成功的,除非没起来。
- forking:标准 Unix Daemon 使用的启动方式。启动程序后会调用 fork() 函数,把必要的通信频道都设置好之后父进程退出,留下守护精灵的子进程。如果启动的进程能够自己创建pidfile,最好也指定下PIDFile=XXX。
- oneshot: 一次性服务,这种服务类型就是启动,完成,没进程了。因为这类服务运行完就没进程了,我们经常会需要 RemainAfterExit=yes。后面配置的意思是说,即使没进程了,我们也要 Systemd 认为该服务是存在并成功了的。其他类型千万不要去设置RemainAfterExit=yes,否则systemd会认为服务启动成功了,重启或再去启动都会失败。
- dbus:这个程序启动时需要获取一块 DBus 空间,所以需要和 BusName= 一起用。只有它成功获得了 DBus 空间,依赖它的程序才会被启动。
- notify: 这个程序在启动完成后会通过 sd_notify 发送一个通知消息。所以还需要配合 NotifyAccess 来让 Systemd 接收消息,后者有三个级别:none,所有消息都忽略掉; main,只接受我们程序的主进程发过去的消息; all,我们程序的所有进程发过去的消息都算。NotifyAccess 要是不写的话默认是 main。
- idle: 这个程序要等它里面调度的全部其它东西都跑完才会跑它自己。比如你 ExecStart 的是个 shell 脚本,里面可能跑了一些别的东西,如果不这样的话,那很可能别的东西的控制台输出里会多一个“启动成功”这样的 Systemd 消息。
-
ExecStart:定义systemctl start所要执行的命令;
-
ExecStartPre:启动当前服务之前执行的命令;
-
ExecStartPost:启动当前服务之后执行的命令
-
ExecReload:定义systemctl restart所要执行的命令;
-
ExecStop:定义systemctl stop所要执行的命令;
-
ExecStopPost:停止当其服务之后执行的命令;
-
Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括 always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
-
RestartSec:自动重启当前服务间隔的秒数;
[install]安装服务
WantedBy=multi-user.target 在 multi-user.target 启用时,我们的服务也就会被启用了。