← 返回文章列表

04 // 正文

systemd 服务管理实战:从开机自启到日志排查

如果说 SysVinit 时代是"手动挡"的驾驶体验,那么 systemd 就是给 Linux 服务管理装上的"自动挡"——优雅、统一,偶尔也需要一点磨合。如今几乎所有主流发行版都默认使用 systemd,掌握它是现代运维的必修课。本文将从零开始,带你走一遍实战流程:用 systemctl 管理服务、手写 Unit 文件、借助 journald 排障,最后用 timer 优雅地替代传统 crontab 调度。

systemd 是什么

systemd 是 Linux 的系统和服务管理器,负责开机引导时的进程启动、服务依赖管理、日志收集等。它把服务、挂载点、定时任务、套接字等统统抽象成Unit,用统一的命令和配置来管理。相比老一代 init,systemd 最大的优势在于:

  • 并行启动:按依赖关系并行拉起服务,开机明显更快
  • 统一管理:服务、日志、定时任务一套工具搞定
  • 日志集中:journald 自动收集所有服务的标准输出
  • 状态可查:每个服务都有明确的活动/退出状态

💡 小知识

systemd 的 PID 永远是 1,它是所有进程的"祖先"。可以在命令行运行 ps -p 1 验证一下。

systemctl 基础命令速查

systemctl 是管理 systemd 的核心命令,日常高频操作如下:

# 服务基本操作
systemctl start nginx        # 启动
systemctl stop nginx         # 停止
systemctl restart nginx      # 重启
systemctl reload nginx       # 重载配置(优雅,不中断服务)
systemctl status nginx       # 查看详细状态

# 开机自启
systemctl enable nginx       # 设置开机自启
systemctl disable nginx      # 取消开机自启
systemctl is-enabled nginx   # 查看是否已启用

# 列出服务
systemctl list-units --type=service         # 运行中的服务
systemctl list-unit-files --type=service    # 所有已安装的服务

用一个表格记住常用的状态查看命令:

命令 作用
systemctl is-active nginx 检查服务是否在运行
systemctl is-failed nginx 检查服务是否启动失败
systemctl daemon-reload 重载 Unit 文件(修改配置后必用)
systemctl list-dependencies nginx 查看服务的依赖关系

手写第一个 Unit 文件

服务在 systemd 眼里就是一个以 .service 结尾的 Unit 文件,通常放在 /etc/systemd/system/(自己写的)或 /lib/systemd/system/(发行版自带的)。一个最小的 Unit 长这样:

# /etc/systemd/system/myapp.service
[Unit]
Description=My demo application
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/myapp/app.py
Restart=on-failure
RestartSec=3
User=myapp
WorkingDirectory=/opt/myapp

[Install]
WantedBy=multi-user.target

几个关键字段的说明:

  • After=:声明在什么之后启动(依赖关系)
  • Type=simple:主进程直接前台运行(最常用)
  • ExecStart=:启动时执行的命令,务必写绝对路径
  • Restart=on-failure:异常退出时自动重启
  • User=:以指定用户运行,遵循最小权限原则
  • WantedBy=multi-user.target:配合 enable 实现开机自启
⚠️ 提醒:修改或新增 Unit 文件后,一定要先执行 systemctl daemon-reload,否则 systemd 不会感知你的改动。

实战:托管一个自定义服务

假设我们在 /opt/myapp/ 下有一个 Python 健康检查脚本,想让它常驻运行并开机自启。完整的落地步骤:

# 1. 准备脚本并赋予执行权限
sudo mkdir -p /opt/myapp
sudo tee /opt/myapp/app.py > /dev/null <<'EOF'
import time
while True:
    print("heartbeat ok")
    time.sleep(5)
EOF
sudo chown -R myapp:myapp /opt/myapp
sudo chmod +x /opt/myapp/app.py

# 2. 写入 Unit 文件(内容见上文示例)
sudo vim /etc/systemd/system/myapp.service

