随机
Enter 搜索 ↑↓ 切换 Esc 清空

systemd

命令

systemd 服务单元与定时器配置指南

systemd

systemd 服务单元与定时器配置指南

补充说明

systemd 是 Linux 系统的基础服务管理器,取代传统的 SysV init。它通过 单元文件(Unit File) 管理服务、挂载点、套接字、定时器等系统资源。

通用说明

  • 单元文件路径优先级:/etc/systemd/system/ > /run/systemd/system/ > /usr/lib/systemd/system/
  • 用户自定义单元应放在 /etc/systemd/system/
  • 修改单元文件后需执行 systemctl daemon-reload

一、单元文件结构

Service 单元示例

以自定义 Nginx 服务为例,创建 /etc/systemd/system/myapp.service

[Unit]
Description=My Custom Application
Documentation=https://example.com/docs
After=network.target network-online.target
Wants=network-online.target
Requires=mysql.service

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yml
ExecStartPre=/usr/bin/test -f /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
Environment="NODE_ENV=production"

[Install]
WantedBy=multi-user.target

关键字段说明

字段 说明
[Unit] 单元元数据
Description 描述信息
After= 在哪些单元之后启动(不影响依赖)
Requires= 强依赖(依赖失败本单元也失败)
Wants= 弱依赖(依赖失败不影响本单元)
BindsTo= 强绑定(依赖停止本单元也停止)
PartOf= 部分依赖
[Service] 服务特有配置
Type=simple 默认值,ExecStart 启动后即认为已启动
Type=exec 类似 simple,但等待 ExecStart 完成执行
Type=forking 进程 fork 后父进程退出(传统 daemon)
Type=oneshot 一次性任务,执行后即退出
Type=notify 进程通过 sd_notify() 通知已就绪
Type=dbus 等待 D-Bus 名称注册
Restart= 重启策略:no / on-success / on-failure / on-abnormal / on-watchdog / always
RestartSec= 重启间隔时间
ExecStartPre= 启动前执行的命令
ExecStart= 主启动命令
ExecReload= 重载命令
ExecStop= 停止命令
ExecStartPost= 启动后执行的命令
[Install] 安装信息
WantedBy= 被哪个 target 启用(systemctl enable 时创建软链接)
RequiredBy= 类似 WantedBy,但创建 Requires 依赖

二、Service 类型详解

1. oneshot — 一次性任务

适合开机初始化脚本:

[Unit]
Description=Initialize Application Data

[Service]
Type=oneshot
ExecStart=/usr/local/bin/init-data.sh
RemainAfterExit=yes    # 标记为已启动(即使退出)

[Install]
WantedBy=multi-user.target

2. forking — 传统守护进程

适合传统 daemon 程序:

[Unit]
Description=Some Traditional Daemon

[Service]
Type=forking
ExecStart=/usr/sbin/somed -c /etc/somed.conf
PIDFile=/run/somed.pid

[Install]
WantedBy=multi-user.target

3. notify — 通知式启动

进程启动后通过 sd_notify(0, "READY=1") 通知 systemd:

[Unit]
Description=Application with Notify

[Service]
Type=notify
ExecStart=/usr/local/bin/app
WatchdogSec=30    # 看门狗超时(进程需定期通知)

[Install]
WantedBy=multi-user.target

4. exec — 执行式

simple 类似,但确保 ExecStart= 已执行完毕才认为已启动:

[Service]
Type=exec
ExecStart=/usr/local/bin/server

三、定时器(Timer)单元

systemd 定时器替代传统的 cron 定时任务,支持精确到秒、日历事件、依赖服务。

基本示例

/etc/systemd/system/backup.service

[Unit]
Description=Daily Backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

/etc/systemd/system/backup.timer

[Unit]
Description=Run backup daily at 3:00 AM
Requires=backup.service

[Timer]
OnCalendar=daily
OnCalendar=*-*-* 03:00:00
Persistent=true    # 错过执行时间后立即补执行
RandomizedDelaySec=300

[Install]
WantedBy=timers.target

启用并启动定时器:

systemctl daemon-reload
systemctl enable backup.timer
systemctl start backup.timer

定时器时间表达式

表达式 含义
OnCalendar=daily 每天凌晨 0 点
OnCalendar=hourly 每小时
OnCalendar=*-*-* 03:00:00 每天凌晨 3 点
OnCalendar=Mon..Fri 09:00:00 工作日早 9 点
OnCalendar=*-*-1..7 00:00:00 每月前 7 天
OnCalendar=*-01-01 00:00:00 每年元旦
OnUnitActiveSec=1h 上次激活后 1 小时
OnBootSec=5min 开机后 5 分钟

管理定时器

# 查看所有定时器
systemctl list-timers --all

# 查看详细状态
systemctl status backup.timer

# 手动触发(不等待调度)
systemctl start backup.service

