路线图

服务管理 systemd

星辉 2026-07-02 阅读 6 min 1,266 字 路线图
服务管理 systemd 封面

Linux 运维基础 · 第十三章 目标:掌握 systemd 体系,能写 service 文件、管理服务和日志


1. 概述#

systemd 是现代 Linux 的初始化系统和服务管理器。它替代了老旧的 SysV init 脚本,提供并行启动、依赖管理、cgroup 集成、统一日志等功能。

本章让你从"会用 systemctl start/stop"升级到"能写 service 文件 + 会查日志排障"。


2. systemd 设计理念#

2.1 SysV init 的痛点#

text
SysV init(老方式):
  - 按顺序启动:A 启动 → B 启动 → C 启动(慢)
  - 脚本管理:每个服务一个 shell 脚本(维护难)
  - 日志:每个服务自己写日志文件(不统一)
  - 依赖:简单的数字优先级(不够精细)

2.2 systemd 的改进#

特性SysV initsystemd
启动方式串行并行(按依赖关系)
服务定义Shell 脚本声明式 Unit 文件
日志各管各的journald 统一收集
资源管理cgroup 集成
依赖管理数字优先级精确依赖描述
socket 激活不支持支持(按需启动)
定时任务cron内置 Timer Unit

cgroup v2 是现代默认:RHEL/Rocky/Alma 9+、Ubuntu 22.04+、Fedora 默认已启用 cgroup v2(统一层级 unified hierarchy),取代 v1 的"每个资源一棵树"。 systemd 靠它给每个 service 做资源统计与限制。查看当前用哪版:

bash
stat -fc %T /sys/fs/cgroup/       # 输出 cgroup2fs = v2;tmpfs = 还是 v1(如 RHEL/Rocky 8)
systemctl show <svc> -p MemoryCurrent   # v2 下能直接读到服务实时内存

service 里可直接写 MemoryMax=512MCPUQuota=50%TasksMax=100 做资源限制(见 5.3)。

2.3 核心概念:Unit#

systemd 中一切皆 Unit。不同类型处理不同职责:

Unit 类型后缀用途
service.service服务(最常用)
target.target一组 Unit 的集合(相当于 SysV 的 runlevel)
timer.timer定时任务(替代 cron)
socket.socketsocket 激活
mount.mount挂载点管理
device.device设备管理
slice.slicecgroup 资源分组

3. systemctl 核心操作#

3.1 启停服务#

bash
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 enable --now nginx     # 一步到位:登记开机自启 + 立即启动

# 坑:enable 与 start 是两件事,容易只做一半
#   只 start 不 enable → 这次跑得好好的,重启后服务没了
#   只 enable 不 start → 现在没跑,要等下次开机才生效

# 屏蔽/取消屏蔽(连手动启动都不让)
systemctl mask nginx             # 屏蔽(创建 /dev/null 的符号链接)
systemctl unmask nginx           # 取消屏蔽

3.2 查看状态#

bash
systemctl status nginx           # 完整状态(进程 + 日志摘要)

# 关键输出解读
# Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
#         ^^ enabled = 开机自启, disabled = 不开机自启, masked = 已屏蔽
# Active: active (running) since ...
#         ^^ active=运行中, inactive=未运行, failed=启动失败, activating=启动中
# Main PID: 1234 (nginx)
# CGroup: /system.slice/nginx.service

3.3 列出 Unit#

bash
# 列出所有已加载的 Unit
systemctl list-units            # 活动的 Unit
systemctl list-units --all      # 所有 Unit(含不活动的)

# 只看服务
systemctl list-units --type=service

# 只看开机自启的服务
systemctl list-unit-files --type=service --state=enabled

# 看失败的 Unit
systemctl --failed

# 看所有已安装的服务(不论是否启用)
systemctl list-unit-files --type=service

3.4 重启 / 关机#

bash
systemctl reboot                 # 重启
systemctl poweroff               # 关机
systemctl suspend                # 休眠
systemctl rescue                 # 救援模式(单用户)

