systemd Service 与 Timer 指南
本文整理 systemd service、systemd timer 以及常见相关 unit 的使用方法。
重点目标不是背配置项,而是建立一条清晰的阅读路径:
1 | systemd 管什么 |
1. systemd 和 unit 是什么
systemd 是 Linux 上常见的系统与服务管理器。
它负责管理:
1 | 系统启动流程 |
在 systemd 里,被管理的对象统一叫 unit。
常见 unit 类型:
1 | .service 后台服务或一次性任务 |
一句话:
1 | systemd 用 unit 描述系统里的各种资源和任务,再用依赖关系把它们组织起来。 |
2. 常见 unit 类型速览
| unit 类型 | 作用 | 典型场景 |
|---|---|---|
.service |
描述一个服务或任务怎么启动、停止、重启 | Java 服务、Nginx、脚本任务 |
.timer |
按时间触发另一个 unit | 每天备份、定期清理、健康检查 |
.target |
表示启动阶段或 unit 分组 | multi-user.target、network-online.target |
.socket |
先监听端口,有连接时再拉起服务 | 按需启动、减少常驻进程 |
.mount |
管理文件系统挂载 | 挂载数据盘、NFS、临时目录 |
.path |
监听文件或目录变化,变化时触发服务 | 文件落地后处理、配置变更触发 |
最常用的是:
1 | .service |
其他类型理解即可,遇到特定场景再深入。
3. unit 文件放在哪里
常见位置:
1 | /etc/systemd/system/ 管理员自定义 unit,优先级高 |
实际项目里,自己写的 service 和 timer 一般放:
1 | /etc/systemd/system/ |
例如:
1 | /etc/systemd/system/app-manager.service |
修改或新增 unit 文件后,需要让 systemd 重新加载配置:
1 | sudo systemctl daemon-reload |
4. Target 是什么
target 可以理解成 systemd 的“启动阶段”或“unit 分组”。
它本身通常不启动进程,而是把一组 unit 组织起来。
常见 target:
| target | 含义 |
|---|---|
basic.target |
基础系统能力就绪 |
network.target |
网络管理服务已启动,但不一定真正联网 |
network-online.target |
网络已经尽量达到可用状态 |
multi-user.target |
多用户命令行环境,服务器常用 |
graphical.target |
图形界面环境 |
timers.target |
定时器集合 |
sockets.target |
socket unit 集合 |
服务器上最常见的是:
1 | [Install] |
意思是:
1 | 当系统进入 multi-user.target 这个阶段时,希望这个服务也被拉起来。 |
这就是很多后台服务开机自启会挂到 multi-user.target 的原因。
5. start 和 enable 的区别
这是理解 systemd 很关键的一点。
1 | sudo systemctl start app.service |
意思是:
1 | 现在立刻启动这个服务。 |
但它不会保证下次开机自动启动。
1 | sudo systemctl enable app.service |
意思是:
1 | 把这个服务挂到某个 target 上,让它以后随系统启动。 |
enable 具体挂到哪里,取决于 unit 文件里的 [Install]。
例如:
1 | [Install] |
执行:
1 | sudo systemctl enable app.service |
systemd 大致会创建类似这样的符号链接:
1 | /etc/systemd/system/multi-user.target.wants/app.service |
所以开机进入 multi-user.target 时,systemd 会发现:
1 | multi-user.target 想要 app.service |
于是就会启动它。
6. 为什么没有 [Install] 就不能直接 enable
[Install] 定义的是:
1 | 这个 unit 被 enable 时,应该挂到哪个 target 或哪个 unit 的依赖目录里。 |
如果没有 [Install],systemd 就不知道:
1 | 开机时应该让谁拉起它 |
所以通常不能直接:
1 | sudo systemctl enable xxx.service |
没有 [Install] 的 unit 仍然可以:
1 | sudo systemctl start xxx.service |
也可以被别的 unit 通过依赖关系触发。
典型例子:
1 | backup.service 没有 [Install] |
这表示:
1 | 不要开机直接启动 backup.service |
7. systemctl 常用命令
查看状态:
1 | systemctl status app.service |
启动、停止、重启:
1 | sudo systemctl start app.service |
开机自启:
1 | sudo systemctl enable app.service |
查看日志:
1 | journalctl -u app.service |
重新加载 unit 文件:
1 | sudo systemctl daemon-reload |
查看失败服务:
1 | systemctl --failed |
8. Service 是什么
.service 用来描述一个服务或任务。
它可以管理:
1 | 长期运行的后台进程 |
典型文件:
1 | /etc/systemd/system/my-app.service |
一个最小示例:
1 | [Unit] |
9. Service 文件结构
[Unit]
描述 unit 的元信息和依赖关系。
常见字段:
1 | [Unit] |
常用含义:
| 字段 | 含义 |
|---|---|
Description |
服务说明 |
After |
启动顺序,在某个 unit 之后启动 |
Before |
启动顺序,在某个 unit 之前启动 |
Wants |
弱依赖,希望一起启动 |
Requires |
强依赖,依赖失败会影响当前 unit |
注意:
1 | After 只控制顺序,不代表会自动拉起依赖。 |
所以常见组合是:
1 | After=network-online.target |
[Service]
描述服务如何运行。
常见字段:
1 | [Service] |
常用含义:
| 字段 | 含义 |
|---|---|
Type |
服务启动类型 |
User |
以哪个用户运行 |
Group |
以哪个用户组运行 |
WorkingDirectory |
工作目录 |
ExecStart |
启动命令 |
ExecReload |
重载命令 |
ExecStop |
停止命令 |
Restart |
重启策略 |
RestartSec |
重启间隔 |
Environment |
环境变量 |
EnvironmentFile |
环境变量文件 |
[Install]
定义 enable 时如何挂到启动目标上。
常见写法:
1 | [Install] |
含义:
1 | 执行 systemctl enable 时,把当前 service 挂到 multi-user.target 的 wants 目录里。 |
如果没有 [Install]:
1 | 通常不能直接 systemctl enable |
10. Service Type
Type 决定 systemd 如何判断服务已经启动。
常见类型:
| Type | 场景 | 说明 |
|---|---|---|
simple |
最常用,前台进程 | ExecStart 启动的进程就是主进程 |
forking |
老式后台 daemon | 进程会 fork 到后台 |
oneshot |
一次性任务 | 命令执行完成即结束 |
notify |
支持 sd_notify 的服务 | 服务主动通知 systemd 已就绪 |
idle |
延迟到其他任务后执行 | 较少用 |
Java、Node、Go、Python 后台服务大多用:
1 | Type=simple |
一次性脚本用:
1 | Type=oneshot |
11. Service 模板
后台服务模板
1 | [Unit] |
适合:
1 | Java 后端服务 |
一次性任务模板
1 | [Unit] |
适合:
1 | 备份脚本 |
注意:
1 | 被 timer 触发的 oneshot service 通常不需要 [Install]。 |
12. ExecStart 注意事项
ExecStart 不是普通 shell。
不能直接写复杂 shell 语法:
1 | ExecStart=cd /opt/app && java -jar app.jar |
推荐写法:
1 | WorkingDirectory=/opt/app |
如果确实需要 shell:
1 | ExecStart=/bin/bash -lc 'cd /opt/app && java -jar app.jar' |
建议:
1 | 简单命令直接写绝对路径 |
示例:
1 | ExecStart=/usr/local/sbin/example-watchdog.sh |
13. 环境变量
直接写在 unit 里:
1 | [Service] |
使用环境变量文件:
1 | [Service] |
/etc/example/app.env:
1 | JAVA_OPTS=-Xms512m -Xmx512m |
注意:
1 | 环境变量文件不要放密码明文,除非权限控制非常明确。 |
14. Timer 是什么
.timer 是 systemd 的定时任务 unit。
它一般不直接执行命令,而是触发同名 .service。
例如:
1 | backup.timer |
所以通常会有两个文件:
1 | /etc/systemd/system/backup.service |
一句话:
1 | service 描述做什么,timer 描述什么时候做。 |
15. Timer 文件结构
示例:
1 | [Unit] |
常用字段:
| 字段 | 含义 |
|---|---|
OnCalendar |
按日历时间触发 |
OnBootSec |
开机后多久触发 |
OnUnitActiveSec |
上次触发后多久再次触发 |
OnUnitInactiveSec |
上次任务结束后多久再次触发 |
Persistent |
错过时间后补跑 |
RandomizedDelaySec |
增加随机延迟 |
Unit |
触发哪个 unit |
如果 timer 和 service 同名,可以省略:
1 | Unit=backup.service |
16. Timer 模板
每天定时任务
/etc/systemd/system/backup.service:
1 | [Unit] |
/etc/systemd/system/backup.timer:
1 | [Unit] |
启用:
1 | sudo systemctl daemon-reload |
间隔任务
例如每 5 分钟执行一次:
1 | [Unit] |
含义:
1 | 开机 1 分钟后先执行一次 |
17. 常用 OnCalendar 写法
每天凌晨 3 点:
1 | OnCalendar=*-*-* 03:00:00 |
每小时:
1 | OnCalendar=hourly |
每天:
1 | OnCalendar=daily |
每周:
1 | OnCalendar=weekly |
每月:
1 | OnCalendar=monthly |
周一到周五 9 点:
1 | OnCalendar=Mon..Fri 09:00:00 |
查看解析结果:
1 | systemd-analyze calendar 'Mon..Fri 09:00:00' |
18. Timer 常用命令
查看所有 timer:
1 | systemctl list-timers --all |
查看某个 timer:
1 | systemctl status backup.timer |
启动 timer:
1 | sudo systemctl enable --now backup.timer |
手动执行对应 service:
1 | sudo systemctl start backup.service |
查看日志:
1 | journalctl -u backup.service |
19. Timer 和 Cron 对比
| 对比项 | systemd timer | cron |
|---|---|---|
| 日志 | 统一进入 journal | 依赖 cron/mail/重定向 |
| 依赖 | 支持 unit 依赖 | 较弱 |
| 错过补跑 | Persistent=true |
默认不补 |
| 随机延迟 | 支持 RandomizedDelaySec |
需要自己写 |
| 管理 | systemctl 统一管理 |
crontab 管理 |
| 适合 | 系统任务、服务相关任务 | 简单周期任务 |
建议:
1 | 服务相关、需要日志和依赖管理的任务,用 systemd timer。 |
20. Socket Unit
.socket 用来让 systemd 先监听 socket,有连接进来时再启动对应 .service。
典型用途:
1 | 按需启动服务 |
示例:
1 | [Unit] |
对应 service:
1 | [Unit] |
启用:
1 | sudo systemctl enable --now example.socket |
注意:
1 | socket 激活要求服务程序适配这种启动方式。 |
21. Path Unit
.path 用来监听文件或目录变化,并触发对应 .service。
典型用途:
1 | 文件上传后触发处理 |
示例:
/etc/systemd/system/process-upload.path:
1 | [Unit] |
/etc/systemd/system/process-upload.service:
1 | [Unit] |
启用:
1 | sudo systemctl daemon-reload |
常见字段:
| 字段 | 含义 |
|---|---|
PathExists |
某个路径存在时触发 |
PathExistsGlob |
匹配 glob 时触发 |
PathChanged |
文件内容变化后触发 |
PathModified |
文件被修改时触发 |
DirectoryNotEmpty |
目录非空时触发 |
22. Mount Unit
.mount 用来管理挂载点。
systemd 可以从 /etc/fstab 自动生成 mount unit,也可以手动写 .mount。
注意命名规则:
1 | 挂载点 /data -> data.mount |
示例:
1 | [Unit] |
启用:
1 | sudo systemctl daemon-reload |
实际运维里更常见的是先写 /etc/fstab:
1 | /dev/sdb1 /data ext4 defaults 0 2 |
然后用:
1 | sudo systemctl daemon-reload |
23. Override 覆盖配置
不要直接改系统包自带的 unit 文件。
推荐用 override:
1 | sudo systemctl edit nginx.service |
会生成类似:
1 | /etc/systemd/system/nginx.service.d/override.conf |
示例:
1 | [Service] |
查看最终合并后的配置:
1 | systemctl cat nginx.service |
如果要清空某个列表型字段,需要先置空再重写。
例如重写 ExecStart:
1 | [Service] |
24. 安全建议
不要默认用 root 跑业务服务。
建议:
1 | [Service] |
限制写目录:
1 | ReadWritePaths=/opt/app /var/log/app |
保护系统目录:
1 | ProtectSystem=full |
限制权限提升:
1 | NoNewPrivileges=true |
建议思路:
1 | 服务需要什么权限,就只给什么权限。 |
25. 排查流程
服务起不来时,按这个顺序排查:
1 | systemctl status app.service |
重点看:
1 | ExecStart 路径是否存在 |
修改 unit 后:
1 | sudo systemctl daemon-reload |
timer 不触发时,按这个顺序:
1 | systemctl list-timers --all |
重点看:
1 | timer 是否 enable/start |
26. 常见模板
Java 应用
1 | [Unit] |
Watchdog
1 | [Unit] |
1 | [Unit] |
备份任务
1 | [Unit] |
1 | [Unit] |
27. 速查表
1 | # 重新加载 unit 文件 |
28. 参考
1 | man systemd.service |
一句话总结:
1 | service 管任务怎么运行,timer 管什么时候运行,target 管启动阶段和分组,socket/path/mount 则把端口、文件变化、挂载点也纳入 systemd 的统一管理。 |
