Ubuntu 系统系列 | 第 8 天:系统服务管理——systemd 与 systemctl 完全指南

第 8/30 天

引言

当你运行 systemctl status sshd 查看 SSH 服务状态,或执行 systemctl enable nginx 让 Nginx 开机自启时,你已经在使用 systemd——当今几乎所有主流 Linux 发行版(包括 Ubuntu)的默认初始化系统(init system)。systemd 不仅是进程管理器,更是一个完整的系统和服务管理器,掌管着从开机引导到服务监督、日志收集、定时任务、设备管理、网络配置等方方面面。

今天我们将深入 systemd 的世界,从核心概念到日常运维命令,再到调试排错和自定义服务编写,帮助你真正掌握这套现代 Linux 的「操作系统内核框架」。

核心概念

什么是 systemd?

systemd(System Daemon)是 Linux 系统的初始化系统和服务管理器,由 Lennart Poettering 于 2010 年创建,旨在替代传统的 SysV init 和 Upstart。它的核心特点:

  • 并行启动:最大化利用多核 CPU,同时启动不依赖的服务,大幅缩短开机时间
  • 依赖管理:通过 Unit 之间的依赖关系,按需自动启动相关服务
  • 统一管理:服务、挂载、设备、套接字、定时器……都用同一套语法和工具管理
  • CGroup 资源隔离:每个服务运行在独立的 Control Group 中,资源使用可追踪可限制
  • Socket 激活:服务无需开机启动,只有收到请求时自动按需拉起

核心概念:Unit

在 systemd 中,一切管理对象都被抽象为 Unit(单元)。每个 Unit 对应一个配置文件(.service.timer.mount 等),定义了该资源的启动方式、依赖关系、执行命令等。

Unit 类型 扩展名 用途 示例
服务单元 .service 管理后台服务进程 ssh.service, nginx.service
套接字单元 .socket 管理网络或 IPC 套接字 docker.socket
设备单元 .device 管理内核设备 sys-subsystem-net-devices-eth0.device
挂载单元 .mount 管理文件系统挂载点 home.mount
自动挂载单元 .automount 按需自动挂载 mnt-data.automount
定时器单元 .timer 替代 cron 的定时任务 systemd-tmpfiles-clean.timer
路径单元 .path 监控文件路径变化 自定义日志监控
目标单元 .target 逻辑分组,类似 SysV 的运行级别 multi-user.target, graphical.target

Unit 配置文件层级

/etc/systemd/system/          # 系统管理员定义(优先级最高)
/run/systemd/system/          # 运行时定义(重启后消失)
/lib/systemd/system/          # 发行版默认定义(apt 安装的软件包写在这里)

Ubuntu 的原则:不要修改 /lib/systemd/system/ 下的文件。自定义或覆盖配置应放在 /etc/systemd/system/ 下。

实战步骤

步骤一:服务状态查询

# 查看所有 Unit 状态(包括运行中、失败、退出的)
systemctl list-units

# 仅查看服务类 Unit
systemctl list-units --type=service

# 查看所有已加载的 Unit(包括 inactive 的)
systemctl list-units --all

# 查看所有已安装的 Unit 文件(含未加载的)
systemctl list-unit-files

# 查看失败的服务
systemctl --failed

输出示例:

UNIT                          LOAD   ACTIVE SUB     DESCRIPTION
cron.service                  loaded active running Regular background program processing daemon
nginx.service                 loaded active running A high performance web server
ssh.service                   loaded active running OpenBSD Secure Shell server
systemd-journald.service      loaded active running Journal Service
systemd-logind.service        loaded active running Login Service

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.

步骤二:服务生命周期管理

# 启动服务
sudo systemctl start nginx

# 停止服务
sudo systemctl stop nginx

# 重启服务
sudo systemctl restart nginx

# 重新加载配置(不中断服务)
sudo systemctl reload nginx

# 如果服务支持 reload 但不确定,用 reload-or-restart
sudo systemctl reload-or-restart nginx

# 查看详细状态
sudo systemctl status nginx

systemctl status 的输出非常丰富:

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2026-07-21 10:00:00 CST; 2h 30min ago
       Docs: man:nginx(8)
    Process: 1234 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 1235 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 1236 (nginx)
      Tasks: 2 (limit: 2317)
     Memory: 5.2M
        CPU: 123ms
     CGroup: /system.slice/nginx.service
             ├─1236 nginx: master process /usr/sbin/nginx -g daemon on; master_process on
             └─1237 nginx: worker process

