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 <端口号>