管理服务单元
systemd 的 service 单元文件定义了如何启动、停止和管理一个后台服务。掌握单元文件的结构和常用配置项,你就可以为任何应用程序创建可靠的系统服务。
单元文件的存放位置
Section titled “单元文件的存放位置”systemd 按优先级从高到低搜索以下目录中的单元文件:
| 路径 | 用途 | 优先级 |
|---|---|---|
/etc/systemd/system/ | 管理员自定义的单元文件 | 最高 |
/run/systemd/system/ | 运行时动态生成的单元文件 | 中 |
/usr/lib/systemd/system/ | 软件包安装的单元文件 | 最低 |
自定义服务应始终放在 /etc/systemd/system/ 目录中。
单元文件结构
Section titled “单元文件结构”一个标准的 service 单元文件由三个主要段落(Section)组成:
[Unit]# 描述和依赖关系
[Service]# 服务运行参数
[Install]# 安装(启用)行为[Unit] 段
Section titled “[Unit] 段”[Unit] 段提供单元的描述信息和依赖关系,是所有单元类型通用的配置段。
[Unit]Description=我的应用服务Documentation=https://example.com/docsAfter=network.targetWants=network-online.targetRequires=postgresql.service常用指令说明:
| 指令 | 说明 |
|---|---|
Description= | 服务的简短描述,会显示在 systemctl status 输出中 |
Documentation= | 文档 URL,供参考使用 |
After= | 指定启动顺序,本服务在指定单元之后启动 |
Before= | 指定启动顺序,本服务在指定单元之前启动 |
Requires= | 强依赖,指定单元未启动则本服务也无法启动 |
Wants= | 弱依赖,指定单元未启动不影响本服务 |
BindsTo= | 类似 Requires,但指定单元停止时本服务也会被停止 |
[Service] 段
Section titled “[Service] 段”[Service] 段是 service 单元文件的核心,定义服务的启动方式、运行参数和重启策略。
[Service]Type=simpleExecStart=/usr/bin/myapp --config /etc/myapp/config.ymlExecReload=/bin/kill -HUP $MAINPIDExecStop=/bin/kill -SIGTERM $MAINPIDRestart=on-failureRestartSec=5User=myappGroup=myappWorkingDirectory=/opt/myappEnvironment=NODE_ENV=productionEnvironmentFile=/etc/myapp/envType= 服务类型
Section titled “Type= 服务类型”Type= 指定服务的启动方式,直接影响 systemd 如何判断服务是否启动成功:
| 类型 | 说明 | 适用场景 |
|---|---|---|
simple(默认) | ExecStart 进程即为主进程 | 大多数前台运行的应用 |
forking | 进程 fork 后父进程退出,子进程成为主进程 | 传统守护进程(如 Apache) |
oneshot | 进程执行完毕即退出,可配合 RemainAfterExit | 初始化脚本、一次性任务 |
notify | 服务通过 sd_notify() 主动通知 systemd 已就绪 | 支持 sd_notify 的应用 |
dbus | 服务获取 D-Bus 名称后视为启动完成 | D-Bus 服务 |
idle | 类似 simple,但等到其他任务完成后再启动 | 控制台输出相关服务 |
Restart= 重启策略
Section titled “Restart= 重启策略”| 值 | 说明 |
|---|---|
no(默认) | 不自动重启 |
on-success | 仅在正常退出(退出码 0)时重启 |
on-failure | 仅在非正常退出时重启(推荐用于守护进程) |
on-abnormal | 在信号终止或超时时重启 |
on-abort | 在收到未捕获的信号时重启 |
always | 无论何种原因退出都重启 |
配合 RestartSec= 设置重启前的等待秒数,避免服务故障时频繁重启。
ExecStart= 启动命令
Section titled “ExecStart= 启动命令”ExecStart= 定义服务启动时执行的命令。注意必须使用绝对路径:
ExecStart=/usr/bin/python3 /opt/myapp/server.py对于 Type=oneshot 的服务,可以指定多个 ExecStart= 行,它们会按顺序依次执行。
[Install] 段
Section titled “[Install] 段”[Install] 段定义了执行 systemctl enable 时的行为:
[Install]WantedBy=multi-user.targetAlias=myapp.service| 指令 | 说明 |
|---|---|
WantedBy= | 指定在哪个 target 下启用本服务(最常用 multi-user.target) |
RequiredBy= | 类似 WantedBy,但建立强依赖关系 |
Alias= | 为服务创建别名 |
创建自定义服务
Section titled “创建自定义服务”下面以一个 Go 语言编写的 Web 应用为例,演示如何创建完整的 systemd 服务。
第一步:编写单元文件
Section titled “第一步:编写单元文件”sudo vim /etc/systemd/system/mywebapp.service写入以下内容:
[Unit]Description=My Go Web ApplicationDocumentation=https://example.com/mywebappAfter=network.targetWants=network-online.target
[Service]Type=simpleUser=webadminGroup=webadminWorkingDirectory=/opt/mywebappExecStart=/opt/mywebapp/mywebappExecReload=/bin/kill -HUP $MAINPIDRestart=on-failureRestartSec=10StandardOutput=journalStandardError=journalSyslogIdentifier=mywebapp
# 安全加固NoNewPrivileges=trueProtectSystem=fullProtectHome=truePrivateTmp=true
# 资源限制LimitNOFILE=65536MemoryMax=512M
[Install]WantedBy=multi-user.target第二步:重新加载 systemd 配置
Section titled “第二步:重新加载 systemd 配置”每次新增或修改单元文件后,必须通知 systemd 重新加载配置:
sudo systemctl daemon-reload第三步:启动并启用服务
Section titled “第三步:启动并启用服务”sudo systemctl enable --now mywebapp.service第四步:验证服务状态
Section titled “第四步:验证服务状态”sudo systemctl status mywebapp.servicesudo journalctl -u mywebapp.service -f覆盖已有服务配置
Section titled “覆盖已有服务配置”如果需要修改软件包安装的服务配置,不应直接编辑 /usr/lib/systemd/system/ 下的文件(系统更新时可能被覆盖)。应使用 drop-in 覆盖机制:
sudo systemctl edit nginx.service这会打开编辑器,创建文件 /etc/systemd/system/nginx.service.d/override.conf。例如增加内存限制:
[Service]MemoryMax=1G保存退出后自动执行 daemon-reload。
如果需要查看完整的合并后配置(原始配置 + 覆盖配置):
systemctl cat nginx.service一次性任务服务示例
Section titled “一次性任务服务示例”有些任务只需执行一次,例如系统初始化脚本:
[Unit]Description=Initialize Application DatabaseAfter=postgresql.serviceRequires=postgresql.service
[Service]Type=oneshotExecStart=/opt/myapp/init-db.shRemainAfterExit=yesUser=postgres
[Install]WantedBy=multi-user.targetRemainAfterExit=yes 表示进程退出后,服务状态仍显示为 active,避免依赖它的其他服务受到影响。
排查服务问题
Section titled “排查服务问题”当服务无法正常启动时,按以下步骤排查:
sudo systemctl status mywebapp.service -lsudo journalctl -u mywebapp.service --no-pager -n 50sudo systemd-analyze verify /etc/systemd/system/mywebapp.servicesystemctl show mywebapp.service