linux中服务创建【systemd service】

适用:使用 systemd 的 Linux 发行版(CentOS/RHEL 7+、Ubuntu 16.04+、Debian 9+、Fedora、Arch、openSUSE 等)
一个可用的服务主要需要:服务单元文件 + 启动命令 + 运行身份/环境 + 依赖与自启配置 + 重载启用步骤。

  1. 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
posted @ 2026-10-28 15:10  哪能咋办嘛  阅读(7)  评论(0)    收藏  举报