Linux · 4 分钟阅读
systemd 服务文件
现代 Linux 服务的"身份证"。三个 section:
[Unit][Service][Install]。改完一定要daemon-reload。
文件位置
| 路径 | 用途 |
|---|---|
/etc/systemd/system/<name>.service |
自定义(推荐) |
/etc/systemd/system/<name>.service.d/*.conf |
覆盖配置 |
/usr/lib/systemd/system/<name>.service |
软件包自带 |
自己的服务放
/etc/systemd/system,升级软件包不会被覆盖。
[Unit]
| 选项 | 含义 |
|---|---|
Description |
描述 |
Documentation |
文档 URL |
After |
列出依赖的目标 / 服务(顺序) |
Before |
在谁之前启动 |
Requires |
强依赖(失败导致本服务停止) |
Wants |
弱依赖(失败不影响) |
Conflicts |
互斥 |
ConditionPathExists |
路径存在才启动 |
[Service]
Type
| 类型 | 行为 |
|---|---|
simple |
默认,ExecStart 起的进程是主进程 |
exec |
同 simple,但确认 ExecStart 真正 fork 完 |
forking |
ExecStart 调 fork 后退出;适合传统 daemon |
oneshot |
一次性任务,配合 RemainAfterExit=yes |
notify |
启动完用 sd_notify 通知 |
idle |
等其他任务跑完再启动 |
进程控制
| 选项 | 含义 |
|---|---|
User / Group |
运行用户 / 组 |
WorkingDirectory |
工作目录 |
RootDirectory |
chroot 目录 |
Nice |
优先级 |
OOMScoreAdjust |
OOM 权重(-1000 ~ 1000) |
LimitNOFILE |
文件描述符上限 |
Environment |
环境变量(也可引 EnvironmentFile) |
启动逻辑
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/app
EnvironmentFile=/etc/default/app
ExecStartPre=/usr/local/bin/pre-check.sh
ExecStart=/usr/bin/app
ExecStartPost=/usr/local/bin/post-start.sh
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/usr/local/bin/stop.sh
TimeoutStopSec=30
Restart=always
RestartSec=5
ExecStart之前用+ - @标记:
+进程获特权-失败不计入全局失败@参数作整体:走环境变量
进程保护
PrivateTmp=true # 独立 /tmp
ProtectSystem=strict # 整个 FS 只读
ProtectHome=true # /home 不可见
NoNewPrivileges=true # 不能 setuid
ReadOnlyPaths=/etc # 指定路径只读
这些是 systemd 推荐的"沙箱"选项。Docker 也吸收了这一套。
[Install]
| 选项 | 含义 |
|---|---|
WantedBy |
enable 时创建 .wants/ 软链(常用 multi-user.target) |
RequiredBy |
强依赖 |
Alias |
启用别名 |
Also |
一起 enable 的服务 |
DefaultInstance |
模板实例默认名 |
完整示例
# /etc/systemd/system/app.service
[Unit]
Description=My App
After=network.target postgresql.service
Requires=postgresql.service
Documentation=https://example.com/docs
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/app
EnvironmentFile=/etc/default/app
ExecStartPre=/opt/app/bin/migrate.sh
ExecStart=/opt/app/bin/server
ExecStop=/bin/kill -TERM $MAINPID
Restart=always
RestartSec=5
TimeoutStopSec=30
# 沙箱
PrivateTmp=true
ProtectSystem=strict
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now app
一些经验
Type选错是常见坑:fork 完退出得用forking,前台程序用simpleRestart=always别乱用,要确保"主动 stop"时不会复活ExecStart一定要绝对路径- 修改后用
systemd-analyze verify app.service验语法 - 想跑一次而不留痕:
systemd-run --unit=test-app /usr/bin/command