使用 systemd 来设置守护进程
目录
我们一般使用 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
- 如果缺少日期部分,那么默认为当天
- 还可以使用
yesterday
、today
、tomorrow
,使用它们时,对应的时间为00:00:00
。 now
表示当前时间
以下,有悟列出几个命令做为演示例子,其它靠大家举一反三。
- 查询从昨天开始的所有日志,
journalctl -u hugo -S yesterday
- 查询直到今日之前为上的所有日志,
journalctl -u hugo -U today
,注意,并不包含今天 - 聪明的同学已经想到,结合
--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
,类似于时间 <=