systemd 是现代 Linux 发行版中默认的初始化系统(init system)和服务管理器。相比传统的 SysVinit,它拥有并发启动、依赖关系解析、服务自恢复等强大特性,因此成为了企业级部署和开发者首选的服务管理框架。

本文将由浅入深,从基本概念讲起,逐步深入到实际配置、调试与优化,帮助你全面掌握 systemd 服务配置技巧。

一、什么是 systemd?

systemd 是一套系统与服务管理工具,主要负责:

  • 系统引导流程(init)
  • 守护进程管理(daemon)
  • 日志收集(journal)
  • 挂载点和自动挂载(mount/automount)
  • 定时任务(timer)
  • 网络管理(通过 networkd

在实际项目中,我们主要用 systemd 来配置和管理后台服务,也就是我们常说的 .service 单元。

二、systemd Unit 文件结构解析

每一个 systemd 管理的服务,都有一个对应的 Unit 文件,后缀为 .service,位于 /etc/systemd/system//lib/systemd/system/

一个典型的服务 unit 文件结构如下:

[Unit]
Description=My Custom Service
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/python3 app.py
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

一个完整的 systemd unit 文件 可能包含如下结构段(根据 unit 类型而定):

段名适用类型说明
[Unit]所有类型描述 unit 的元信息与依赖
[Service].service服务定义本体
[Socket].socket套接字激活设置
[Timer].timer定时任务调度
[Install]所有类型定义该 unit 如何安装到目标(target)中
[Path].path文件或目录监控触发器
[Mount].mount文件系统挂载定义
[Swap].swapSwap 分区管理
[Slice].sliceCGroup 切片资源限制
[Scope].scope对临时进程组的描述(不常手写)

三、常见配置选项详解

PS. 如何查找所有可用字段与含义?使用 man 命令是最权威的方式:

man systemd.unit
man systemd.service
man systemd.timer
man systemd.exec
man systemd.directives

其中 systemd.directives 是所有可用字段的索引总表,你可以通过关键词查找字段适用于哪些 unit 类型。

[Unit] 段配置项大全

配置项说明
Description=对该 unit 的描述,用于 systemctl status 显示
Documentation=指定帮助文档链接
Requires=启动该服务时,必须同时启动的依赖
Wants=弱依赖,如果依赖项失败,当前服务仍会继续
Before= / After=启动顺序关系,不代表依赖,只是时间顺序
Conflicts=定义与当前 unit 冲突的其他 unit,不能共存
Condition...=启动前的判断条件,如 ConditionPathExists=ConditionUser=
Assert...=Condition 类似,但失败时会导致硬错误(启动失败)

[Service] 段配置项详解(含可选值)

配置项说明
Type=服务运行类型,有以下选项:
- simple:默认,ExecStart 启动的进程保持前台运行
- forking:后台进程(如传统的守护进程)
- oneshot:执行一次性命令,通常用于初始化脚本
- notify:服务会发送 readiness 信号(需实现 systemd 通信)
- idle:延迟到所有任务处理完再启动
ExecStart=启动命令,必须指定
ExecStop= / ExecReload=停止 / 重载命令(可选)
Restart=自动重启策略:
- no(默认):不重启
- on-success:成功退出时重启
- on-failure:非 0 退出时重启
- always:无论如何都重启
- on-abnormal:信号或 core dump 时重启
RestartSec=重启前延迟秒数
RemainAfterExit=适用于 oneshot 类型,服务执行后是否保持“激活”状态
TimeoutStartSec= / TimeoutStopSec=启动/停止的超时时间
User= / Group=服务运行用户和组
WorkingDirectory=工作目录
Environment= / EnvironmentFile=设置环境变量
StandardOutput= / StandardError=设置日志输出: journal, syslog, null, tty
LimitNOFILE=文件描述符限制
CapabilityBoundingSet=限制服务拥有的 Linux capabilities

[Install] 段配置项(决定 enable 方式)

配置项说明
WantedBy=该 unit 会被自动添加到此 target 的 wants 目录中(enable 时)
RequiredBy=会被添加到其他 unit 的 Requires= 依赖中
Also=指定其他 unit 文件也一起启用
Alias=提供 unit 文件的别名,供 systemctl 调用

[Timer] 配置项

配置项示例值含义
OnCalendar=daily, Mon *-*-* 05:00:00, *-*-01 00:00:00类似 cron 表达式的时间格式
OnBootSec=5min开机后 N 秒启动
OnUnitActiveSec=10min上次激活后多久再次启动
Persistent=true/false是否补偿 missed 事件(如关机期间)

[Socket] 配置项

配置项示例值含义
ListenStream=8080, /run/myapp.sockTCP 或 Unix socket
SocketMode=0660socket 权限
Accept=yes/no是否接受多个连接(多实例)

[Path] 配置项

配置项示例值说明
PathExists=/srv/myapp/config.yaml监控文件是否存在
PathChanged=/srv/myapp/data/监控内容变化
Unit=myapp.service被触发的 unit 名称

[Mount] 配置项

与 /etc/fstab 类似,用于挂载磁盘:

配置项示例值说明
What=/dev/sdb1挂载源设备
Where=/mnt/data挂载目标路径
Type=ext4文件系统类型
Options=defaults,noatime挂载选项

四、创建并启用一个自定义服务

假设你有一个脚本 /srv/myapp/run.sh,希望开机自启并自动重启,我们可以按以下步骤操作:

1. 创建 Unit 文件

sudo nano /etc/systemd/system/myapp.service

写入如下内容:

[Unit]
Description=MyApp Daemon
After=network.target

[Service]
Type=simple
ExecStart=/srv/myapp/run.sh
WorkingDirectory=/srv/myapp
Restart=on-failure
RestartSec=2
User=www-data

[Install]
WantedBy=multi-user.target

2. 重新加载 systemd 配置

sudo systemctl daemon-reexec      # 重载 systemd 本身(极少用)
sudo systemctl daemon-reload      # 重载所有 unit 文件(常用)

3. 启动服务并设置开机启动

sudo systemctl start myapp.service
sudo systemctl enable myapp.service

4. 查看服务状态与日志

sudo systemctl status myapp
sudo journalctl -u myapp -f

五、调试技巧与问题排查

1. 服务失败但没有报错?

  • 检查 ExecStart 路径是否正确,是否有执行权限。
  • 使用 journalctl -xe 获取详细错误信息。

2. 修改 unit 文件后没生效?

别忘了执行:

sudo systemctl daemon-reload

再重启服务:

sudo systemctl restart myapp

3. 服务运行不了但手动命令可以?

可能是缺少环境变量或依赖。使用以下方式添加环境:

Environment="PATH=/usr/local/bin:/usr/bin"

或者写入 .env 文件后使用:

EnvironmentFile=/srv/myapp/.env

六、进阶用法:依赖管理与 Timer 定时任务

1. 服务依赖(Before / After)

如果你希望你的服务在某个服务启动后运行:

[Unit]
After=nginx.service
Requires=nginx.service

After 是启动顺序,Requires 是依赖绑定。

2. 定时任务(Timer)

替代 crontab 的现代方式。示例:

创建 /etc/systemd/system/cleanup.service

[Unit]
Description=Daily Cleanup

[Service]
Type=oneshot
ExecStart=/srv/scripts/cleanup.sh

然后创建对应的 Timer:

[Unit]
Description=Run Cleanup Daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

启用并启动 Timer:

sudo systemctl enable --now cleanup.timer

七、最佳实践总结

操作建议
文件存放路径建议放在 /etc/systemd/system/,便于管理与持久化
用户权限尽量使用非 root 用户运行服务
重启策略推荐使用 on-failurealways,结合 RestartSec
日志收集使用 journalctl 替代传统 log 文件,方便统一查看
自动 reload修改后不要忘记 daemon-reload

八、参考命令速查表

# 启动 / 停止 / 重启
sudo systemctl start xxx.service
sudo systemctl stop xxx.service
sudo systemctl restart xxx.service

# 查看服务状态
sudo systemctl status xxx.service

# 设置服务开机启动 / 禁用
sudo systemctl enable xxx.service
sudo systemctl disable xxx.service

# 查看实时日志
sudo journalctl -u xxx.service -f

# 列出所有服务
systemctl list-units --type=service

总结

无论是部署 Web 应用、运行 Python 脚本、还是控制后台服务进程,systemd 都能提供稳定而强大的守护能力。掌握它,不仅可以简化服务器管理工作流,还能显著提升系统可靠性。

在容器化、微服务流行的今天,systemd 仍然在边缘节点、物理机、IoT、传统云服务器中占据一席之地。它不是过时的工具,而是基础设施工程师的必备技能。


也可以看看