# 查看下次执行时间
systemctl list-timers backup.timer

四、Path 单元(文件监控)

监控文件变化触发服务:

/etc/systemd/system/watchfile.path

[Unit]
Description=Watch config file

[Path]
PathModified=/etc/myapp/config.yml
Unit=myapp-reload.service

[Install]
WantedBy=multi-user.target

/etc/systemd/system/myapp-reload.service

[Unit]
Description=Reload myapp config

[Service]
Type=oneshot
ExecStart=/bin/systemctl reload myapp

五、资源限制与隔离

1. CPU / 内存限制

[Service]
# CPU 限制
CPUQuota=50%               # 最多使用 50% CPU
CPUShares=512              # CPU 权重(默认 1024)
CPUAccounting=true

# 内存限制
MemoryMax=512M             # 最大内存
MemoryHigh=384M            # 内存上限(软限制)
MemorySwapMax=0            # 禁止使用 Swap
MemoryAccounting=true

# IO 限制
IOWeight=100               # IO 权重
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M

# 任务数限制
TasksMax=100               # 最大进程/线程数

2. 安全隔离

[Service]
# 文件系统
ProtectSystem=full         # 禁止写 /usr /etc(仅 /var /tmp 可写)
ProtectHome=true           # 禁止访问 /home /root
ReadWritePaths=/var/lib/myapp   # 额外可写路径

# 网络
PrivateNetwork=true        # 禁止网络访问

# 设备
PrivateDevices=true        # 禁止访问物理设备

# 内核
CapabilityBoundingSet=CAP_NET_BIND_SERVICE   # 限制能力
NoNewPrivileges=true       # 禁止提权

# 命名空间
PrivateTmp=true            # 隔离 /tmp

六、常用 systemctl 管理

# 重新加载单元文件
systemctl daemon-reload

# 启用/禁用开机自启
systemctl enable myapp.service
systemctl disable myapp.service

# 启动/停止/重启
systemctl start myapp.service
systemctl stop myapp.service
systemctl restart myapp.service

# 重载配置(不中断)
systemctl reload myapp.service

# 查看状态
systemctl status myapp.service

# 查看日志
journalctl -u myapp.service -n 50 -f

# 屏蔽(禁止启动)
systemctl mask myapp.service

# 取消屏蔽
systemctl unmask myapp.service

# 列出所有单元
systemctl list-units --type=service --all

# 查看依赖树
systemctl list-dependencies myapp.service

七、用户级 systemd

无需 root 权限,管理用户级服务:

# 用户单元目录
mkdir -p ~/.config/systemd/user/

~/.config/systemd/user/mytunnel.service

[Unit]
Description=SSH Tunnel

[Service]
ExecStart=/usr/bin/ssh -N -L 8080:localhost:8080 user@server

[Install]
WantedBy=default.target

启用:

# 用户级操作不加 sudo
systemctl --user daemon-reload
systemctl --user enable mytunnel.service
systemctl --user start mytunnel.service

# 查看用户级服务
systemctl --user list-units

# 启用用户服务开机自启(需启用 linger)
loginctl enable-linger $USER

八、综合实战:部署 Web 应用

创建目录和脚本

mkdir -p /opt/webapp

/opt/webapp/start.sh

#!/bin/bash
cd /opt/webapp
exec node server.js

创建系统用户:

useradd -M -s /sbin/nologin webapp
chown -R webapp:webapp /opt/webapp

服务单元

/etc/systemd/system/webapp.service

[Unit]
Description=Web Application
After=network.target mysql.service
Requires=mysql.service

[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
ExecStart=/opt/webapp/start.sh
Restart=always
RestartSec=10
LimitNOFILE=65536
MemoryMax=512M
ProtectSystem=full
PrivateTmp=true

[Install]
WantedBy=multi-user.target

健康检查定时器

/etc/systemd/system/webapp-healthcheck.service

[Unit]
Description=Web Application Health Check

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -sf http://localhost:3000/health || /bin/systemctl restart webapp

/etc/systemd/system/webapp-healthcheck.timer

[Unit]
Description=Health check every 5 minutes

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target

启用

systemctl daemon-reload
systemctl enable --now webapp.service
systemctl enable --now webapp-healthcheck.timer

常见问题

服务启动失败

# 查看完整错误日志
journalctl -u myapp.service -xe --no-pager

# 查看内核日志
dmesg | tail -20

权限不足

# 检查文件权限
ls -la /usr/local/bin/myapp

# 检查 SELinux
getenforce
ausearch -m avc -ts recent

单元文件语法检查

# systemd 自带检查
systemd-analyze verify /etc/systemd/system/myapp.service

# 查看完整的单元配置
systemctl cat myapp.service

端口被占用

systemctl status myapp.service   # 查看是否端口冲突
ss -tlnp | grep <端口号>

参考链接