# 3. 重载并启动
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# 4. 确认状态
sudo systemctl status myapp

至此,一个带自动重启、开机自启的常驻服务就托管好了。之后每次改完代码,只需 systemctl restart myapp 即可。

开机自启与依赖管理

真实环境里服务之间往往有先后关系,比如"Web 应用必须在数据库起来之后才能启动"。用 After=Requires= / Wants= 来表达:

  • Requires=:硬依赖,依赖失败则本服务也失败
  • Wants=:软依赖,依赖失败不影响本服务
  • After=:只约定顺序,不建立依赖
[Unit]
Description=My web application
After=network.target postgresql.service
Requires=postgresql.service

[Service]
ExecStart=/usr/bin/python3 /opt/myapp/server.py
Restart=on-failure

journald 日志查看

服务日志不再散落在各处文件,systemd 统一由 journalctl 收集和管理。服务里的 printecho 输出会自动进入日志:

# 查看某个服务的全部日志
journalctl -u myapp

# 实时跟踪日志(类似 tail -f)
journalctl -u myapp -f

# 只看最近 1 小时的日志
journalctl -u myapp --since "1 hour ago"

# 按时间范围过滤
journalctl -u myapp --since "2026-08-08 10:00" --until "2026-08-08 11:00"

# 查看本次启动以来的日志
journalctl -b

# 查看错误级别以上的日志
journalctl -p err

💡 提示

如果服务起不来,第一步永远是 journalctl -u 服务名 --since "10 min ago" 看报错,而不是盲目重启——重启只是治标,日志才能让你找到病根。

timer 定时任务

systemd 的 timer 可以优雅地替代 crontab:它同样以 Unit 形式存在,且能利用 journald 记录每次执行情况。一个每天凌晨 3 点备份的定时任务:

# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup job

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 03:00

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target
# 启用定时任务
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

# 查看定时任务状态
systemctl list-timers

OnCalendar= 支持多种写法:Mon..Fri 09:00:00*-*-1,15 00:00:00 等;Persistent=true 表示错过执行时间(如关机)后开机补跑一次,这正是 crontab 缺失的能力。

常见故障排查

遇到服务异常,按下面的顺序排查通常不会扑空:

  1. systemctl status 服务名 看整体状态和最近报错
  2. journalctl -u 服务名 -n 50 看最近的日志输出
  3. systemctl is-failed 服务名 确认是否启动失败
  4. 确认 Unit 文件语法:systemd-analyze verify /etc/systemd/system/xxx.service
  5. 确认命令路径存在:ExecStart 必须用绝对路径,which 核实
  6. 确认权限:服务指定了 User= 时,该用户要能访问相应文件和目录
⚠️ 高发坑位:很多"起不来"的根源是 ExecStart 里的路径写错、脚本没有执行权限、或者改了 Unit 忘了 daemon-reload。按上面顺序排查,90% 的问题都能在 5 分钟内定位。

最佳实践建议

  • 最小权限:服务务必指定 User=,别用 root 裸跑
  • 配置即文档:Unit 文件的 Description 写清楚,别人(和三个月后的你)才能看懂
  • 善用 Restart:业务进程设 Restart=on-failure + RestartSec=3,扛住偶发抖动
  • 日志要留存:用 journalctl --disk-usage 观察日志体积,必要时设置 SystemMaxUse 限制
  • 先测试再上线:生产环境改动前,先在测试机 daemon-reload + start + status 验证一遍
systemd 初学时会觉得它"又多又重",但一旦理解 Unit 这套统一抽象,你会发现服务管理、日志、定时任务都能用同一套思维轻松搞定。记住:任何工具的服务化,本质都是写好一个 Unit、讲清楚依赖、再交给 systemd 去守护。

systemctl 到 Unit 文件,从 journald 到 timer,这套体系足以覆盖日常绝大多数服务管理需求。下一步可以试试为你的 Nginx、Node 应用甚至一条 Python 脚本各写一个 Unit,亲手体验"托管"的乐趣。

相关资源