linux中服务创建【systemd service】
适用:使用 systemd 的 Linux 发行版(CentOS/RHEL 7+、Ubuntu 16.04+、Debian 9+、Fedora、Arch、openSUSE 等)
一个可用的服务主要需要:服务单元文件 + 启动命令 + 运行身份/环境 + 依赖与自启配置 + 重载启用步骤。
- systemd 服务单元文件
路径通常:/etc/systemd/system/服务名.service
最小可用示例:
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
Restart=on-failure
[Install]
WantedBy=multi-user.target
1.2 各段配置说明
[Unit]:服务描述和依赖关系
[Unit]
Description=服务描述
After=network.target
Requires=network.target
Wants=network-online.target
| 指令 | 含义 |
|---|---|
Description |
服务说明 |
After |
在哪些服务之后启动 |
Requires |
强依赖,依赖失败则本服务失败 |
Wants |
弱依赖,依赖失败不影响本服务 |
Before |
在哪些服务之前启动 |
[Service]:核心运行配置
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp --config /etc/myapp.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=ENV=production
EnvironmentFile=-/etc/sysconfig/myapp
| 指令 | 说明 |
|---|---|
Type |
服务类型,见第二部分 |
ExecStart |
启动命令,必须是绝对路径 |
ExecReload |
重载命令 |
ExecStop |
停止命令(一般不用写,systemd 默认发 SIGTERM) |
Restart |
重启策略,如 on-failure、always、no |
RestartSec |
重启间隔秒数 |
User / Group |
以哪个用户/组运行,建议不要用 root |
WorkingDirectory |
工作目录 |
Environment |
环境变量 |
EnvironmentFile |
从文件读取环境变量,- 表示文件不存在也不报错 |
StandardOutput / StandardError |
日志输出方式,通常交给 journald |
LimitNOFILE |
文件描述符限制 |
[Install]:开机自启配置
[Install]
WantedBy=multi-user.target
表示 systemctl enable 后挂到多用户运行级别,实现开机自启。
1.3 创建和启用服务的步骤
# 1. 创建单元文件
sudo vim /etc/systemd/system/myapp.service
# 2. 重新加载 systemd 配置
sudo systemctl daemon-reload
# 3. 启用并立即启动
sudo systemctl enable --now myapp.service
# 4. 查看状态
sudo systemctl status myapp.service
# 5. 查看日志
journalctl -u myapp.service -f
常用命令:
systemctl start myapp
systemctl stop myapp
systemctl restart myapp
systemctl reload myapp
systemctl enable myapp
systemctl disable myapp
systemctl status myapp
systemctl is-enabled myapp
systemctl is-active myapp
journalctl -u myapp
journalctl -u myapp -f
1.4 脚本作为服务时的注意点
如果 ExecStart 指向脚本:
ExecStart=/opt/myapp/start.sh
需要:
chmod +x /opt/myapp/start.sh
脚本首行要有 shebang:
#!/bin/bash
并且脚本最好 前台运行、不要自己后台化,由 systemd 管理进程。
1.5 可选但推荐的安全 / 运维配置
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
LimitNOFILE=65535
日志一般不用自己写日志文件,直接用:
journalctl -u myapp.service
二、Type 类型每一个值的含义
在 [Service] 段的 Type= 决定 systemd 什么时候认为服务“启动完成”,以及主进程如何工作。
| Type 值 | 含义 | systemd 何时认为启动完成 | 典型场景 / 注意 |
|---|---|---|---|
simple |
默认值之一。ExecStart= 启动的进程就是主进程 |
主进程被 fork 出来后,systemd 就认为服务已启动,不等待 exec 成功,也不等待业务就绪 | 普通前台常驻程序。程序如果启动后立刻崩溃,可能先显示 started 再失败 |
exec |
类似 simple,但会等待 execve() 成功 |
ExecStart= 指定的程序成功 exec 后才认为启动完成 |
推荐用于普通前台程序;可避免“可执行文件不存在/无权限”时误判为已启动 |
forking |
传统 daemon 模式:ExecStart= 进程会 fork 子进程,然后父进程退出 |
父进程退出后,systemd 认为启动完成;子进程成为主进程 | 老式会自己后台化的守护进程。常配合 PIDFile=;不推荐新程序使用 |
oneshot |
一次性任务:进程执行完就退出 | systemd 等待主进程退出后才认为启动完成 | 初始化脚本、挂载、备份、一次性任务。常配合 RemainAfterExit=yes;Restart=always 不适用 |
dbus |
服务通过 D-Bus 注册名称 | 服务成功在 D-Bus 上获得 BusName= 指定的名字后认为启动完成 |
需要 D-Bus 的服务;必须配置 BusName= |
notify |
服务通过 sd_notify(READY=1) 主动通知 systemd “我准备好了” |
收到 READY=1 后才认为启动完成 |
启动较慢、需要明确就绪信号的服务。常配合 NotifyAccess=,如 main / all |
notify-reload |
类似 notify,但支持通过 SIGHUP 触发重载,并等待服务重新通知就绪 |
启动时等 READY=1;重载时 systemd 发 SIGHUP,并等待服务重新 READY=1 |
较新 systemd 支持;适合支持“重载完成后通知”的服务 |
idle |
类似 simple,但会延迟启动,直到其他任务都派发完,最多延迟约 5 秒 |
延迟结束后按 simple 方式认为启动完成 |
主要用于避免启动日志/控制台输出交错;不是“空闲时才启动”的意思 |
2.1 simple vs exec
Type=simple
ExecStart=/usr/local/bin/myapp
- 主进程 fork 出来就认为启动成功
- 可执行文件路径错、权限错,也可能先显示 started 再失败
Type=exec
ExecStart=/usr/local/bin/myapp
- 等
execve()成功后才认为启动成功 - 普通前台程序更推荐
exec
2.2 forking:传统后台守护进程
Type=forking
ExecStart=/usr/sbin/mydaemon
PIDFile=/run/mydaemon.pid
含义:
ExecStart启动的进程会 fork 子进程- 父进程退出
- 子进程继续运行
- systemd 在父进程退出后认为服务启动完成
新程序一般不要自己 daemonize,推荐 Type=simple 或 Type=exec 前台运行。
2.3 oneshot:一次性任务
Type=oneshot
ExecStart=/usr/local/bin/init-data.sh
RemainAfterExit=yes
- systemd 等脚本执行完才认为启动完成
RemainAfterExit=yes表示脚本退出后 unit 仍保持 active- 不加
RemainAfterExit,脚本成功退出后服务会变成 inactive/dead
2.4 notify:需要程序主动通知
程序里需要类似:
sd_notify(0, "READY=1");
service 配置:
Type=notify
ExecStart=/usr/local/bin/myapp
NotifyAccess=main
如果程序不发 READY=1,systemd 会一直等到 TimeoutStartSec 超时。
2.5 dbus:需要 D-Bus 名称
Type=dbus
BusName=com.example.MyApp
ExecStart=/usr/local/bin/myapp
systemd 等服务在 D-Bus 上拿到 com.example.MyApp 这个名字后,才认为启动完成。
2.6 idle:不是“空闲才启动”
Type=idle
ExecStart=/usr/local/bin/myapp
它只是把启动稍微延后,避免和其他服务的控制台输出混在一起,最多等约 5 秒。
2.7 默认值
- 写了
ExecStart=但没写Type=:默认通常是simple - 没写
ExecStart=的情况:默认可能是oneshot - 实际建议:
- 普通常驻服务明确写
Type=exec或Type=simple - 一次性任务写
Type=oneshot - 需要就绪通知写
Type=notify
- 普通常驻服务明确写
2.8 选择建议
# 普通前台常驻程序
Type=exec
ExecStart=/usr/local/bin/myapp
# 老式自己后台化的 daemon
Type=forking
ExecStart=/usr/sbin/mydaemon
PIDFile=/run/mydaemon.pid
# 一次性初始化/备份任务
Type=oneshot
ExecStart=/usr/local/bin/task.sh
RemainAfterExit=yes
# 需要程序主动通知就绪
Type=notify
ExecStart=/usr/local/bin/myapp
NotifyAccess=main
最常用的是:exec、simple、oneshot、forking、notify。
三、systemd 单元文件路径全面列表
systemd 会从多个目录搜索单元文件。同名单元文件在多个路径存在时,优先级高的覆盖优先级低的。
3.1 系统级单元路径(按优先级从高到低)
| 优先级 | 路径 | 说明 |
|---|---|---|
| 1 | /etc/systemd/system.control/ |
通过 systemctl 持久化的运行时控制配置,较新 systemd |
| 2 | /run/systemd/system.control/ |
运行时控制配置,重启丢失 |
| 3 | /run/systemd/transient/ |
瞬态单元,systemd-run 创建,重启丢失 |
| 4 | /run/systemd/generator.early/ |
早期生成器输出 |
| 5 | /etc/systemd/system/ |
管理员自定义单元,最常用 |
| 5 | /etc/systemd/system.attached/ |
portable service 附加单元,属于 /etc/systemd/system/ 体系 |
| 6 | /run/systemd/system/ |
运行时单元,重启丢失 |
| 6 | /run/systemd/system.attached/ |
运行时 portable service 附加单元 |
| 7 | /run/systemd/generator/ |
生成器输出 |
| 8 | /usr/local/lib/systemd/system/ |
本地管理员安装的单元 |
| 9 | /usr/lib/systemd/system/ |
发行版软件包安装的单元 |
| 10 | /run/systemd/generator.late/ |
晚期生成器输出 |
补充说明:
/lib/systemd/system/是旧路径,在现代 usrmerge 系统中通常是/usr/lib/systemd/system/的符号链接。/usr/local/lib/systemd/system/用于本地编译安装的软件。/etc/systemd/system/是管理员放置自定义服务的首选目录。
3.2 .wants / .requires 依赖目录
systemctl enable 会在目标目录下创建符号链接,形成依赖关系。
系统级常见位置:
/etc/systemd/system/*.wants/
/etc/systemd/system/*.requires/
/etc/systemd/system/*.upholds/
/run/systemd/system/*.wants/
/run/systemd/system/*.requires/
/usr/local/lib/systemd/system/*.wants/
/usr/lib/systemd/system/*.wants/
示例:
/etc/systemd/system/multi-user.target.wants/myapp.service
浙公网安备 33010602011771号