深入了解 Systemd 之 Service 单元

深入了解 Systemd 之 Service 单元

业务中我们经常要开发一些守护进程的服务,尤其是近来 Golang、Rust 等编译型语言的崛起。但他们都没有提供如 C 语言一样的进程管理,默认情况下,编译后的文件只能在前台运行。所以今天就了解一下如何通过 Systemd 管理这些守护进程。

Unit 部分

Unit 部分用于定义单元的启动顺序与依赖关系。

Description

单元名称简介,该选项尽量简短且易于理解。

示例

1
Description=Nginx Web Server

Documentation

单元的文档URL地址列表,多个URL用空格隔开。支持 https://file:info:man: 类型的URL;

示例

1
Documentation=http://nginx.org/en/docs file:/usr/local/nginx/docs

Requires

定义单元的强依赖,指定当前单元依赖的其他单元列表,以空格分割多个单元。如果这个单元被激活,这里列出的单元也将被激活。如果设置了 After 并且其中有一个单元无法激活(涉及依赖关系,与启动顺序无关,默认他们是并行启动的)。

Wants

定义单元的弱依赖,与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败(涉及依赖关系,与启动顺序无关,默认他们是并行启动的)。

BindsTo

与Requires类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行。

Before

如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动(只涉及启动顺序,不涉及依赖关系)。

After

如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动(只涉及启动顺序,不涉及依赖关系)。

Conflicts

这里指定的 Unit 不能与当前 Unit 同时运行。

Condition…

当前 Unit 运行必须满足的条件,否则不会运行。

Assert…

当前 Unit 运行必须满足的条件,否则会报启动失败。

  • Requisite:
  • Wants:指定依赖的服务;并不强依赖,如果没有指定 After 则该单元与依赖单元不会并行启动;
  • BindsTo:
  • After:指定依赖的服务,在指定服务启动后,启动本服务;
  • PartOf:
  • Conflicts:
  • OnFailure:
  • PropagatesReloadTo:
  • ReloadPropagatedFrom:
  • JoinsNamespaceOf:
  • RequiresMountsFor:
  • OnFailureJobMode:
  • IgnoreOnIsolate:
  • StopWhenUnneeded:
  • RefuseManualStart:
  • RefuseManualStop:
  • AllowIsolate:
  • DefaultDependencies:
  • CollectMode:
  • FailureAction:
  • SuccessAction:
  • FailureActionExitStatus:
  • SuccessActionExitStatus:
  • JobTimeoutSec:
  • JobRunningTimeoutSec:
  • JobTimeoutAction:
  • JobTimeoutRebootArgument:
  • StartLimitIntervalSec:
  • StartLimitBurst:
  • StartLimitAction:
  • RebootArgument:

依赖关系通常被用在服务(service)而不是目标(target)上。例如,network.target 一般会被某个配置网络接口的服务引入,所以,将自定义的单元排在该服务之后即可,因为 network.target 已经启动。

Service 部分

该部分定义 Unit 的启动行为。

该部分的选项可以使用 - 连词号,来表示抑制错误,即发生错误的时候,不影响其他命令的执行。

Type

定义 Unit 启动类型。

可选项

  • simple(默认值):Systemd 认为该服务将立即启动。服务进程不会 fork 。如果该服务要启动其他服务,不要使用此类型启动,除非该服务是 socket 激活型。
  • forking:Systemd 认为当该服务进程 fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便 Systemd 能够跟踪服务的主进程。
  • oneshot:这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。
  • notify:与 simple 相同,但约定服务会在就绪后向 Systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。
  • dbus:若以此方式启动,当指定的 BusName 出现在DBus系统总线上时,Systemd 认为服务就绪。
  • idle:Systemd 会等待所有任务处理完成后,才开始执行 idle 类型的单元。其他行为与 Type=simple 类似。

User

Group

DynamicUser

SupplementaryGroups

PAMName

WorkingDirectory

