跳转到内容

管理服务单元

systemd 的 service 单元文件定义了如何启动、停止和管理一个后台服务。掌握单元文件的结构和常用配置项,你就可以为任何应用程序创建可靠的系统服务。

systemd 按优先级从高到低搜索以下目录中的单元文件:

路径用途优先级
/etc/systemd/system/管理员自定义的单元文件最高
/run/systemd/system/运行时动态生成的单元文件
/usr/lib/systemd/system/软件包安装的单元文件最低

自定义服务应始终放在 /etc/systemd/system/ 目录中。

一个标准的 service 单元文件由三个主要段落(Section)组成:

单元文件基本结构
[Unit]
# 描述和依赖关系
[Service]
# 服务运行参数
[Install]
# 安装(启用)行为

[Unit] 段提供单元的描述信息和依赖关系,是所有单元类型通用的配置段。

[Unit] 段常用配置项
[Unit]
Description=我的应用服务
Documentation=https://example.com/docs
After=network.target
Wants=network-online.target
Requires=postgresql.service

常用指令说明:

指令说明
Description=服务的简短描述,会显示在 systemctl status 输出中
Documentation=文档 URL,供参考使用
After=指定启动顺序,本服务在指定单元之后启动
Before=指定启动顺序,本服务在指定单元之前启动
Requires=强依赖,指定单元未启动则本服务也无法启动
Wants=弱依赖,指定单元未启动不影响本服务
BindsTo=类似 Requires,但指定单元停止时本服务也会被停止

[Service] 段是 service 单元文件的核心,定义服务的启动方式、运行参数和重启策略。

[Service] 段常用配置项
[Service]
Type=simple
ExecStart=/usr/bin/myapp --config /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=5
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
EnvironmentFile=/etc/myapp/env

Type= 指定服务的启动方式,直接影响 systemd 如何判断服务是否启动成功:

类型说明适用场景
simple(默认)ExecStart 进程即为主进程大多数前台运行的应用
forking进程 fork 后父进程退出,子进程成为主进程传统守护进程(如 Apache)
oneshot进程执行完毕即退出,可配合 RemainAfterExit初始化脚本、一次性任务
notify服务通过 sd_notify() 主动通知 systemd 已就绪支持 sd_notify 的应用
dbus服务获取 D-Bus 名称后视为启动完成D-Bus 服务
idle类似 simple,但等到其他任务完成后再启动控制台输出相关服务
说明
no(默认)不自动重启
on-success仅在正常退出(退出码 0)时重启
on-failure仅在非正常退出时重启(推荐用于守护进程)
on-abnormal在信号终止或超时时重启
on-abort在收到未捕获的信号时重启
always无论何种原因退出都重启

配合 RestartSec= 设置重启前的等待秒数,避免服务故障时频繁重启。

ExecStart= 定义服务启动时执行的命令。注意必须使用绝对路径:

正确写法 - 使用绝对路径
ExecStart=/usr/bin/python3 /opt/myapp/server.py

对于 Type=oneshot 的服务,可以指定多个 ExecStart= 行,它们会按顺序依次执行。

[Install] 段定义了执行 systemctl enable 时的行为:

[Install] 段常用配置项
[Install]
WantedBy=multi-user.target
Alias=myapp.service
指令说明
WantedBy=指定在哪个 target 下启用本服务(最常用 multi-user.target
RequiredBy=类似 WantedBy,但建立强依赖关系
Alias=为服务创建别名

下面以一个 Go 语言编写的 Web 应用为例,演示如何创建完整的 systemd 服务。

创建自定义服务单元文件
sudo vim /etc/systemd/system/mywebapp.service

写入以下内容:

/etc/systemd/system/mywebapp.service
[Unit]
Description=My Go Web Application
Documentation=https://example.com/mywebapp
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=webadmin
Group=webadmin
WorkingDirectory=/opt/mywebapp
ExecStart=/opt/mywebapp/mywebapp
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mywebapp
# 安全加固
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
# 资源限制
LimitNOFILE=65536
MemoryMax=512M
[Install]
WantedBy=multi-user.target

每次新增或修改单元文件后,必须通知 systemd 重新加载配置:

重新加载 systemd 守护进程配置
sudo systemctl daemon-reload
启用开机自启并立即启动服务
sudo systemctl enable --now mywebapp.service
查看服务运行状态
sudo systemctl status mywebapp.service
查看服务日志
sudo journalctl -u mywebapp.service -f

如果需要修改软件包安装的服务配置,不应直接编辑 /usr/lib/systemd/system/ 下的文件(系统更新时可能被覆盖)。应使用 drop-in 覆盖机制:

使用 systemctl edit 创建覆盖配置
sudo systemctl edit nginx.service

这会打开编辑器,创建文件 /etc/systemd/system/nginx.service.d/override.conf。例如增加内存限制:

override.conf 示例 - 添加内存限制
[Service]
MemoryMax=1G

保存退出后自动执行 daemon-reload

如果需要查看完整的合并后配置(原始配置 + 覆盖配置):

查看合并后的完整单元配置
systemctl cat nginx.service

有些任务只需执行一次,例如系统初始化脚本:

/etc/systemd/system/init-database.service
[Unit]
Description=Initialize Application Database
After=postgresql.service
Requires=postgresql.service
[Service]
Type=oneshot
ExecStart=/opt/myapp/init-db.sh
RemainAfterExit=yes
User=postgres
[Install]
WantedBy=multi-user.target

RemainAfterExit=yes 表示进程退出后,服务状态仍显示为 active,避免依赖它的其他服务受到影响。

当服务无法正常启动时,按以下步骤排查:

查看服务详细状态和最近日志
sudo systemctl status mywebapp.service -l
查看服务完整日志(不截断长行)
sudo journalctl -u mywebapp.service --no-pager -n 50
验证单元文件语法是否正确
sudo systemd-analyze verify /etc/systemd/system/mywebapp.service
查看服务的所有属性
systemctl show mywebapp.service