4. journalctl — 日志查看#

4.1 为什么重要#

systemd 用 journald 统一收集所有服务的 stdout/stderr + 内核日志 + syslog。不再需要去 /var/log/ 逐文件翻

4.2 基本用法#

bash
journalctl                       # 所有日志(可能很多)
journalctl -u nginx              # 只看 nginx 服务的日志
journalctl -u nginx -f           # 实时跟踪(类似 tail -f)
journalctl -u nginx --since "10min ago"  # 最近 10 分钟
journalctl -u nginx --since "2026-07-01 10:00:00" --until "2026-07-01 11:00:00"

# 只看今天的
journalctl --since today

# 看最近启动的日志(本次系统启动后的)
journalctl -b                    # 本次启动
journalctl -b -1                 # 上一次启动

# 按优先级过滤
journalctl -p err                # 只看错误及以上级别
journalctl -p warn               # 只看警告及以上
# 级别:emerg(0) alert(1) crit(2) err(3) warning(4) notice(5) info(6) debug(7)

# 搜索
journalctl --grep="timeout"      # 搜索关键词
journalctl -u nginx --grep="error"

# 内核日志
journalctl -k                    # 只看内核日志(同 dmesg)
journalctl -k --since today

4.3 日志分析#

bash
# 看服务为什么启动失败
systemctl status nginx --no-pager -l  # 显示完整日志
journalctl -u nginx -n 50 --no-pager

# 统计某个服务今天的错误数
journalctl -u nginx --since today -p err --no-pager | wc -l

# 查看日志占用的磁盘空间
journalctl --disk-usage

# 清理旧日志(只保留最近 7 天)
journalctl --vacuum-time=7d
# 或限制大小
journalctl --vacuum-size=500M

4.4 日志持久化(默认可能一重启就没了!)#

大坑journald 默认 Storage=auto,只有当目录 /var/log/journal/ 存在时才落盘持久化; 否则日志写在 /run/log/journal/(tmpfs 内存),一重启全丢。RHEL 系默认就没有这个目录 (历史日志查不到,journalctl -b -1 看上次启动会直接报错),排障前务必先开持久化。

bash
# 判断当前是否持久化
journalctl --disk-usage          # 若显示的是 /run/... 路径 → 没持久化
ls /var/log/journal/ 2>/dev/null # 目录不存在 = 没持久化

# 开启持久化(两种方式,任选其一)
# 方式 A:建目录,auto 模式即自动落盘
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

# 方式 B:显式配置
# 编辑 /etc/systemd/journald.conf
# [Journal]
# Storage=persistent
# SystemMaxUse=1G          # 顺手限制上限,防止日志撑爆磁盘
systemctl restart systemd-journald

5. 编写 Unit 文件#

5.1 文件位置#

路径用途优先级
/etc/systemd/system/用户自定义(运维装的服务放这)最高
/run/systemd/system/运行时生成的
/usr/lib/systemd/system/系统包管理器安装的(不要手改)最低

5.2 三段结构#

一个 .service 文件有三个主要段落:

ini
[Unit]
# 服务描述和依赖

[Service]
# 如何启动/停止这个服务

[Install]
# 安装相关的配置(开机自启等)

5.3 完整示例:myapp.service#

ini
# /etc/systemd/system/myapp.service

[Unit]
Description=My Application Service
Documentation=https://docs.myapp.com
After=network.target                    # 网络就绪后启动
Requires=network.target                 # 硬依赖(网络挂了本服务也停)

[Service]
Type=simple                             # 最常用:主进程就是服务进程
User=myapp                              # 用哪个用户运行
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID      # reload 时发 SIGHUP
ExecStop=/bin/kill -TERM $MAINPID       # 停止命令
Restart=on-failure                      # 异常退出时自动重启
RestartSec=5                            # 重启前等 5 秒
StandardOutput=journal                  # stdout 进 journald
StandardError=journal                   # stderr 进 journald
SyslogIdentifier=myapp                  # journald 中的标识
LimitNOFILE=65536                       # 文件描述符上限

