7.6 RHCSA 备考:systemd 服务管理

预计阅读时间:13 分钟

📖 目录

RHEL 9 使用 systemd 作为 init 系统。RHCSA 考试要求考生能创建和管理 systemd 服务单元、配置定时任务、分析启动性能。systemd 服务管理是 RHCSA 的核心考点之一,通常以「创建自定义服务」或「配置定时任务」的形式出现。

systemd:Linux 系统的初始化系统和服务管理器,取代了传统的 SysV init。它以并行方式启动服务,显著加快系统启动速度。unit:systemd 管理的对象(服务、定时器、挂载点等),每个 unit 有一个对应的 .service/.timer/.mount 文件。journalctl:systemd 的日志查询工具,支持按服务、时间、优先级等条件筛选日志。target:systemd 的运行级别概念(如 multi-user.target 对应传统的 runlevel 3),用于控制系统启动到哪个状态。

学习目标

  • 熟练使用 systemctl 管理服务的生命周期(启动/停止/启用/屏蔽等)
  • 掌握 journalctl 日志分析方法,能够根据日志排查服务问题
  • 能够编写自定义 systemd service unit 文件,配置服务的启动参数和依赖
  • 理解 systemd timer 的配置方法,能够替代 cron 实现定时任务

前置知识

考试要点

考点权重典型题目
systemctl 服务管理25%启动/停止/启用/禁用指定服务
自定义 unit 文件30%创建一个 systemd 服务单元文件
systemd timer20%配置定时执行的备份任务
journalctl 日志15%查看服务日志、筛选错误级别
target 管理10%切换默认运行级别

1. systemctl 基本操作

# 服务生命周期
sudo systemctl start nginx        # 启动
sudo systemctl stop nginx         # 停止
sudo systemctl restart nginx      # 重启
sudo systemctl reload nginx       # 重载配置(不中断服务)
sudo systemctl enable nginx       # 开机自启
sudo systemctl disable nginx      # 关闭自启
sudo systemctl status nginx       # 查看状态(PID、日志等)
# 预期输出:● nginx.service - A high performance web server
#            Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled)
#            Active: active (running) since ...

# 常用查询
systemctl is-active nginx          # active / inactive
systemctl is-enabled nginx         # enabled / disabled
systemctl list-units --type=service --state=running
# 预期输出:UNIT                     LOAD   ACTIVE SUB     DESCRIPTION
#            nginx.service            loaded active running A high performance web server

systemctl list-unit-files --type=service
# 预期输出:UNIT FILE                  STATE
#            nginx.service             enabled

# 屏蔽服务(防止手动或依赖启动)
sudo systemctl mask cups
# 预期输出:Created symlink /etc/systemd/system/cups.service → /dev/null

sudo systemctl unmask cups

enable 与 start 的区别

  • start:立即启动服务(当前会话)
  • enable:设置开机自启(通过创建符号链接)
  • enable --now:同时执行 enable + start(推荐使用)
  • disable --now:同时执行 disable + stop

systemd vs SysVinit 对比

特性systemdSysVinit
启动速度并行启动,速度快串行启动,速度慢
服务管理systemctl(统一入口)service / chkconfig
依赖管理支持 Wants/Requires/After依赖文件中的 # Required-Start
日志journald(结构化日志)syslog(文本文件)
定时任务systemd timercron
资源控制内置 cgroup 支持需额外工具
RHEL 版本RHEL 7+ 默认RHEL 6 及之前

mask 的使用场景

# mask 会创建 /dev/null 符号链接,完全阻止服务启动
# 适用于不使用的服务,防止被其他服务依赖启动
sudo systemctl mask cups

# 查看 mask 状态
systemctl is-enabled cups
# masked

# unmask 恢复
sudo systemctl unmask cups

2. journalctl 日志管理

# 查看服务日志
sudo journalctl -u nginx                    # 全部日志
sudo journalctl -u nginx -n 50              # 最后 50 行
sudo journalctl -u nginx -f                 # 实时跟踪
sudo journalctl -u nginx --since "1 hour ago"  # 近 1 小时
sudo journalctl -u nginx --until "2026-07-29 12:00"  # 截止时间