定义运行服务时的工作目录,如果该字段未设置,则判断是否设置 User 字段,如果两个都为设置,则该字段值默认为 / 。如果设置了 User 的值,则该字段为 User 指定用户的 ~ 目录,即 Home 目录。

示例

1
WorkingDirectory=~

作用范围

  • service.service;
  • socket.socket;
  • mount.mount;
  • swap.swap。

LimitCPU

LimitFSIZE

LimitDATA

LimitSTACK

LimitCORE

LimitRSS

LimitNOFILE

LimitAS

LimitNPROC

LimitMEMLOCK

LimitLOCKS

LimitSIGPENDING

LimitMSGQUEUE

LimitNICE

LimitRTPRIO

LimitRTTIME

LimitCPU

LimitCPU

LimitCPU

LimitCPU

LimitCPU

LimitCPU

LimitCPU

LimitCPU

BindPaths

RemainAfterExit

GuessMainPID

PIDFile

BusName

ExecStart

启动当前服务的命令,可以结合 EnvironmentFile 配置项,使用 $OPTIONS 获取配置文件的 KEY/VALUE。

例子
1
/usr/sbin/sshd -D $OPTIONS

ExecStartPre

启动当前服务之前执行的命令

ExecStartPost

启动当前服务之后执行的命令

ExecReload

重启当前服务时执行的命令

ExecStop

停止当前服务时执行的命令

ExecStopPost

停止当其服务之后执行的命令

RestartSec

自动重启当前服务间隔的秒数

TimeoutStartSec

TimeoutStopSec

TimeoutSec

定义 Systemd 停止当前服务之前等待的秒数

RuntimeMaxSec

WatchdogSec

Restart

定义何种情况 Systemd 会自动重启当前服务。

可选项

  • no(默认值):退出后不会重启
  • on-success:只有正常退出时(退出状态码为0),才会重启
  • on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启
  • on-abnormal:只有被信号终止和超时,才会重启
  • on-abort:只有在收到没有捕捉到的信号终止时,才会重启
  • on-watchdog:超时退出,才会重启
  • always:不管是什么退出原因,总是重启

Environment

指定环境变量

EnvironmentFile

许多软件都有自己的环境参数文件,该文件可以用 EnvironmentFile 字段读取。该文件内部的 key=value 键值对,可以用 $key 的形式,在当前配置文件中获取。

KillMode

定义 Systemd 如何停止 Unit。

可选项

  • control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
  • process:表示只停止主进程,不停止子进程,这个配置很少用到,但在 SSH 中会用到,如果你需要停止 sshd 服务,但是不希望,当前的 ssh session 断开。
  • mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
  • none:没有进程会被杀掉,只是执行服务的 stop 命令。

SuccessExitStatus

RestartPreventExitStatus

RestartForceExitStatus

RootDirectoryStartOnly

NonBlocking

NotifyAccess

Sockets

FileDescriptorStoreMax

USBFunctionDescriptors

USBFunctionStrings

PermissionsStartOnly

是否以 root 用户权限执行 Pre 和 Post 脚本,如 ExecStartPreExecStartPost

注意:该字段已被弃用,但是未删除。

示例

1
PermissionsStartOnly=true

Install 部分

通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。

WantedBy

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

示例

1
WantedBy=multi-user.target

RequiredBy

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

示例

1
WantedBy=multi-user.target

Alias

当前 Unit 可用于启动的别名。

示例

1
Alias=nginx

Also

当前 Unit 激活(enable)时,会被同时激活的其他 Unit。

示例

1
Also=php-fpm.service

Socket 激活

我们知道,HTTP服务器启动在那里,终究是耗费资源的。那么能不能做到,只有有人访问时,才启动服务,否则就关闭?
这在 Systemd 里面叫做”Socket 激活”。开发者可以指定 Socket 监听的端口,系统根据有没有收到请求,自动启动或关闭服务。不难想到,只要前面加一层负载均衡器,这就等同于实现了一个简单的云服务,即根据访问量,系统自动扩容或收缩。

评论