# 资源限制(cgroup v2,防止单个服务拖垮整机)
MemoryMax=512M                          # 内存硬上限,超了会被 OOM 掉该服务
CPUQuota=50%                            # 最多用 0.5 个 CPU
TasksMax=100                            # 最多 100 个线程/进程

# 环境变量
Environment="APP_ENV=production"
Environment="DATABASE_URL=postgresql://localhost:5432/myapp"
# 或从文件加载
# EnvironmentFile=/etc/myapp/env.conf

[Install]
WantedBy=multi-user.target              # 多用户模式下启动

5.4 Type 类型说明#

Type含义何时用
simpleExecStart 启动的进程就是主进程最常用
forking服务自己 fork 到后台,父进程退出老式守护进程(如 nginx)
oneshot执行一次就退出,配合 RemainAfterExit初始化脚本
notify服务就绪后发 sd_notify() 通知支持 systemd 的新式服务
dbus服务从 D-Bus 获取名称桌面环境
idle等待所有其他作业结束后启动延迟到启动末尾

5.5 Restart 策略#

含义
no不重启(默认)
always不管怎样退出都重启
on-success退出码 0 才重启
on-failure退出码非 0 或被信号杀才重启(推荐)
on-abnormal被信号杀或超时才重启
on-watchdog看门狗超时

5.6 安装和生效#

bash
# 创建文件后
systemctl daemon-reload              # 重载 systemd 配置

# 启动并设为开机自启
systemctl enable --now myapp        # enable + start 一步到位

# 查看状态
systemctl status myapp

# 删服务
systemctl stop myapp
systemctl disable myapp
rm /etc/systemd/system/myapp.service
systemctl daemon-reload

6. Target vs Runlevel#

6.1 对应关系#

SysV Runlevelsystemd Target含义
0poweroff.target关机
1rescue.target单用户/救援模式
3multi-user.target多用户命令行(服务器默认)
5graphical.target图形界面(桌面版默认)
6reboot.target重启
bash
# 查看当前 target
systemctl get-default

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

# 临时切换 target
systemctl isolate rescue.target

7. systemd Timer(cron 的现代替代)#

Timer 是 systemd 内置的定时机制,逐渐取代 cron。相比 crontab 的优势: 统一进 journald(journalctl -u 直接看每次跑的输出,cron 得自己重定向日志)、 支持 Persistent=关机期间错过的任务,开机后补跑,cron 做不到)、 依赖/资源限制/失败自动重试都能复用 service 的能力。

7.1 Timer + Service 成对出现#

一个 timer 负责"何时触发",它拉起一个同名的 .service 负责"干什么"。

ini
# /etc/systemd/system/backup.service   —— 干活的(Type=oneshot,跑完就退)
[Unit]
Description=Daily backup job

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup.sh
ini
# /etc/systemd/system/backup.timer     —— 定时的(文件名主干必须和 service 一致)
[Unit]
Description=Run backup daily at 02:00

[Timer]
OnCalendar=*-*-* 02:00:00       # 每天 02:00(格式:星期 年-月-日 时:分:秒)
Persistent=true                 # 错过了(比如那会儿关机)开机后立即补跑一次
RandomizedDelaySec=300          # 随机延迟 0~5 分钟,避免整点惊群

[Install]
WantedBy=timers.target
bash
# 启用的是 timer,不是 service
systemctl daemon-reload
systemctl enable --now backup.timer

systemctl list-timers --all      # 看所有定时器:下次触发时间 / 上次触发 / 还剩多久
journalctl -u backup.service     # 看历次执行的输出(cron 得自己配日志才有)
systemd-run --on-active=30s /path/cmd   # 临时一次性定时(不用建文件)

7.2 OnCalendar 常用写法 & 与 cron 对照#