# 查看特定优先级
sudo journalctl -u nginx -p err             # 只看错误级别
# 预期输出:-- Logs begin at ... --
#            Jul 29 10:00:00 server nginx[1234]: [error] ...

# 查看内核日志
sudo journalctl -k
sudo journalctl -k -p err

# 查看启动日志
sudo journalctl -b 0                        # 本次启动
sudo journalctl -b -1                       # 上次启动

# 日志维护
sudo journalctl --vacuum-size=500M          # 限制日志大小
sudo journalctl --vacuum-time=7d            # 保留 7 天

日志优先级

级别缩写含义
emerg0系统不可用
alert1需要立即处理
crit2严重错误
err3错误
warning4警告
notice5正常但值得注意
info6信息
debug7调试

journalctl 常用组合

# 查看指定时间范围的日志
sudo journalctl --since "2026-07-29 10:00" --until "2026-07-29 12:00"

# 查看指定 PID 的日志
sudo journalctl _PID=1234

# 查看指定用户的日志
sudo journalctl _UID=1000

# 只看本次启动的错误日志
sudo journalctl -b -p err

# 输出为传统日志格式
sudo journalctl -o short-precise

3. 自定义 service unit 文件

# 创建服务单元
sudo vim /etc/systemd/system/myapp.service

# 内容:
[Unit]
Description=My Custom Application
After=network.target
Wants=network.target

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

[Install]
WantedBy=multi-user.target

# 重新加载 systemd
sudo systemctl daemon-reload
# 预期输出:无输出表示成功

# 启用并启动
sudo systemctl enable myapp
# 预期输出:Created symlink /etc/systemd/system/multi-user.target.wants/myapp.service → /etc/systemd/system/myapp.service

sudo systemctl start myapp

# 验证服务状态
sudo systemctl status myapp
# 预期输出:● myapp.service - My Custom Application
#            Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
#            Active: active (running) since ...

Unit 文件三大段详解

[Unit] 段

指令说明
Description服务描述(必填)
After在哪些单元之后启动(不影响启动顺序)
Before在哪些单元之前启动
Wants弱依赖:启动失败不影响当前服务
Requires强依赖:启动失败则当前服务也失败
Conflicts冲突:不能同时运行

[Service] 段

指令说明
Type服务类型(simple/forking/oneshot/notify/dbus)
ExecStart启动命令(必填)
ExecStop停止命令(可选)
ExecReload重载命令(可选)
Restart重启策略(always/on-failure/on-abnormal)
RestartSec重启间隔(秒)
User/Group运行用户/组
WorkingDirectory工作目录
Environment环境变量
LimitNOFILE最大打开文件数

[Install] 段

指令说明
WantedBy在哪个 target 下启动(最常用 multi-user.target)
RequiredBy强依赖于当前服务的目标
Also同时启用的其他单元

Type 详解

Type用途
simple默认。ExecStart 启动后立即认为已就绪
forking进程 fork 到后台(需 PIDFile)
oneshot执行一次即结束(适合一次性任务)
notify进程通过 sd_notify() 通知就绪
dbus等待 D-Bus 名字出现

4. systemd timer(替代 cron)

# 创建 timer 单元(配套 service)
sudo vim /etc/systemd/system/backup.timer

[Unit]
Description=Daily backup timer

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target

# 对应的 service 单元
sudo vim /etc/systemd/system/backup.service

[Unit]
Description=Daily backup job

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

# 启用 timer(不是 service!)
sudo systemctl enable backup.timer
sudo systemctl start backup.timer

# 查看 timer
systemctl list-timers --all
# 预期输出:NEXT                        LEFT          LAST                        PASSED               UNIT           ACTIVATES
#            Mon 2026-07-29 00:00:00 UTC  ...           Mon 2026-07-28 00:00:00 UTC  ...                   backup.timer   backup.service