关键信息含义:
Loaded:配置文件是否已加载以及是否开机自启(enabled/disabled)
Active:服务当前运行状态(active/running, inactive/dead, failed)
Main PID:主进程 PID
Tasks:该服务使用的任务数
CGroup:CGroups 层级,显示所有子进程

步骤三:开机自启管理

# 启用开机自启
sudo systemctl enable nginx

# 禁用开机自启
sudo systemctl disable nginx

# 启用并立即启动(常用组合)
sudo systemctl enable --now nginx

# 禁用并立即停止
sudo systemctl disable --now nginx

# 检查是否开机自启
systemctl is-enabled nginx

# 查看所有已启用的服务
systemctl list-unit-files --state=enabled

步骤四:日志查看(journalctl)

systemd 自带日志系统 journald,统一管理所有服务的日志输出。

# 查看所有日志(最新在末尾)
journalctl

# 查看特定服务的日志
journalctl -u nginx.service

# 查看最近 30 分钟的日志
journalctl --since "30 min ago"

# 查看今天的日志
journalctl --since today

# 查看特定时间范围的日志
journalctl --since "2026-07-21 08:00:00" --until "2026-07-21 10:00:00"

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

# 查看内核日志
journalctl -k

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

# 查看最近 N 条日志
journalctl -n 50

# 以 JSON 格式输出(便于程序解析)
journalctl -u nginx.service -o json-pretty

步骤五:分析系统启动时间

# 查看总体启动耗时
systemd-analyze

# 查看每个服务的启动耗时(按时间排序)
systemd-analyze blame

# 查看关键事件链
systemd-analyze critical-chain

# 生成 SVG 启动时间图(可直观分析)
systemd-analyze plot > /tmp/boot.svg

systemd-analyze blame 输出示例:

  3.214s networkd-dispatcher.service
  2.896s udisks2.service
  2.214s snapd.service
  1.843s dev-sda1.device
  1.567s systemd-resolved.service
  1.234s NetworkManager-wait-online.service
  0.987s postgresql@14-main.service

步骤六:编写自定义 systemd 服务

假设你有一个 Python 应用 ~/app/server.py,希望它作为系统服务运行。

1. 创建服务配置文件:

sudo tee /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Custom Python Application
Documentation=https://example.com/docs
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/server.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
Environment="PYTHONUNBUFFERED=1"
EnvironmentFile=/opt/myapp/.env
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

关键参数说明:

参数 说明 推荐值
Type 服务类型(simple/forking/oneshot/notify/dbus) 普通服务用 simple
User/Group 运行服务的用户 遵循最小权限原则
ExecStart 启动命令(绝对路径) 始终使用完整路径
Restart 重启策略(no/on-failure/on-abnormal/always) on-failure 最实用
RestartSec 重启间隔(秒) 5-10 秒
EnvironmentFile 环境变量文件 避免机密信息写在配置中
LimitNOFILE 文件描述符限制 高并发服务设为 65536

2. 重新加载 systemd 配置:

sudo systemctl daemon-reload

3. 启用并启动服务:

sudo systemctl enable --now myapp

4. 查看运行状态:

sudo systemctl status myapp

步骤七:使用 systemd Timer 替代 cron

systemd timer 比 cron 更强大:支持随机延迟、跳过已错过的任务、依赖其他服务、日志统一管理。

示例:每天凌晨 3 点执行备份脚本

1. 创建服务单元(timer 触发的工作):

sudo tee /etc/systemd/system/backup.service << 'EOF'
[Unit]
Description=Daily Backup Script

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

2. 创建定时器单元:

sudo tee /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Run backup daily at 3 AM
Requires=backup.service

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

[Install]
WantedBy=timers.target
EOF

参数说明:
OnCalendar=daily:每天执行一次
OnCalendar=*-*-* 03:00:00:指定每天凌晨 3 点
RandomizedDelaySec=300:随机延迟 0-300 秒,避免所有定时器同时触发
Persistent=true:如果系统在预定时间关机,开机后补执行

3. 启用定时器:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

4. 查看所有定时器:

systemctl list-timers --all

输出示例:

NEXT                         LEFT       LAST                         PASSED    UNIT                         ACTIVATES
Tue 2026-07-22 00:00:00 CST  13h left   Tue 2026-07-21 00:00:12 CST  10h ago   systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Tue 2026-07-22 03:00:00 CST  16h left   n/a                          n/a       backup.timer                 backup.service

步骤八:Mask 和覆盖

有时需要屏蔽某些服务,使其完全无法启动:

# 屏蔽服务(创建指向 /dev/null 的符号链接)
sudo systemctl mask apt-daily.service

# 解除屏蔽
sudo systemctl unmask apt-daily.service

# 覆盖服务配置(不修改原始文件)
sudo systemctl edit nginx.service
# 这会创建 /etc/systemd/system/nginx.service.d/override.conf

覆盖配置示例:

[Service]
# 增加 nginx 的文件描述符限制
LimitNOFILE=65536
# 覆盖 ExecStart 前的清理操作
ExecStartPre=
ExecStartPre=/usr/bin/sleep 10

常见问题

Q1: 修改了服务配置后为什么不生效?

现象:编辑了 /lib/systemd/system/nginx.service 后重启服务,配置没变。

原因:systemd 缓存了配置,需要重新加载。

解决方案

# 编辑配置后必须执行
sudo systemctl daemon-reload

# 然后重启服务
sudo systemctl restart nginx

Q2: 服务启动失败,但日志没有明确错误

现象systemctl start myservice 返回失败,status 只显示 failed

解决方案:使用 journalctl 查看详细日志:

# 查看该服务的完整日志
journalctl -u myservice.service -n 100 --no-pager

# 查看实时日志后启动服务
journalctl -u myservice.service -f &
systemctl start myservice

Q3: 服务无法停止(卡在 stop 状态)

现象systemctl stop myservice 超时,服务一直显示 deactivating

解决方案

# 查看还在运行的进程
systemctl status myservice

# 强制终止(发送 SIGKILL)
sudo systemctl kill -s SIGKILL myservice

# 或设置超时后强制结束
sudo systemctl stop myservice --force

防止未来出现此问题,修改服务配置:

[Service]
TimeoutStopSec=30
KillMode=mixed

Q4: systemd 版本差异

现象:Ubuntu 20.04 和 22.04 的 systemd 命令行为不同。

原因:Ubuntu 20.04 使用 systemd 245,22.04 使用 249,功能有差异。

功能 20.04 (v245) 22.04 (v249)
systemctl --user 支持 支持,更完善
systemd-resolveresolvectl 旧命令 新命令
oomd 不支持 支持
systemd-sysusers 不支持 支持

检查版本:

systemd --version

Q5: 服务启动顺序依赖

现象:服务 A 需要数据库就绪后才能启动,但有时 A 先启动导致连接失败。

解决方案:使用 After + Wants 组合,再加上重试机制:

[Unit]
Description=My App
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
# 使用脚本等待数据库就绪
ExecStartPre=/usr/local/bin/wait-for-it.sh localhost:5432 --timeout=30
ExecStart=/usr/bin/python3 /opt/myapp/server.py
Restart=on-failure
RestartSec=5

总结

  1. systemd 是现代 Linux 的核心 — 它不仅是 init 系统,更是统一的服务管理器、日志系统、定时任务系统。掌握 systemd 是 Ubuntu 运维的必修课。

  2. 核心命令三板斧systemctl(服务管理)、journalctl(日志查看)、systemd-analyze(性能分析),覆盖了从日常运维到故障排查的完整场景。

  3. 自定义服务最佳实践:使用 Type=simple + Restart=on-failure + RestartSec=5 作为通用模板;通过 daemon-reload 使配置生效;用 systemctl edit 覆盖而非直接修改原始文件。

  4. Timer 比 cron 更强大:随机延迟、持久化执行、统一日志管理的优势让它成为现代 Linux 的首选定时方案。

  5. 排错思路:先 status 看状态,再 journalctl -u 查日志,然后 daemon-reload 排除缓存问题,最后 analyze blame 定位性能瓶颈。

下期预告

第 9 天我们将探讨磁盘与文件系统管理——从 lsblkfdisk 分区工具,到 mkfs 创建文件系统、mount 挂载管理,再到 LVM 逻辑卷的弹性伸缩实战,带你全面掌握 Ubuntu 存储管理。

系列目录

天数 主题 链接
第 1 天 Ubuntu 简介与版本选择 阅读
第 2 天 手把手安装 Ubuntu 阅读
第 3 天 Ubuntu 桌面环境初探 阅读
第 4 天 Ubuntu 终端基础 阅读
第 5 天 用户与权限管理 阅读
第 6 天 软件包管理 阅读
第 7 天 文件与文本操作 阅读
第 8 天 系统服务管理:systemd 与 systemctl ← 今日发布
第 9 天 磁盘与文件系统管理 🔜 敬请期待
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容