在服务器上守护进程的方法有许许多多,但利用现在大部分系统的第一个进程 Systemd 来守护算得上是一个最优解。
写在前面 链接到标题
本文只专注于利用 Systemd 来守护进程,它的其他对于系统的操作不会被涉及。
本文以 Ubuntu 20.04.4 LTS 为例进行介绍。
Service Unit 链接到标题
配置文件 链接到标题
每个 Unit 都有对应的配置文件,它们分散在三个目录里。
/lib/systemd/system
:系统默认的单元文件/etc/systemd/system
:用户安装的软件的单元文件/usr/lib/systemd/system
:用户自己定义的单元文件
一般的进程服务配置文件后缀为 .service
。
[Unit]
Description=test
Requires=network-online.target
After=network.target
[Service]
Type=simple
ExecStart=/opt/test
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
[Unit]区块:启动顺序与依赖关系 链接到标题
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 运行必须满足的条件,否则会报启动失败
需要注意的是 After 和 Before 字段只涉及启动顺序,不涉及依赖关系;Wants 和 Requires 字段只涉及依赖关系,与启动顺序无关,默认是同时启动的。所以在写一个需要有前置服务的服务时这两者都是需要的。
[Service]区块:启动行为 链接到标题
ExecStart
:启动当前服务的命令ExecStartPre
:启动当前服务之前执行的命令ExecStartPost
:启动当前服务之后执行的命令ExecReload
:重启当前服务时执行的命令ExecStop
:停止当前服务时执行的命令ExecStopPost
:停止当其服务之后执行的命令RestartSec
:自动重启当前服务间隔的秒数Restart
:定义何种情况 Systemd 会自动重启当前服务,可能的值包括always
(总是重启)、on-success
、on-failure
、on-abnormal
、on-abort
、on-watchdog
TimeoutSec
:定义 Systemd 停止当前服务之前等待的秒数Environment
:指定环境变量WorkingDirectory
:指定服务运行目录
所有的启动设置之前,都可以加上一个连词号(-
),表示"抑制错误",即发生错误的时候,不影响其他命令的执行。比如,EnvironmentFile=-/etc/sysconfig/sshd
(注意等号后面的那个连词号),就表示即使/etc/sysconfig/sshd
文件不存在,也不会抛出错误。具体请参考官方文档,中文文档。
启动类型 链接到标题
Type
字段定义启动类型。它可以设置的值如下。
- simple(默认值):
ExecStart
字段启动的进程为主进程 - forking:
ExecStart
字段将以fork()
方式启动,此时父进程将会退出,子进程将成为主进程 - oneshot:类似于
simple
,但只执行一次,Systemd 会等它执行完,才启动其他服务 - dbus:类似于
simple
,但会等待 D-Bus 信号后启动 - notify:类似于
simple
,启动结束后会发出通知信号,然后 Systemd 再启动其他服务 - idle:类似于
simple
,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合。
重启行为 链接到标题
KillMode
字段定义 Systemd 如何停止 sshd 服务。它可以设置的值如下。
- control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
- process:只杀主进程
- mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
- none:没有进程会被杀掉,只是执行服务的 stop 命令。
Restart
字段定义了 sshd 退出后,Systemd 的重启方式。它可以设置的值如下。
- no(默认值):退出后不会重启
- on-success:只有正常退出时(退出状态码为0),才会重启
- on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启
- on-abnormal:只有被信号终止和超时,才会重启
- on-abort:只有在收到没有捕捉到的信号终止时,才会重启
- on-watchdog:超时退出,才会重启
- always:不管是什么退出原因,总是重启
RestartSec
字段表示 Systemd 重启服务之前,需要等待的秒数。
[Install] 区块 链接到标题
WantedBy
:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system
目录下面以 Target 名 +.wants
后缀构成的子目录中RequiredBy
:它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入/etc/systemd/system
目录下面以 Target 名 +.required
后缀构成的子目录中Alias
:当前 Unit 可用于启动的别名Also
:当前 Unit 激活(enable)时,会被同时激活的其他 Unit
Target
的含义是服务组,表示一组服务。WantedBy=multi-user.target
指的是,sshd 所在的 Target 即multi-user.target
。
这个设置非常重要,因为执行systemctl enable sshd.service
命令时,sshd.service
的一个符号链接,就会放在/etc/systemd/system
目录下面的multi-user.target.wants
子目录之中。
Systemd 有默认的启动 Target。
$ systemctl get-default
multi-user.target
上面的结果表示,默认的启动 Target 是multi-user.target
。在这个组里的所有服务,都将开机启动。这就是为什么systemctl enable
命令能设置开机启动的原因。
使用 Target 的时候,systemctl list-dependencies
命令和systemctl isolate
命令也很有用。
# 查看 multi-user.target 包含的所有服务
$ systemctl list-dependencies multi-user.target
# 切换到另一个 target
# shutdown.target 就是关机状态
$ sudo systemctl isolate shutdown.target
一般来说,常用的 Target 有两个:一个是multi-user.target
,表示多用户命令行状态;另一个是graphical.target
,表示图形用户状态,它依赖于multi-user.target
。官方文档有一张非常清晰的 Target 依赖关系图。
常用命令 链接到标题
# 重新加载配置文件
$ sudo systemctl daemon-reload
# 列出正在运行的 Unit
$ systemctl list-units
# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all
# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service
# 显示系统状态
$ systemctl status
# 显示单个 Unit 的状态
$ sysystemctl status bluetooth.service
# 显示某个 Unit 是否正在运行
$ systemctl is-active application.service
# 显示某个 Unit 是否处于启动失败状态
$ systemctl is-failed application.service
# 显示某个 Unit 服务是否建立了启动链接
$ systemctl is-enabled application.service
# 立即启动一个服务
$ sudo systemctl start apache.service
# 立即停止一个服务
$ sudo systemctl stop apache.service
# 重启一个服务
$ sudo systemctl restart apache.service
# 杀死一个服务的所有子进程
$ sudo systemctl kill apache.service
# 开机自动执行该单元
$ systemctl enable [UnitName]
# 关闭开机自动执行
$ systemctl disable [UnitName]
# 重新加载一个服务的配置文件
$ sudo systemctl reload apache.service
# 显示某个 Unit 的所有底层参数
$ systemctl show httpd.service
# 显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service
# 设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500
# 查看配置文件的内容。
$ systemctl cat atd.service
日志管理 链接到标题
Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用journalctl
一个命令,查看所有日志(内核日志和应用日志)。日志的配置文件是/etc/systemd/journald.conf
。
journalctl
功能强大,用法非常多。
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$ sudo journalctl
# 查看指定时间的日志
$ sudo journalctl --since="2012-10-30 18:17:16"
$ sudo journalctl --since "20 min ago"
$ sudo journalctl --since yesterday
$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$ sudo journalctl --since 09:00 --until "1 hour ago"
# 显示尾部的最新10行日志
$ sudo journalctl -n
# 显示尾部指定行数的日志
$ sudo journalctl -n 20
# 查看某个 Unit 的日志
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today
# 实时滚动显示某个 Unit 的最新日志
$ sudo journalctl -u nginx.service -f
# 合并显示多个 Unit 的日志
$ journalctl -u nginx.service -u php-fpm.service --since today
Timer Unit 链接到标题
这是一种用于定时执行某一 Service 的 Unit,它的配置文件也分散保存在三个同样的目录中。
/lib/systemd/system
:系统默认的单元文件/etc/systemd/system
:用户安装的软件的单元文件/usr/lib/systemd/system
:用户自己定义的单元文件
配置文件 链接到标题
Timer 的配置文件和 Service 类似,有类似的 [Unit] 和 [Install] 区块,有区别的是 [Service] 区块被替换成了 [Timer] 区块,且后缀为 .timer
。
[Timer] 区块 链接到标题
OnActiveSec
:定时器生效后,多少时间开始执行任务OnCalendar
:定时器生效后,以什么间隔开始执行任务OnBootSec
:系统启动后,多少时间开始执行任务OnStartupSec
:Systemd 进程启动后,多少时间开始执行任务OnUnitActiveSec
:该单元上次执行后,等多少时间再次执行OnUnitInactiveSec
: 定时器上次关闭后多少时间,再次执行OnCalendar
:基于绝对时间,而不是相对时间执行AccuracySec
:如果因为各种原因,任务必须推迟执行,推迟的最大秒数,默认是60秒Unit
:真正要执行的任务,默认是同名的带有.service
后缀的单元Persistent
:如果设置了该字段,即使定时器到时没有启动,也会自动执行相应的单元WakeSystem
:如果系统休眠,是否自动唤醒系统
OnUnitActiveSec=1h
表示一个小时执行一次任务,该字段只能写时间间隔。
OnCalendar==*-*-* 02:00:00
表示每天凌晨两点执行,OnUnitActiveSec=Mon *-*-* 02:00:00
表示每周一凌晨两点执行,具体请参考官方文档。
常用命令 链接到标题
# 启动该定时器
$ sudo systemctl start mytimer.timer
# 查看该定时器的状态
$ systemctl status mytimer.timer
# 查看所有正在运行的定时器。
$ systemctl list-timers
# 关闭该定时器。
$ sudo systemctl stop myscript.timer
# 下次开机时自动运行该定时器。
$ sudo systemctl enable myscript.timer
# 关闭定时器的开机自启动。
$ sudo systemctl disable myscript.timer