# 日历表达式示例
# OnCalendar=daily                    # 每天 00:00
# OnCalendar=*-*-* 03:00              # 每天 03:00
# OnCalendar=Mon..Fri 09:00           # 工作日 09:00
# OnCalendar=*-*-1..7 02:00           # 每月前 7 天 02:00
# OnBootSec=5min                      # 启动后 5 分钟
# OnUnitActiveSec=1h                  # 上次激活后 1 小时

Timer 与 Cron 对比

特性systemd timercron
日志journalctl 集成单独日志文件
依赖管理支持 After/Requires不支持
错过的任务Persistent=true 可补执行不执行
精度微秒级分钟级
随机延迟RandomizedDelaySec不支持

常见 OnCalendar 表达式

# 每天凌晨 2 点
OnCalendar=*-*-* 02:00:00

# 每小时
OnCalendar=*-*-* *:00:00

# 每周一上午 9 点
OnCalendar=Mon *-*-* 09:00:00

# 每月 1 号 0 点
OnCalendar=*-*-01 00:00:00

# 每 5 分钟
OnCalendar=*-*-* *:00/05:00

# 每 30 秒(配合 AccuracySec)
OnCalendar=*-*-* *:00/00:30

5. Target 管理

# 查看当前 target
systemctl get-default
# multi-user.target(命令行)或 graphical.target(图形)

# 设置默认 target
sudo systemctl set-default multi-user.target

# 切换到不同 target(不重启)
sudo systemctl isolate multi-user.target

# 类比传统运行级别
# runlevel3 → multi-user.target
# runlevel5 → graphical.target
# runlevel0 → poweroff.target
# runlevel6 → reboot.target

# 查看所有 target
systemctl list-units --type=target

常用 Target

Target用途
multi-user.target多用户命令行模式
graphical.target图形界面模式
rescue.target单用户救援模式
emergency.target紧急模式(最小系统)
poweroff.target关机
reboot.target重启

6. 启动性能分析

# 总启动时间
systemd-analyze time

# 各单元启动耗时排名
systemd-analyze blame | head -10

# 关键启动路径
systemd-analyze critical-chain

# 生成启动时间 SVG 图
systemd-analyze plot > /tmp/boot.svg

# 查看单元依赖树
systemctl list-dependencies nginx

启动优化建议

# 禁用不需要的服务
sudo systemctl disable --now postfix
sudo systemctl disable --now cups

# 查看耗时最长的服务
systemd-analyze blame | head -5

# 查看关键链上的瓶颈
systemd-analyze critical-chain --no-pager

常见错误

常见错误原因分析解决方法
修改 unit 文件后不生效未执行 daemon-reloadsudo systemctl daemon-reload
服务启动失败ExecStart 路径错误或权限不足检查路径、权限、用户设置
enable 后重启不启动WantedBy 配置错误检查 [Install] 段的 WantedBy 值
timer 不触发未启动 timer 或表达式错误检查 systemctl list-timers
日志为空服务未配置日志输出检查 stdout/stderr 是否重定向到 journal

故障案例:修改 unit 文件后服务行为不变

现象

# 修改了 /etc/systemd/system/myapp.service 的 ExecStart 路径
# 但 systemctl restart myapp 后仍然执行旧命令

sudo systemctl status myapp
# Active: active (running) ...
# 执行的还是旧路径

原因与修复

# 原因:systemd 在启动时缓存了 unit 文件内容
# 修改文件后必须执行 daemon-reload 重新加载

# 修复
sudo systemctl daemon-reload          # 重新加载所有 unit 文件
sudo systemctl restart myapp          # 重启服务使用新配置
sudo journalctl -u myapp -n 5         # 确认日志中是新命令

故障案例:服务启动报 "Permission denied"

现象

sudo systemctl start myapp
# Job for myapp.service failed because the control process exited with error code.

sudo journalctl -u myapp -n 10
# ... /usr/local/bin/myapp: Permission denied

原因与修复