text
OnCalendar=hourly                 # 每小时(整点)
OnCalendar=daily                  # 每天 00:00
OnCalendar=*-*-* 02:00:00         # 每天 02:00           cron: 0 2 * * *
OnCalendar=Mon *-*-* 09:00:00     # 每周一 09:00         cron: 0 9 * * 1
OnCalendar=*-*-* *:00/15:00       # 每 15 分钟           cron: */15 * * * *
OnCalendar=*-*-01 03:00:00        # 每月 1 号 03:00      cron: 0 3 1 * *
bash
systemd-analyze calendar "*-*-* 02:00:00"   # 校验表达式并算出下几次触发时间(写完先验证!)

另一种触发方式:OnBootSec=/OnUnitActiveSec=(相对时间),如 OnBootSec=15min(开机 15 分钟后跑)、OnUnitActiveSec=1h(上次跑完 1 小时后再跑)。


8. 启动耗时分析(systemd-analyze)#

bash
systemd-analyze                  # 总启动时间(firmware/loader/kernel/userspace 各占多少)
systemd-analyze blame            # 按耗时排序,揪出拖慢开机的服务
systemd-analyze critical-chain   # 关键路径:哪些服务串在启动链上、各自等了多久
systemd-analyze plot > boot.svg  # 生成可视化启动时间线

排查"开机很慢"就从 blame 入手:某个服务耗时几十秒,多半是它在等网络/DNS/依赖。


9. 实战#

实战 1:看开机自启的服务#

bash
systemctl list-unit-files --type=service --state=enabled

# 常见结果
# nginx.service          enabled
# sshd.service           enabled
# docker.service         enabled
# cron.service           enabled

# 检查有没有不该自启的、或者应该自启的没开

实战 2:排查服务启动失败#

bash
# Step 1:看状态
systemctl status myapp
# Active: failed (Result: exit-code)

# Step 2:看日志
journalctl -u myapp -n 50 --no-pager

# Step 3:手动跑 ExecStart 命令,看报错
sudo -u myapp /opt/myapp/bin/myapp --config /etc/myapp/config.yaml

# Step 4:常见问题
# - 配置文件路径不对
# - User 不存在
# - 端口被占用
# - 权限不够

实战 3:修改已安装服务的配置#

bash
# 不要直接改 /usr/lib/systemd/system/nginx.service
# 覆盖机制:

# 创建覆盖目录
mkdir -p /etc/systemd/system/nginx.service.d/

# 创建覆盖文件
cat > /etc/systemd/system/nginx.service.d/override.conf << EOF
[Service]
LimitNOFILE=65535
Environment="EXAMPLE=value"
EOF

# 重载
systemctl daemon-reload
systemctl restart nginx

# 查看合并后的完整配置
systemctl cat nginx

10. 小结#

知识点一句话记忆
systemctlstart/stop/restart/reload/enable/disable/status
enable vs startenable=开机自启(不立即跑),start=立即跑(不持久);enable --now 两件事一起做
改 unit 后必须 systemctl daemon-reload 才生效
服务文件三段:[Unit] 依赖 + [Service] 启动方式 + [Install] 自启
文件位置/etc/systemd/system/ 用户自定义,优先级最高
常用 Typesimple(最常用)/ forking(老式)/ oneshot(一次性/timer)
journalctl-u 指定服务,-f 实时,--since 时间过滤,-p 级别过滤
日志持久化默认可能不落盘,建 /var/log/journalStorage=persistent
Timercron 的现代替代,OnCalendar + Persistent=true 补跑,list-timers
systemd-analyzeblame 揪出拖慢开机的服务
覆盖配置/etc/systemd/system/<service>.d/override.conf,不修改原文件
targetmulti-user.target≈runlevel 3(命令行)、graphical.target≈runlevel 5(图形)
cgroup v2RHEL9/Ubuntu22.04+ 默认,service 里可写 MemoryMax/CPUQuota 限资源

上一章:十二、存储与磁盘管理 下一章:十四、Shell 脚本