type: learning-note
title: Linux:第三部分产出日(整块复习 + 服务起不来排查记录)
date: 2026-10-15
description: 六天主干逐段复现(日志全景/journalctl四限定/logger/logrotate/unit三段/status两轴;其中 5 条验收未过);⭐ 按 Claude 提供的坏服务照抄写入 s1lab-collector、从 start 不报错一路查到 /var/lib/s1lab 不存在、修好后 active、再收拾干净;踩坑八条(-p error→err、-u≠-t、logger 等 stdin、WantedBy≠Wants、rm -f 不删目录、-d 看目录自己、systemd reload 不存在、is-active 查太早)
2026-10-15 学习笔记
> 📌 GitHub:https://github.com/AR-firefly
笔记区
核心观点
**一、复习第三部分**
第三部分六天学的是**日志 + systemd**。产出日要做两件事:
-
**整块复习**(六天的主干不查笔记复现一遍)
-
**交产出**:「**服务起不来**排查记录」------ **这是 S1 闸门的主产出**
**二、第三部分六天主干(复现时用的)**
| 天 | 主干 |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 第 1 天 · 日志全景 | 两个用途(排障/审计)· 三个前提 · **链路四步**(来源→收集器→**路由/保留**→查看器,第 ③ 步定"存哪")· journald 与 rsyslog **可并存** · 不能背路径,要现查 |
| 第 2 天 · `journalctl` | 四种限定:`-u`(unit)· `-b`(哪次开机,正数从头、负数从尾)· `-p`(优先级,含该档及更严重)· `--since`/`--until`(时间)· `-n` 是条数轴,别跟时间轴混 |
| 第 3 天 · syslog | `syslog`=规矩、`rsyslog`=一种实现、`journald`=另一个程序;**journald 上游 → 转发给 rsyslog**;设施会被改写(`kern`→`user`);八级 0--7(越小越严重);`logger` 造日志;`dmesg` 读内核环形缓冲 |
| 第 4 天 · logrotate | 防撑爆 + 防丢证据;**轮转不是备份**;五步(改名→建替代→(可选)叫重开→压缩→删超保留);`weekly` 管频率、`rotate N` 管库存;`compress`/`delaycompress`;`postrotate`...`endscript` 轮转后跑;`-d` 预演、`-f` 强行真转 |
| 第 5 天 · unit 模型 | systemd 是 PID 1;单元靠后缀分类;三目录优先级 `/etc` > `/run` > `/usr/lib`;三段 `Unit`/`Service`/`Install`;**依赖 vs 顺序正交**(`Wants`/`Requires` 管要不要,`After`/`Before` 管先后,要写两遍) |
| 第 6 天 · systemctl | 最小 unit 三行;`start`/`stop`/`restart` + `is-active`;`status` 两个轴(`Loaded:` 开机起不起 / `Active:` 现在跑不跑);`enable`/`disable` 建删 `.wants/` 链接;**`Type=simple` 的坑** |
**三、⭐ 一次完整的「服务起不来」排查**
`/opt/s1lab/collector.sh`:
```bash
#!/bin/bash
cd /var/lib/s1lab || exit 1
echo collector ready
exec sleep infinity
```
`/etc/systemd/system/s1lab-collector.service`:
```ini
Unit
Description=S1 Lab Collector
Service
ExecStart=/opt/s1lab/collector.sh
Install
WantedBy=multi-user.target
```
> `Service` 只写 `ExecStart=` 是**合法的最小写法**;**`Type=` 不写 = 默认 `simple`**。
**排查七步**:
| 步 | 敲了什么 | 看到了什么 |
| --- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| 1 | `sudo systemctl start s1lab-collector.service` + `echo $?` | **没报错,退出码 0** |
| 2 | `systemctl is-active s1lab-collector.service` | **`failed`** ← 才发现它根本没起来 |
| 3 | `systemctl status s1lab-collector.service` | `Active: failed (Result: exit-code)`;`Process: ... status=1/FAILURE`;**底部日志写出真原因** |
| 4 | `journalctl -u s1lab-collector.service --no-pager` | **原始输出**含两次开机(`-- Boot ... --` 分隔);**产出里已剔除 10-15 备课段**,只留 10-17 的 |
| 5 | `systemctl cat s1lab-collector.service` | `ExecStart=/opt/s1lab/collector.sh`(确认跑的是哪个脚本) |
| 6 | `cat /opt/s1lab/collector.sh` | 第 2 行 `cd /var/lib/s1lab \|\| exit 1` |
| 7 | `ls -ld /var/lib/s1lab` | `No such file or directory` ← **根因** |
**修复**:
```bash
$ sudo mkdir -p /var/lib/s1lab
$ sudo systemctl start s1lab-collector.service
$ systemctl is-active s1lab-collector.service
active
```
**收拾现场**(**5 个动作 + 1 次核对**):`stop` → 删 unit 文件 → `rm -R` 删 `/opt/s1lab/` → `rm -R` 删 `/var/lib/s1lab/` → `daemon-reload` → 核对 **13 个条目 / 0 失败单元**。(服务是修好后才停的,`failed` 状态已清,没单独跑 `reset-failed`。)
**四、今天撞到的坑**
| # | 坑 | 正解 |
| --- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| 1 | `-p error` 报 `Unknown log level error` | 名字是 **`err`**(不是 `error`);这套名字**爱砍尾巴** |
| 2 | `journalctl -u s1lab` 查不到 `logger` 写的消息 | **`-u` 筛 unit,`-t` 筛 tag**;`logger` 写的是 tag,不属于任何单元 |
| 3 | `logger -t 'text1'` 敲完光标不回来 | **没给消息正文** → 它转去等**标准输入**;要给:`logger -t xxx "一句话"` |
| 4 | `WantedBy=` 当成 `Wants=` | `Wants=` 是**主动要它**;`WantedBy=` 是**被它要**(`By` = 被动) |
| 5 | `rm -f 目录` 报 `Is a directory` | `-f` 只是"强制、不提示";删目录要 **`-R`**(递归) |
| 6 | `ls -ld 目录 \| wc -l` 得 1 | `-d`=看**目录自己**;数里面要 `ls 目录 \| wc -l` |
| 7 | `systemd reload` 命令不存在 | 是 **`systemctl daemon-reload`** |
| 8 | `start` 后**立刻** `is-active` 显示 `active`(**备课实跑观察,非用户当天亲测**) | **查得太早**:进程刚拉起还没退出;等一两秒会翻 `failed`,判定要等稳定 |
关键方法/流程
**排查「服务起不来」的顺序(按需往下,不必走满)**
-
`start` 之后**别信它没报错** → `is-active` / `status`
-
`status` **底部**够清楚 → 直接验证那一条(`ls` / `cat`)
-
`status` 底部含糊 → `journalctl -u <单元> --no-pager` 拉全
-
日志指向"某脚本第 N 行" → `cat` 那个脚本(`systemctl cat` 确认脚本路径)
-
根因指向一个路径/目录 → `ls -ld` 确认它在不在
**判据**:`start` / `echo $?` 说的是"**命令吃没吃下去**";服务跑没跑,得**单独问 `is-active`**。
实战记录(**10-17 补跑**,用户亲自操作的输出)
> 本产出日 10-15 开始,实际跨 10-15~10-17 完成;下面是补跑那天的真实输出。
**① start 不报错,其实失败了**
```bash
$ sudo systemctl start s1lab-collector.service
echo ?
0
$ systemctl is-active s1lab-collector.service
failed
```
**② status 底部直接写出原因**
```bash
$ systemctl status s1lab-collector.service
× s1lab-collector.service - S1 Lab Collector
Active: failed (Result: exit-code) since Sat 2026-10-17 17:25:08 CST; 6min ago
Process: 4719 ExecStart=/opt/s1lab/collector.sh (code=exited, status=1/FAILURE)
Oct 17 17:25:08 AR-firefly collector.sh4719: /opt/s1lab/collector.sh: line 2: cd: /var/lib/s1lab: No such file or directory
Oct 17 17:25:08 AR-firefly systemd1: s1lab-collector.service: Failed with result 'exit-code'.
```
**③ 确认根因与修复**
```bash
$ ls -ld /var/lib/s1lab
ls: cannot access '/var/lib/s1lab': No such file or directory
$ sudo mkdir -p /var/lib/s1lab
$ sudo systemctl start s1lab-collector.service
$ systemctl is-active s1lab-collector.service
active
```
线索区(学完后合上材料,自问自答)
**Q: 日志链路四步是哪四步?哪一步决定"存哪"?**
A: 来源 → 收集器 → **路由/保留** → 查看器。**第 ③ 步**决定存哪、留多久。
**Q: 怎么在本机现查"日志存在哪、谁在写"?**
A: `ls -la /var/log`(有哪些文件)+ `systemctl list-units --state=running`(看 `journald` / `rsyslog` 在不在跑)。
**Q: `journalctl` 的四种限定各是什么?**
A: `-u` 按单元 · `-b` 按哪次开机 · `-p` 按优先级(含该档及更严重)· `--since`/`--until` 按时间。
**Q: `-u` 和 `-t` 差在哪?**
A: `-u` 筛 **unit**(哪个单元写的);`-t` 筛 **tag**(消息自带的标签)。`logger` 写的是 tag,不属于任何单元。
**Q: `logger -t xxx` 为什么不给消息会卡住?**
A: 消息项是"可选"的;不给它就从**标准输入**读,一直在等喂。
**Q: `rotate N` 和 `weekly` 是一回事吗?**
A: 不是。`weekly` 管**多久判一次**(频率),`rotate N` 管**留几代**(库存,总文件 = 1 + N)。
**Q: `delaycompress` 是为了方便查吗?**
A: 不是。是**程序还开着那份刚改名的老文件**,这时压缩会压坏;延迟一代,等它换了新文件再压。
**Q: `postrotate` 到 `endscript` 之间那段是干什么的?**
A: **轮转之后**要跑的命令 ------ 用来**喊程序重开日志**(本机是给 `rsyslog` 发 HUP)。
**Q: `Wants=` 和 `WantedBy=` 差在哪?**
A: `Wants=` 在 `Unit` 段,是**主动要它**(弱依赖);`WantedBy=` 在 `Install` 段,是**被它要**(`By` = 被动),`enable` 时据此建链接。
**Q: `start` 和 `enable` 差在哪?为什么 `enable` 完可能还没跑?**
A: `start` 管**现在跑**(直接按名字找单元文件);`enable` 管**开机自启**(建 `.wants/` 链接)。两个独立,谁也不等谁。
**Q: `Loaded:` 和 `Active:` 各答什么?**
A: `Loaded:` 答「**开机会不会自动起**」(`enabled`/`disabled`);`Active:` 答「**现在在不在跑**」(`active`/`inactive`/`failed`)。
**Q: `start` 一个坏服务,为什么命令不报错?**
A: 没写 `Type=`,默认 `simple` ------ "进程拉起来就算启动完成",不等它跑通。
**Q: 服务 `failed` 了,第一眼该看哪?**
A: `systemctl status` 一屏:`Active:` 行的 `Result:`、`Process:` 行的退出码、**底部日志**(常常直接写出原因)。
**Q: `Failed with result 'exit-code'` 就是原因吗?**
A: 不是。它只说"结果是非零退出";**真原因在它上面那行**(带 `line N` 和路径的那行)。
**Q: 排查必须把 `status → journalctl -u → unit → 依赖` 全走完吗?**
A: 不必。**哪一步已经给出足够信息,就直接验证那一条**;不够才往下。
**Q: `rm -f` 为什么删不掉目录?**
A: `-f` 只是"强制、不提示";删目录要 `-R`(递归)。
**Q: `ls -ld 目录` 和 `ls 目录` 差在哪?**
A: `-d` = 看**目录本身**(1 行);不带 `-d` = 列出**里面**的条目。
总结(50字以内,用自己的话概括今天学到了什么)
第三部分**未结清**:日志 + systemd 六天主干过了一遍,亲手把「start 不报错、其实 failed」的服务从 status 一路查到目录不存在、修好、清理干净、交出排查记录;但仍有五条验收未过。
复习卡片
| 概念 | 必学/了解 | 一句话 | 场景 |
| ------------------------------------ | --------- | --------------------------------------------------------------------------- | ------------------ |
| 日志链路四步 | 必学 | 来源→收集器→**路由/保留**→查看器 | 讲日志怎么流动 |
| 第 ③ 步定"存哪" | 必学 | 四步里只有它管存哪、留多久 | 日志找不到时定位 |
| `journalctl -u` | 必学 | 按单元筛 | 只看一个服务 |
| `journalctl -b` | 必学 | 按哪次开机筛(正数从头、负数从尾) | 查上次开机 |
| `journalctl -p` | 必学 | 按优先级筛,**含该档及更严重** | 只看要紧的 |
| `journalctl --since/--until` | 必学 | 按时间筛 | 框时间窗 |
| syslog 八级 | 必学 | `emerg`0 → `debug`7,**越小越严重**;错误那级叫 **`err`** | 写规则 / `-p` |
| `logger -t` | 必学 | 往系统日志写一条,`-t` 打标签;不给正文会等标准输入 | 造测试日志 |
| `-t`(journalctl) | 必学 | 按 **tag** 筛 | 捞自己造的日志 |
| `-u` ≠ `-t` | 必学 | `-u` 筛单元、`-t` 筛标签 | 别混 |
| `rotate N` | 必学 | 留 N 代(总文件 = 1 + N) | 数代数 |
| `weekly` | 必学 | 多久判一次(频率),跟 `rotate` 不是一回事 | 读配置 |
| `compress` | 必学 | 老的压成 `.gz` | 省空间 |
| `delaycompress` | 必学 | 刚退下来那代**先不压**(程序还开着) | 防压坏 |
| `postrotate`...`endscript` | 必学 | **轮转之后**跑,喊程序重开日志 | 读配置 |
| `logrotate -d` | 必学 | 预演:只打印、不动文件 | 改配置前 |
| `Unit` / `Service` / `Install` | 必学 | 是什么/跟谁有关系 · 怎么跑 · 装的时候怎么挂 | 三段 |
| `Wants=` | 必学 | **主动**要它(弱依赖) | `Unit` |
| `WantedBy=` | 必学 | **被**它要(`enable` 时建链接) | `Install` |
| `start` | 必学 | 现在跑(直接按名字找单元文件) | 开关 |
| `enable` | 必学 | 开机自启(建 `.wants/` 链接),**不启动** | 开机 |
| `Loaded:` | 必学 | 答"开机会不会自动起" | status |
| `Active:` | 必学 | 答"现在在不在跑" | status |
| `is-active` | 必学 | 问句:现在在不在跑 | 验证 start |
| `is-enabled` | 必学 | 问句:开机会不会起 | 另一个轴 |
| `is-failed` | 必学 | 问句:是不是失败状态 | 排查 |
| `Type=`(默认 `simple`) | 必学 | "怎么算起来了";不写 = 拉起来就算成功 | start 不报错的根因 |
| `status` 底部日志 | 必学 | 最近 10 行,常常直接写出原因 | 第一跳 |
| `systemctl cat` | 必学 | 看实际加载的 unit 文件(首行 `#` 是注释) | 确认 ExecStart |
| `systemctl daemon-reload` | 必学 | 让 systemd 重读单元文件 | 改完配置 |
| `rm -R` | 必学 | 递归删目录;`-f` 只是不问 | 清理现场 |
| `ls -d` | 必学 | 看目录自己,不展开 | 对比 `ls 目录` |
| `Service` 最小写法 | 必学 | 只写 `ExecStart=` 合法,其余走默认 | 今天这份 unit |
| `--list-boots` | 必学 | 列所有启动记录 | 查跨开机日志 |
| `reset-failed` | 必学 | 清失败状态(删文件不会自动抹掉) | 收拾现场 |
| `copytruncate` | 必学 | 复制一份 + 原件原地清空,不用喊程序;代价是那一下可能丢写 | 程序喊不动时用 |
| `Requires=` | 必学 | 强依赖:它起不来这边也不起 | `Unit` |
| `After=` / `Before=` | 必学 | 顺序:谁先谁后(对称的两种写法) | `Unit` |
| `stop` / `restart` | 必学 | 现在停 / 停再起 | 开关 |
| `disable` | 必学 | 删 `.wants/` 链接(开机不自启,**手动还能起**) | 开机 |
| `not-found` | 必学 | `is-enabled` 对不存在单元的回答 | 确认删干净 |
| `-n`(条数轴) | 必学 | 按条数取,跟时间轴分开 | 只要最近几条 |
| `--disk-usage` | 必学 | journal 占多大(验收条 ③ 要求"知道") | 查磁盘 |
| `mask` 与 `disable` 的区别 | 必学 | `disable` 只取消开机自启(手动能起);`mask` 链到 `/dev/null`(手动也不行) | 验收条 ⑥ 要求 |
| **了解层(合并一行)** | 了解 | `Type=` 其余 7 种 / `-o verbose`、`-o json` | 见到再查 |
旧知识回收(今天暴露出来的)
| 概念 | 一句话 | 出处 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| `ps` 默认范围 | 光敲 `ps` 只列"**同一个终端 + 同一个用户**"的进程;看全部要 `ps aux`(`a` 所有终端、`x` 连无终端的、`u` 用户格式) | 抽考 09-15 第 1 问,答"当前正在运行的",用 root 进程撞破 |
| `kill` 不等进程跑完 | 15 号 TERM 是"**请求**退出",发完就返回;`Terminated` ≠ `Done` | 抽考 09-15 第 2 问,答"等它完成了再杀",用 `sleep 300 &` 撞破 |
| crontab 五段与 `*` | 分 时 日 月 周;`*` = **不限**(不是"默认");第 5 段 `0` = **周日**;crontab 是**重复**跑 | 抽考 09-15 第 3 问 |
| `2>&1` 必须配 `>> 文件` | `2>&1` 只把错误接到输出那条线上;**那条线通向哪还得指定**------cron 里没终端,不写 `>>` 就丢 | 抽考 09-15 第 4 问 |
| `crontab -l/-e/-r` | `l`=读 · `e`=改 · `r`=**删全部**(没有 read 这个字母) | 抽考 09-15 补问 |
| 查八级的入口 | `man 1 journalctl` 的 `-p` 段列全八级;`man 3 syslog` 本机**没装** | 点 3 追问 |
| `list-units` 拆解 | `list-units` = 列单元;`--state=` = 筛子(通用) | 点 2 追问 |
今日产出验收
| # | 产出 | 判定 | 证据 |
| --- | --------------------------- | --------------- | -------------------------------------------------------------------------------------------------- |
| 1 | 六天主干不查笔记复现 | 未过 | 日志四步、四限定、`logger`、`logrotate` 三项指令、`WantedBy`、两个轴复现了;**②③④⑤⑥ 五条缺口未过** |
| 2 | ⭐ 独立定位一个起不来的服务 | 做得出 + 讲得清 | 亲手走完 7 步,自己指出 `line 2: cd: /var/lib/s1lab`,并总结出"status 够清楚就跳过" |
| 3 | ⭐ 自己修好 | 做得出 | `mkdir -p /var/lib/s1lab` → `start` → `is-active` = `active` |
| 4 | 排查记录落盘 | 有实物 | `kb\产出\块3-服务起不来排查记录.md` |
| 5 | 块 3 验收 7 条逐条判定 | 有结论 | 见产出文件第七节 |