# 常见原因:① ExecStart 脚本无执行权限 ② User 字段指定的用户无权执行
# ③ SELinux 阻止 ④ 文件系统挂载为 noexec

# 排查
ls -la /usr/local/bin/myapp
# -rw-r--r-- 1 root root ... /usr/local/bin/myapp   ← 缺少 x 权限

# 修复
sudo chmod +x /usr/local/bin/myapp
sudo systemctl start myapp

# 如果是 SELinux 问题
sudo ausearch -m avc -ts recent | grep myapp
sudo setsebool -P myapp_execmem on   # 视情况开启布尔值

故障案例:systemd timer 不触发任务

现象

# timer 已启用但任务不执行
systemctl list-timers
# NEXT                         LEFT     LAST PASSED UNIT         ACTIVATES
# n/a                          n/a      n/a  n/a    backup.timer backup.service  ← 无 NEXT 时间

原因与修复

# 常见原因:① OnCalendar 表达式语法错误 ② timer 单元未 start ③ 时区问题

# 排查
systemctl status backup.timer
# Active: inactive (dead)   ← timer 未激活

# 修复:确保 start timer(不是 service!)
sudo systemctl start backup.timer
sudo systemctl status backup.timer
# Active: active (waiting)

# 验证日历表达式
systemd-analyze calendar "daily"
# → Next elapse: ...

# 查看上次执行日志
journalctl -u backup.service -n 20

实操练习

练习 1:创建自定义 systemd 服务

创建一个简单的 HTTP 服务单元文件,要求服务以 nobody 用户运行,开机自启。

# 创建脚本
echo '#!/bin/bash
while true; do echo "HTTP/1.1 200 OK\r\n\r\nHello" | nc -l -p 8888; done' | sudo tee /usr/local/bin/myservice.sh
sudo chmod +x /usr/local/bin/myservice.sh

# 创建 unit 文件
sudo vim /etc/systemd/system/myservice.service

# 内容
[Unit]
Description=Simple HTTP Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/myservice.sh
Restart=always
RestartSec=3
User=nobody

[Install]
WantedBy=multi-user.target

# 启用并启动
sudo systemctl daemon-reload
sudo systemctl enable --now myservice
curl http://localhost:8888

练习 2:配置 systemd timer 执行日志清理

创建一个 timer,每天凌晨 3 点清理 /tmp 目录中超过 7 天的文件。

# 创建清理脚本
echo '#!/bin/bash
find /tmp -type f -mtime +7 -delete' | sudo tee /usr/local/bin/cleanup.sh
sudo chmod +x /usr/local/bin/cleanup.sh

# 创建 service 单元
sudo vim /etc/systemd/system/cleanup.service
# [Unit]
# Description=Daily temp cleanup
# [Service]
# Type=oneshot
# ExecStart=/usr/local/bin/cleanup.sh

# 创建 timer 单元
sudo vim /etc/systemd/system/cleanup.timer
# [Unit]
# Description=Daily cleanup timer
# [Timer]
# OnCalendar=*-*-* 03:00:00
# Persistent=true
# [Install]
# WantedBy=timers.target

# 启用 timer
sudo systemctl daemon-reload
sudo systemctl enable --now cleanup.timer

# 验证
systemctl list-timers | grep cleanup

练习 3:排查服务启动失败

模拟一个服务启动失败的场景,使用 journalctl 排查问题。

# 故意创建一个有问题的 unit 文件(ExecStart 路径错误)
sudo vim /etc/systemd/system/test.service
# [Unit]
# Description=Test service
# [Service]
# Type=simple
# ExecStart=/nonexistent/path
# [Install]
# WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl start test
# 会失败

# 排查
sudo systemctl status test
sudo journalctl -u test -n 30
# 查看错误原因并修复

练习 4:查看和分析系统启动性能

# 查看启动时间
systemd-analyze time

# 查看耗时最长的服务
systemd-analyze blame | head -10

# 查看关键路径
systemd-analyze critical-chain

