跳到主要内容
  1. Skills/
  2. 系统运维、优化/

使用 systemd 来设置守护进程

··字数 2577·6 分钟
有悟方法 hugo

我们一般使用 linux 来做web服务器的操作系统,当你的程序是自制的并没有带 system service时,可以使用 systemd 来帮助设置后台守护进程 daemon。这样程序将做为服务注册到 systemd中,由它来管理启动,可实现开机自动启动。

service配置文件 #

service配置文件统一位于 /etc/systemd/system/下,如本站有部分页面是准实时更新,根据最新的数据文件自动生成静态页面。这样就要求后台需要运行一个 hugo 进程并监控文件目录变化。

文件变化监控是 Hugo 自带功能,也不是这本文的重点。

/etc/systemd/system 目录下,使用有 读写权限 的用户(root 用户或 sudo)创建一个 service 文件,如 hugo.service

[Unit]
Description=hugo serve 随便你
Documentation=https://gohugo.io/
After=network.target nss-lookup.target

[Service]
User=ubuntu
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
WorkingDirectory=/home/public/youwu.today/site
ExecStart=/home/ubuntu/bin/hugo --config config.toml --environment production -w 
Restart=on-failure
RestartPreventExitStatus=23

[Install]
WantedBy=multi-user.target
重要参数说明:
After
指定在什么系统服务启动后启动
User
使用什么用户来启动程序
WorkingDirectory
程序启动时的工作目录,相当于 cd WorkingDirectory
ExecStart
程序启动命令
CapabilityBoundingSet、AmbientCapabilities
由于安全的原因,使用低段位的端口需要管理员权限或者授权。这样,启动的非管理员进程就可以监听低段的端口,如 80 端口。

上面这个 service 例子非常简单,有可能满足不了你的实际需求,那么参阅 systemd 的用户使用手册。

service 的管理 #

几个常用的 service 命令与使用场景

使用 systemctl 来管理 service ,这个命令的运行,需要使用到 root 用户或者 sudo

  • 当新添加到 xxx.service/etc/systemd/system 目录后,使用 systemctl enable xxx.service 使用服务生效

  • 当修改了 xxx.service 文件,需要执行命令 systemctl daemon-reload 使修改生效

  • 手动重启服务使用命令 systemctl restart xxx.service

  • 手动启动与停止服务的命令分别为 systemctl start xxx.service 或者 systemctl stop xxx.service

  • 查看服务状态(因为 start/stop 不会打印程序在标准终端上的输出), 使用命令 systemctl status xxx.service。当然如果只是想看进程是否已经启动,使用 ps -ef | grep 程序名称查询即可。

service的运行日志 #

在 systemd 中运行的命令或者程序,它在 STDOUT 标准终端的输出被 systemd 接管,也就是说,通过在命令打印的日志,通通被 systemd 收集起来。

如何查看这些日志呢?这要用到另外一个工具,journalctl,它是与 systemd 配套的日志管理工具,无须再另行安装,包含日志查询、日志清理等功能。

journalctl 命令的详细操作说明,见 官方文档 journalctl — Query the systemd journal

可以通过 journalctl -h 或者 journalctl --help 查看它的命令选项,

ubuntu@VM-12-6-ubuntu:~$ journalctl --help
journalctl [OPTIONS...] [MATCHES...]

Query the journal.

Options:
     --system                Show the system journal
     --user                  Show the user journal for the c>
  -M --machine=CONTAINER     Operate on local container
  -S --since=DATE            Show entries not older than the>
  -U --until=DATE            Show entries not newer than the>
  -c --cursor=CURSOR         Show entries starting at the sp>
...

其中有比较多的参数,有悟将介绍几个常用的。

比如在 systemd 中有一个叫 hugo.service 的服务,我们可以通过 systemctl status hugo 来查看状态,还可以看到最新的终端打印日志,但显示的日志行数非常少。这时可以使用 journalctl -u hugo 来查询.

  • systemctl 查询服务状态
