用 systemd 托管长期运行的服务
比 nohup 和额外的进程管理工具更省心的方式,是用系统自带的 systemd。一个 unit 文件、一条 enable,就能拿到开机自启、崩溃重启和统一日志。
最小可用的 unit
把文件放在 /etc/systemd/system/ 下,文件名就是服务名。下面这个例子启动一个监听 3000 端口的应用。
# /etc/systemd/system/notes-api.service
[Unit]
Description=Notes API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=notes
Group=notes
WorkingDirectory=/srv/notes
EnvironmentFile=/etc/notes-api.env
ExecStart=/srv/notes/bin/notes-api --port 3000
Restart=on-failure
RestartSec=3
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target
几个字段怎么选
Type=simple适合前台运行、不主动 fork 的程序,绝大多数现代应用都是这一类。传统守护进程用forking,支持 sd_notify 的用notify。Restart=on-failure只在退出码非零或被信号杀死时重启,配置错误导致反复崩溃时不会无限循环。always更适合必须常驻的网关类服务。EnvironmentFile用来放密钥和连接串,文件权限设为600,不要写进 unit 文件本身。User与Group指定运行身份,永远不要用 root 跑业务进程。
顺手加上沙箱选项
systemd 自带一层轻量隔离,成本很低。注意 ProtectSystem=strict 会把整个文件系统设为只读,需要写入的目录必须用 ReadWritePaths 单独放开。
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/srv/notes/data
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
PrivateDevices=true
加上这些选项后先重启服务,确认日志里没有权限错误,再观察一天。某些需要调用外部命令或读取证书的程序可能要额外放开路径。
启动、开机自启与日志
改完 unit 文件必须 daemon-reload,否则 systemd 用的还是旧内容。这是最容易忘的一步。
sudo systemctl daemon-reload
sudo systemctl enable --now notes-api
sudo systemctl status notes-api
journalctl -u notes-api -f
journalctl -u notes-api -n 100 --no-pager
journalctl -u notes-api --since "1 hour ago"
默认日志放在 /run 下,重启就没了。需要长期保留时,把 /etc/systemd/journald.conf 里的 Storage 改为 persistent,并用 SystemMaxUse 限制占用。
[Journal]
Storage=persistent
SystemMaxUse=500M
用 timer 代替 cron
定时任务也交给 systemd,好处是日志和 systemctl list-timers 统一可见。先写一个一次性的 service,再写触发它的 timer。
# /etc/systemd/system/notes-backup.service
[Unit]
Description=Backup notes data
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notes-backup.sh
# /etc/systemd/system/notes-backup.timer
[Unit]
Description=Daily notes backup
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now notes-backup.timer
systemctl list-timers --all
Persistent=true 会补跑关机期间错过的任务,RandomizedDelaySec 则避免所有机器在同一秒集中执行。
常用命令
| 目的 | 命令 |
|---|---|
| 查看状态 | systemctl status notes-api |
| 重启 | systemctl restart notes-api |
| 停止并禁止自启 | systemctl disable --now notes-api |
| 查看依赖关系 | systemctl list-dependencies notes-api |
| 查看启动耗时 | systemd-analyze blame |
写 unit 文件的时间通常不超过十分钟,但能省掉之后每一次“服务怎么又没了”的排查。值得。