# 禁用不需要的服务(如 postfix)
sudo systemctl disable --now postfix 2>/dev/null
sudo systemctl disable --now cups 2>/dev/null

模拟题

题目 1

创建一个 systemd 服务单元文件,服务名为 webapp,描述为 "My Web Application",以 appuser 用户运行,工作目录为 /opt/webapp,启动命令为 /opt/webapp/start.sh,失败时自动重启(间隔 10 秒),开机自启。

参考答案sudo useradd -r -s /sbin/nologin appuser(先创建用户)→ sudo vim /etc/systemd/system/webapp.service 写入 [Unit] Description=My Web Application / After=network.target / [Service] Type=simple / User=appuser / Group=appuser / WorkingDirectory=/opt/webapp / ExecStart=/opt/webapp/start.sh / Restart=on-failure / RestartSec=10 / [Install] WantedBy=multi-user.target → sudo systemctl daemon-reloadsudo systemctl enable --now webappsudo systemctl status webapp 确认 active (running)。

题目 2

配置一个 systemd timer,每小时执行一次 /usr/local/bin/cleanup.sh 脚本。要求 timer 在启动后立即执行一次,并支持错过的任务补执行。

参考答案sudo vim /etc/systemd/system/cleanup.service([Service] Type=oneshot / ExecStart=/usr/local/bin/cleanup.sh)→ sudo vim /etc/systemd/system/cleanup.timer([Timer] OnCalendar=*-*-* *:00:00 / OnBootSec=5min / Persistent=true / [Install] WantedBy=timers.target)→ sudo systemctl daemon-reloadsudo systemctl enable --now cleanup.timersystemctl list-timers | grep cleanup 确认 timer 已激活。Persistent=true 确保错过的任务在下次启动时补执行。

题目 3

服务 httpd 无法启动,使用 journalctl 查看日志,找到错误原因并修复。要求记录排查过程和修复步骤。

参考答案:① sudo systemctl status httpd 查看状态(显示 failed);② sudo journalctl -u httpd -n 30 查看日志定位错误;③ 常见原因与修复:端口被占用 → ss -tlnp | grep :80 找到占用进程 → kill 或修改 httpd 端口;配置语法错误 → sudo httpd -t 检查配置 → 修正后 sudo systemctl start httpd;SELinux 阻止 → sudo ausearch -m avc -ts recent → 修复 context 或布尔值;④ 修复后 sudo systemctl restart httpdsudo systemctl status httpd 确认 active (running)。

最佳实践

  • 修改 unit 文件后务必执行 systemctl daemon-reload,否则 systemd 不会重新读取配置
  • 创建服务时,优先使用 enable --now 代替分别执行 enable 和 start
  • 自定义 unit 文件放在 /etc/systemd/system/,不要修改 /usr/lib/systemd/system/ 下的文件
  • 使用 systemctl mask 完全禁用不需要的服务,比 disable 更彻底
  • 考试中先确认 systemd 版本:systemctl --version,确保命令兼容

验证 checklist

systemctl status myapp        # 自定义服务状态
systemctl list-timers         # timer 是否激活
systemctl get-default         # 默认 target
journalctl -u myapp -n 20    # 服务日志
sudo systemctl daemon-reload # 修改 unit 后的必要步骤

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释 systemd 的 unit 类型和 target 概念尝试向他人讲解
命令操作能不查文档完成 systemctl 服务管理和 systemd unit 编写在终端实际执行
原理掌握能说出 systemd 的依赖管理和并行启动原理画出流程图
故障排查能独立排查服务启动失败或依赖关系错误的问题模拟故障并修复
最佳实践能说明为什么推荐使用 systemd timer 替代 cron对比不同方案

本章总结

systemd 是 RHEL 服务的唯一入口,掌握 systemctl 三大操作面:unit 管理、日志、依赖。自定义 unit 的 Restart 策略与 Wants/After 依赖是高频考点,timer 替代 cron 的写法要会。故障排查先看 status 再看 journalctl -xe。

延伸阅读

↑ 回到顶部