ubuntu@VM-12-6-ubuntu:~$ systemctl status hugo
● hugo.service - hugo serve
     Loaded: loaded (/etc/systemd/system/hugo.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2021-07-05 12:42:19 CST; 1 months 7 days ago
       Docs: https://gohugo.io/
   Main PID: 2213457 (hugo)
      Tasks: 10 (limit: 4474)
     Memory: 89.2M
     CGroup: /system.slice/hugo.service
             └─2213457 /home/ubuntu/bin/hugo --config config.toml,config.crawling.toml --environment production -w

Aug 12 21:01:36 VM-12-6-ubuntu hugo[2213457]: Data changed "/home/public/youwu/site/data/hub.yml": WRITE
Aug 12 21:01:36 VM-12-6-ubuntu hugo[2213457]: Total in 30 ms
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Change of Static files detected, rebuilding site.
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: 2021-08-12 21:02:00.343 +0800
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Syncing data/jd/jingfen.41.json to /home/public/youwu/public-hot/
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Syncing data/tb/optimus.28026.json to /home/public/youwu/public-hot/
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Change detected, rebuilding site.
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: 2021-08-12 21:02:00.344 +0800
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Data changed "/home/public/youwu/site/data/hub.yml": WRITE
Aug 12 21:02:00 VM-12-6-ubuntu hugo[2213457]: Total in 29 ms
ubuntu@VM-12-6-ubuntu:~$

如果直接使用 journalctl -u service名称 查看日志,可以明确告诉你,查询到的结果不是你想要的。因为它是从日志库中查询这个 service 有记录以来的日志,只能反映日志从什么时候开始被收集起来的。

所以,我们需要另外加上一些过滤选项,缩小这个日志查询结果。

tail -f 一样跟踪日志 #

journalctl -u service名称 -f-f 表示 follow,跟随的意思,在命令行尝试,你会发现它打印的日志与 systemctl status 的最新 status 是一致的。直到使用 ctrl+c 中断之前,journalctl 会不断的向终端输出对应程序当前打印的最新日志。

ubuntu@VM-12-6-ubuntu:~$ journalctl -u hugo.service -f
-- Logs begin at Tue 2021-05-18 01:05:32 CST. --
Aug 12 21:11:34 VM-12-6-ubuntu hugo[2213457]: Data changed "/home/public/youwu/site/data/hub.yml": WRITE
Aug 12 21:11:34 VM-12-6-ubuntu hugo[2213457]: Total in 31 ms
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Change of Static files detected, rebuilding site.
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: 2021-08-12 21:12:00.342 +0800
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Syncing data/tb/optimus.3764.json to /home/public/youwu/public-hot/
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Syncing data/zhihu_hot.json to /home/public/youwu/public-hot/
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Change detected, rebuilding site.
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: 2021-08-12 21:12:00.344 +0800
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Data changed "/home/public/youwu/site/data/hub.yml": WRITE
Aug 12 21:12:00 VM-12-6-ubuntu hugo[2213457]: Total in 30 ms

查询某个时间或者时间段的日志 #

如果你知道程序出问题的大概时间,那么可以通过 journalctl 的时间选项来定位日志,看看报错信息。

journalctl -u service名称 --since 时间,查询指定时间开始的所有日志 journalctl -u service名称 --until 时间,查询截止指定时间的所有日志

--since 可以使用缩写为 -S,注意是大写的S;--until 可以缩写为 -U,也是大写的。它们的时间参数遵守以下的规则:

  • yyyy-mm-dd hi:mm:ss, 如 2021-08-12 21:21:00
  • 如果仅有日期部分 yyyy-mm-dd,那么时间缺省默认值为 00:00:00
  • 如果缺少秒 :ss,那么默认为 :00
  • 如果缺少日期部分,那么默认为当天
  • 还可以使用 yesterdaytodaytomorrow ,使用它们时,对应的时间为 00:00:00
  • now 表示当前时间

以下,有悟列出几个命令做为演示例子,其它靠大家举一反三。

  1. 查询从昨天开始的所有日志,journalctl -u hugo -S yesterday
  2. 查询直到今日之前为上的所有日志,journalctl -u hugo -U today,注意,并不包含今天
  3. 聪明的同学已经想到,结合 --since(自从) 和 --until(直到)来限定时间段。journalctl -u hugo -S yesterday -U today,查询昨天一天的所有日志。

以上的示例命令,有悟都测试过,可运行。你所需要的,是把上面的 hugo 改为想查询的服务名,把 yesterday、today 等改为更具体的 yyyy-mm-dd hi:mm:ss 格式,以更精准定位问题。

日志的打印顺序 #

上面几小节中列举的 journalctl -u 日志查询命令,日志信息都是按照时间先后顺序列出的。可以使用 -r 参数,让日志倒序排序,有时这样可能更符合使用需要。有点像静止版的-f

日志清理 #

既然有日志查询,自然就有日志清理。因这些日志是被保存在操作系统上的,占用磁盘空间的。 使用 journalctl --disk-usage 来查询日志信息占用的空间。

ubuntu@VM-12-6-ubuntu:~$ journalctl --disk-usage
Archived and active journals take up 3.9G in the file system.
ubuntu@VM-12-6-ubuntu:~$

上面命令所查询的日志空间,对应于 linux 操作系统的 /var/log/journal 路径。

可以使用 --vacuum-time--vacuum-size来控制所保存的日志文件大小。

journalctl --vacuum-size=100M,告诉 journald 把日志文件控制中在 100m 以内。 journalctl --vacuum-time=1d,告诉 journald 日志文件只保留1天的日志。 还有个 --rotate,循环日志,用来控制是否循环利用日志存储空间的。

这一小节所列的关于影响日志存储的命令选项,要谨慎使用。这些参数是系统管理员做系统维护时考虑的,如果你不是系统管理员,也不参与系统空间规划,最好不要去碰它。

journalctl 的总结 #

了解或者熟悉 sql 的同学,可能会发现 journalctl 的查询非常类似于 sql。如果你把它与 sql 查询类比起来学习,可能会更容易掌握。

  • -r,类似于 sql 的 order by 时间 desc
  • -u,类似于 where 服务=
  • --since,类似于 时间 >=
  • --until,类似于 时间 <=