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 实现定时任务
前置知识
- 2.4:进程管理 进程管理——进程生命周期与信号
- 2.12:systemd 深入 systemd 深入——systemd 作为 init 系统的定位
- 4.8:集中式日志管理 集中式日志管理——journald 与日志体系
- 2.1:Shell 脚本入门 Shell 脚本入门——编写 ExecStart 脚本基础
考试要点
| 考点 | 权重 | 典型题目 |
|---|---|---|
| systemctl 服务管理 | 25% | 启动/停止/启用/禁用指定服务 |
| 自定义 unit 文件 | 30% | 创建一个 systemd 服务单元文件 |
| systemd timer | 20% | 配置定时执行的备份任务 |
| 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 对比
| 特性 | systemd | SysVinit |
|---|---|---|
| 启动速度 | 并行启动,速度快 | 串行启动,速度慢 |
| 服务管理 | systemctl(统一入口) | service / chkconfig |
| 依赖管理 | 支持 Wants/Requires/After | 依赖文件中的 # Required-Start |
| 日志 | journald(结构化日志) | syslog(文本文件) |
| 定时任务 | systemd timer | cron |
| 资源控制 | 内置 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 天
日志优先级
| 级别 | 缩写 | 含义 |
|---|---|---|
| emerg | 0 | 系统不可用 |
| alert | 1 | 需要立即处理 |
| crit | 2 | 严重错误 |
| err | 3 | 错误 |
| warning | 4 | 警告 |
| notice | 5 | 正常但值得注意 |
| info | 6 | 信息 |
| debug | 7 | 调试 |
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 timer | cron |
|---|---|---|
| 日志 | 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-reload | sudo 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-reload → sudo systemctl enable --now webapp → sudo 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-reload → sudo systemctl enable --now cleanup.timer → systemctl 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 httpd → sudo 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。
延伸阅读
- 2.12:systemd 深入 systemd 深入——Unit 编写与高级特性(BIOS → GRUB → systemd)