极简工具箱

免费在线工具 · 本地计算 · 无需上传

写一个 systemd 服务:开机自启、崩溃重启与看日志

写了个脚本想让它常驻、崩了自动拉起、开机就跑——不用装 supervisor,一个 `.service` 文件就够。

广告位(百度联盟)

一、最小可用单元文件

保存为 /etc/systemd/system/myapp.service:

[Unit]
Description=My App
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/main.py
Restart=always
RestartSec=3
User=www-data
Environment=ENV=prod

[Install]
WantedBy=multi-user.target

三段的作用:

二、Type 怎么选

Type适用
simple(默认)前台常驻进程,最常用
forking传统守护进程,自己 fork 到后台
oneshot执行一次就退出(初始化脚本)
notify程序主动 sd_notify 告诉 systemd 已就绪

用错 Type 的典型症状:前台进程写成 forking,systemd 以为它启动了但其实没有;一次性脚本写成 simple,会被反复判定「已退出」。

三、Restart 策略

Restart=always
RestartSec=3
StartLimitIntervalSec=60
StartLimitBurst=5

四、enable ≠ start

这是最常混淆的一对:

systemctl daemon-reload      # 改了 unit 文件必须先执行
systemctl start myapp        # 现在启动
systemctl enable myapp       # 开机自启(不启动)
systemctl enable --now myapp # 两者一起
systemctl status myapp       # 看状态与最近日志
systemctl restart myapp
systemctl stop myapp
systemctl disable myapp

改完 unit 文件忘记 daemon-reload,是「配置明明改了却没生效」的头号原因。

五、看日志

journalctl -u myapp -n 100          # 最近 100 行
journalctl -u myapp -f              # 实时跟踪(类似 tail -f)
journalctl -u myapp --since "10 min ago"
journalctl -u myapp -p err          # 只看错误级别

journalctl 的日志默认存在内存/磁盘,长期保留要配置 Storage=persistent 或改用文件日志。程序自己写日志文件也没问题,两者不冲突。

六、启动失败怎么查

  1. systemctl status myapp 看 Active: 和最后几行输出。
  2. journalctl -u myapp -n 50 --no-pager 看完整报错。
  3. 常见原因:
  1. 手动用同样的用户、同样的绝对路径跑一遍 ExecStart,能复现就能定位。

七、几个实用配置

# 环境变量文件
EnvironmentFile=/etc/myapp/env

# 进程挂掉时同时杀掉子进程
KillMode=control-group

# 标准输出重定向到文件(也可交给 journal)
StandardOutput=append:/var/log/myapp.log
StandardError=append:/var/log/myapp.err

# 限制资源
MemoryMax=512M
CPUQuota=80%

EnvironmentFile 的格式是 KEY=value,不能有 export,注释用 #。

小结

三段结构 + ExecStart 绝对路径 + Type=simple + Restart=on-failure;改配置先 daemon-reload;enable 是开机自启、start 是现在运行;排查先看 status 和 journalctl。

本文相关工具(本地计算,无需上传)

更多使用教程