本页目录

systemd 操作手册

systemd 是 Linux 用户态的 init 系统和服务管理器。理解 unit 类型(service/timer/socket/mount)、依赖关系、日志系统和 override 机制是运维的基础。

进程查看: btm (bottom, 替代 top — CPU/内存/磁盘/温度 TUI), procs (替代 ps — 彩色, 自动列宽, 进程树)

查看状态

# 查看 unit 当前状态,含最近 10 行日志、cgroup 树、主 PID
systemctl status nginx
# Active: active (running) since ... — 正在运行
# Active: inactive (dead) — 没在跑
# Active: failed — 启动失败 (看 exit code)

# 列出所有 unit,按状态过滤
systemctl list-units                    # 所有 loaded unit
systemctl list-units --state=failed    # 只看失败的
systemctl list-units --type=service    # 只看 service
systemctl list-units --type=timer      # 只看 timer

# 查看 unit 是否 enabled (开机启动)
systemctl is-enabled nginx
systemctl is-active nginx              # 是否在跑

# 查看 unit 的依赖树
systemctl list-dependencies nginx
systemctl list-dependencies nginx --reverse  # 谁依赖它

# 查看 unit 文件内容 (含所有 drop-in)
systemctl cat nginx

# 列出所有 timer 和下次触发时间
systemctl list-timers
systemctl list-timers --all            # 含 inactive

管理

# 启动/停止/重启
systemctl start nginx
systemctl stop nginx
systemctl restart nginx                # 进程会短暂中断
systemctl reload nginx                 # 热重载 (需服务支持, 如 nginx -s reload)
# 区别: reload 不中断连接,restart 会
# 如果服务不支持 reload,这个命令会失败

systemctl enable nginx                 # 开机启动 (创建 symlink)
systemctl disable nginx                # 取消开机启动
systemctl reenable nginx               # 先 disable 再 enable (修复损坏的 symlink)

systemctl mask nginx                   # 彻底禁止启动 (symlink → /dev/null)
systemctl unmask nginx                 # 恢复
# mask vs disable: disable 后仍可手动 start, mask 后连手动 start 都拒绝

# 改完 unit 文件后必须执行
systemctl daemon-reload                # 重新加载所有 unit 文件

# 清除 failed 状态 (否则 status 一直显示 failed)
systemctl reset-failed nginx

用户级服务

# 用户服务独立于系统服务: ~/.config/systemd/user/
# 用户登出后默认会停 (除非开启 lingering)
systemctl --user status myservice
systemctl --user start myservice
systemctl --user enable myservice

# 开启 lingering (登出后服务继续跑):
loginctl enable-linger <username>
# 适用: rathole-watchdog, 本地 llama-server, 等需要常驻的用户服务

Timer

Timer 在指定时间触发对应的 service (同名 + .timer/.service 配对):

# 查看 timer 定义
systemctl cat mytask.timer

# 手动触发 (不等下次)
systemctl start mytask.service         # 直接跑 service, timer 不受影响
# /etc/systemd/system/mytask.timer
[Timer]
# 上次激活后每隔 5min 触发
OnUnitActiveSec=5min
# 启动后 2min 触发首次
OnBootSec=2min
# 随机延迟 30s (避免多台机器同时跑 — thundering herd)
RandomizedDelaySec=30
# 弥补错过的触发 (如机器当时在休眠)
Persistent=true

[Install]
WantedBy=timers.target
# /etc/systemd/system/mytask.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/mytask.sh
User=myuser

日志

journalctl -u nginx                     # 指定 unit 的全部日志
journalctl -u nginx -f                  # follow (tail -f)
journalctl -u nginx --since "10 min ago"
journalctl -u nginx --since "2025-01-01" --until "2025-01-02"
journalctl -u nginx -n 50               # 最后 50 行
journalctl -u nginx -o json-pretty | head -100  # 结构化输出 (含 PID, UID, cmdline)

journalctl -b                           # 本次启动的日志
journalctl -b -1                        # 上次启动的日志 (排查重启前的问题)

journalctl _PID=12345                   # 按 PID 过滤

# 空间管理
journalctl --disk-usage                  # 当前占用
journalctl --vacuum-size=500M            # 限制最大 500MB
journalctl --vacuum-time=7d              # 保留最近 7 天

Override

修改 unit 配置不直接改 /usr/lib/systemd/system/ (会被包管理覆盖), 而是创建 drop-in:

# 打开编辑器创建 override
systemctl edit nginx
# → /etc/systemd/system/nginx.service.d/override.conf

# 或手动
mkdir -p /etc/systemd/system/nginx.service.d
cat > /etc/systemd/system/nginx.service.d/override.conf <<EOF
[Service]
# 加环境变量
Environment=DEBUG=true
# 修改启动命令 (先清空再设新的)
ExecStart=
ExecStart=/usr/bin/nginx -g "daemon off;"
# 设置重启策略
Restart=on-failure
RestartSec=5s
EOF
systemctl daemon-reload
systemctl restart nginx

注意 ExecStart= 第一行清空必须存在——不先清空的话,新的 ExecStart 是追加而非替换。

故障排查

# 服务起不来
systemctl status nginx                  # 看 Active: failed 和退出码
journalctl -u nginx -e --no-pager      # 最后 1000 行 (看 stderr)

# 分析启动瓶颈
systemd-analyze blame | head -20        # 每个 unit 初始化耗时
systemd-analyze critical-chain nginx    # 依赖链上哪一环最慢

# 验证 unit 文件
systemd-analyze verify /etc/systemd/system/myservice.service

# 某个 unit 被 mask 了导致 WantedBy 失效
ls -la /etc/systemd/system/*.target.wants/ | grep /dev/null