作者:Chancel Yang, 创建:2024-08-30, 字数:8230, 已阅:143, 最后更新:2024-08-30
systemd 是一个 2010 年诞生的系统和服务管理器,广泛用于现代Linux发行版中,负责启动和管理系统的用户空间服务、挂载文件系统、启动并管理设备、以及其他系统初始化任务
作为取代 init 系统的系统项目,systemd 通过单一的控制进程(systemd进程)来管理系统的生命周期,其采用 ini 配置文件来描述和管理服务
现代大部分 Linux 系统都由 systemd 启动和管理
以 sshd 服务为例,控制其启动、关闭、和重启:
sudo systemctl stop sshd
sudo systemctl start sshd
sudo systemctl restart sshd
# 检查 sshd 的服务状态
sudo systemctl status sshd
sshd 服务的开启、关闭、重启都由 /etc/systemd/system/sshd.service
定义,其内容如下:
[Unit]
# 描述该服务的功能
Description=OpenBSD Secure Shell server
# 提供该服务的文档参考,包括手册页
Documentation=man:sshd(8) man:sshd_config(5)
# 指定该服务在网络服务和审计服务启动后启动
After=network.target auditd.service
# 确保在指定路径下不存在文件时,该服务才会运行
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
# 从指定文件加载环境变量,如果文件不存在则忽略
EnvironmentFile=-/etc/default/ssh
# 在服务启动之前检查 sshd 配置文件的有效性
ExecStartPre=/usr/sbin/sshd -t
# 启动 sshd 服务,使用 -D 参数使其在前台运行并使用环境变量中的选项
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
# 当服务重新加载时,首先检查配置文件的有效性
ExecReload=/usr/sbin/sshd -t
# 向主进程发送 HUP 信号以重新加载配置
ExecReload=/bin/kill -HUP $MAINPID
# 设置进程终止模式为仅终止该进程,而不是其子进程
KillMode=process
# 服务失败时自动重启
Restart=on-failure
# 避免在特定情况下(退出状态为255)重启服务
RestartPreventExitStatus=255
# 指定服务类型为 notify,表示服务会通过 sd_notify 通知其状态
Type=notify
# 为 sshd 服务创建运行时目录,以存储临时文件
RuntimeDirectory=sshd
# 设置运行时目录的权限为 0755
RuntimeDirectoryMode=0755
[Install]
# 指定在 multi-user.target 启动时自动启用该服务
WantedBy=multi-user.target
# 为该服务创建一个别名,方便管理
Alias=sshd.service
接下来,将简单快速拆解一下这种 ini 文件的构成
systemd 采用 ini 配置文件来描述和管理服务,其主要构成有三部分: Unit、Service 以及 Install 三大部分
Unit 用于定义程序的基本属性和元数据,常见选项:
Service 用于定义服务的行为(如执行命令、环境变量、运行用户),常见选项:
Install 用于控制服务的启用和禁用策略,常见选项:
在 Unit 部分中,After 和 Before 是较为常用的,都是用于描述启动顺序,即程序在什么情况下启动
这两者的值是一样的,常用值有:
After = 在指定情况后启动
Before = 在指定情况前启动
在 Service 中, Restart 和 Environment 是比较常用的,一个用于控制程序的重启逻辑,一个用于控制程序运行时的环境变量
Restart 常见值:
Environment 用于设置程序的环境变量
Type 用于帮助 systemd 判定服务是否启动成功,常见值:
sd_notidy
通知 systemd,适合与 systemd 进行复杂交互的程序如果环境变量过多,可以使用 EnvironmentFile 选项用于指定一个环境变量的文件路径
常见的 WantedBy 和 RequiredBy 值包括:
RequiredBy 的用法与 WantedBy 类似,但它表示服务是目标的必须依赖项,如果目标被激活,则这个服务也必须被激活RequiredBy 一般用于更严格的依赖关系
这两者与 Unit 中的 Before 和 After 非常相似,但区别很大:
接下来,以配置一个 easydns 程序服务为例
easydns 是一个简易 DNS 分流程序
编辑: /etc/systemd/system/easydns.service
[Unit]
# 服务描述
Description=easydns service
# 启动顺序
After=network.target
[Service]
# 执行命令
ExecStart=/root/easydns/easydns -m=127.0.0.1:1080 -p=114.114.114.114:53 -d=/root/easydns/domain.txt
# 退出机制
Restart=always
# 内存限制
MemoryMax=64M
# 日志输出 %N表示服务名称,所以日志输出位置是 /var/log/easydns.log
StandardOutput=append:/var/log/%N.log
# 重定向错误日志到标准流
StandardError=inherit
# 运行用户
User=root
[Install]
# 启动类型
WantedBy=multi-user.target
让 systemd 读入该配置单并设置开机自启
sudo systemctl daemon-reload
sudo systemctl enable easydns --now
查看 easydns 的运行服务状态
sudo systemctl status easydns
与 crontab 相比,systemd 的定时服务提供了状态监控、日志管理、重启策略控制等功能模块,能适应更复杂的运行需求
例如我们要定时执行域名 DDNS 解析,程序执行方法如下
/root/NameSilo-DDNS/namesilo-ddns --domain=chancel.me --name=demo --type=AAAA --record=$inet6_address --key=123456
创建一个 Service 描述文件,编辑: /etc/systemd/system/namesilo-ddns.service
[Unit]
Description=Register NameSilo dns record
[Service]
Type=oneshot
ExecStart=/root/NameSilo-DDNS/namesilo-ddns --domain=chancel.me --name=demo --type=AAAA --record=$inet6_address --key=123456
StandardOutput=append:/var/log/%N.log
StandardError=inherit
User=root
再创建一个定时器,编辑: /etc/systemd/system/namesilo-ddns.timer
[Unit]
Description=Register a DDNS domain every 30 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=30min
Unit=namesilo-ddns.service
[Install]
WantedBy=timers.target
OnBootSec
设置了程序在系统启动后1分钟运行程序,此后 OnUnitActiveSec
规定了每30分钟执行一次
接下来启动这个定时器即可
sudo systemctl daemon-reload
sudo systemctl enable namesilo-ddns.timer --now
查看所有定时器列表
sudo systemctl list-timers
列表如下
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2024-09-04 15:15:01 CST 29min left Wed 2024-09-04 14:45:01 CST 12s ago namesilo-ddns.timer namesilo-ddns.service
...
Mon 2024-09-09 01:38:12 CST 4 days left Mon 2024-09-02 01:39:50 CST 2 days ago fstrim.timer fstrim.service
9 timers listed.
Pass --all to see loaded but inactive timers, too.
systemd 使用 journald 进行日志管理,你可以通过以下命令查看日志:
# 查看所有日志
journalctl
# 查看特定服务日志
journalctl -u <service_name>
日志切割的话,通常会借助 logrotate 来实现
logrotate 是一个常用的日志管理工具,可以定期轮换、压缩和删除旧日志文件
以配置 easydns 为例
编辑:/etc/logrotate.d/easydns
/var/log/easydns.log {
size 10M # 设置每个日志文件的最大大小为10MB
rotate 5 # 保留5个轮换的日志文件
compress # 压缩旧日志文件
missingok # 如果日志文件丢失,不报错
notifempty # 仅在日志非空时轮换
copytruncate # 保证在日志被写入时也可以实现轮换压缩日志
create 0640 root root # 创建新的日志文件时的权限
}
配置完成后,可以试运行(不会实际执行任何操作)
logrotate -d /etc/logrotate.d/easydns
通常而言, logrotate 在 crontab 或 systemd 中被预先定义为1天执行1次,可以通过检查 systemd 的定时任务来查看:
$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2024-09-02 00:00:00 CST 56min left Sun 2024-09-01 00:00:01 CST 23h ago dpkg-db-backup.timer dpkg-db-backup.service
Mon 2024-09-02 00:00:00 CST 56min left Sun 2024-09-01 00:00:01 CST 23h ago logrotate.timer logrotate.service
Mon 2024-09-02 01:10:26 CST 2h 7min left Sun 2024-09-01 04:48:01 CST 18h ago man-db.timer man-db.service
Mon 2024-09-02 01:39:40 CST 2h 36min left Mon 2024-08-26 01:39:19 CST 6 days ago fstrim.timer fstrim.service
Mon 2024-09-02 06:10:27 CST 7h left Sun 2024-09-01 06:45:01 CST 16h ago apt-daily-upgrade.timer apt-daily-upgrade.service
Mon 2024-09-02 10:53:56 CST 11h left Sun 2024-09-01 10:53:56 CST 12h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2024-09-02 14:55:51 CST 15h left Sun 2024-09-01 21:53:03 CST 1h 9min ago apt-daily.timer apt-daily.service
Sun 2024-09-08 03:10:20 CST 6 days left Sun 2024-09-01 03:11:06 CST 19h ago e2scrub_all.timer e2scrub_all.service
8 timers listed.
Pass --all to see loaded but inactive timers, too.