跳转至

告警/MQ 链路体检(check-pipeline.sh)

仓库路径:deploy/nfos/check-pipeline.sh | 线上路径:/opt/Cy/services/system.d/nfos/check-pipeline.sh 语言:bash(调 curl + python3 解析)| 定时器:无(手动体检脚本)


用途

一条命令判断「告警/MQ 链路现在能不能信任」。

源码头部记下的动机(2026-09-24 实战暴露的问题不是「没有监控」,而是没人能一眼判断链路是否可用):

  • NATS 服务器活着(NatsDown=inactive)≠ 有人在消费(实测 in_msgs=35862 / out_msgs=38);
  • 平台 messages 表 pending=0 ≠ 端到端送达(可能是「没人订阅、消息被丢弃」);
  • 告警自检 success=1 ≠ 刚刚送达到人(要看 last_run 新鲜度与 adapter 投递计数是否在增长)。

本脚本按「六道关」逐项给 PASS / FAIL / WARN,最后给一句结论:

关 检查内容 判据要点
1 NATS 服务器是否可用 GET $NATS/varz 里有没有 "server_id"
2 投递率:有没有人在消费(最关键) ① 订阅级 msgs 是否随时间增长(/subsz?subs=1,前后各取一次,sleep 6);② JetStream 是否积压(/jsz?streams=1,messages>0 但 consumer_count=0 → !! 有消息但无消费者(积压风险))。明确警告不要用 /varz 的 out_msgs 当投递计数(启用 JetStream 时会得出「投递率 0.1%、没人消费」的错误 FAIL)
3 平台侧消息队列积压(messages 表) sudo -n test -r "$DB" 可读时按 status 分组计数,最老未投递时间;VERDICT=BACKLOG(pending+queued+failed > 100)或 OK。不可读时 warn "本机没有 nfarmsd 库(备节点为常态)"
4 告警链路自检的新鲜度与结果 Prometheus 查 nfos_alert_selftest_success 与 nfos_alert_selftest_last_run_timestamp_seconds;success=1 则 ok,否则 bad;age > 129600(36h)→ warn "自检超过 36h 未跑"
5 投递计数是否在增长(adapter 有没有真的推出去) 前后各查一次 nfos_alert_adapter_webhooks_total(sleep 3);并给出「要主动测用 sec-notify.py --selftest」的提示
6 Alertmanager 活动告警的「可判读性」 GET $AM/api/v2/alerts?active=true;统计缺 alertname 的条数,VERDICT=UNREADABLE 或 OK。在主节点按设计 SKIP(见下)

输出结论(三选一):

不可信任:N 项 FAIL —— 先修 FAIL 项,再谈"堆积"是否告警
基本可用但有 N 项 WARN —— 需要人工确认后再信任
链路自检全部通过(N 项)

第 6 关的重要前提(源码注释)

Alertmanager 只在备节点 Cy.TencentCOS-SG 上,而且绑的是 127.0.0.1:9093(podman 端口映射 127.0.0.1:9093->9093)。所以在主节点执行本脚本时,第 6 关按设计就取不到——这不是链路故障。脚本据此直接打印 SKIP。

何时用

  • 察觉到「告警没来」时:这是第一现场工具,比逐个 systemctl status 快。
  • 汇总通报正文末尾:digest.py 会把 sudo bash /opt/Cy/services/system.d/nfos/check-pipeline.sh 作为自证链路命令附在消息末尾(整条命令豁免路径脱敏,见 sanitize.PRESERVE_PATHS),值班照着敲即可。
  • 改过监控/告警链路之后:作为回归手段。
  • 不要当监控用:它是手动体检,没有 timer。

命令与参数

用法(脚本头部逐字):

sudo bash check-pipeline.sh [--json]
参数 类型 默认值 说明
--json 开关(仅出现在用法注释里) — 源码里没有实现:脚本体中不存在任何 argv 解析(无 case "$1"、无 getopts),$1 只作为 ok() / bad() / warn() 函数的第一个入参使用。(源码未给出 --json 的实现;传它不会报错,但也不会改变任何输出)

已验证的 argv 参数:0 个。

不带参数时的默认行为

完整跑完六道关,输出彩色 PASS / FAIL / WARN 行与结论。 全程只读(curl 查询 + sudo -n 只读打开 SQLite),不修改任何系统状态。

可用的环境变量(脚本头部三个变量,形式为 VAR=${VAR:-默认值})

变量 默认值 说明
PROM http://127.0.0.1:9090 Prometheus 基址(第 4/5 关)
NATS http://127.0.0.1:8222 NATS 监控端口基址(第 1/2 关)
AM http://127.0.0.1:9093 Alertmanager 基址(第 6 关)
DB /opt/Cy/services/system.d/nfarmsd/data/nfarmsd.db 硬编码,不可用环境变量覆盖(第 3 关)

示例

C=/opt/Cy/services/system.d/nfos/check-pipeline.sh

# 1) 标准用法(脚本头部给出的形式)
sudo bash "$C"

# 2) 从汇总通报正文里那条自证链路命令(digest.py 附在消息末尾)
sudo bash /opt/Cy/services/system.d/nfos/check-pipeline.sh

# 3) 只想看结论(过滤掉六道关的明细)
sudo bash "$C" | tail -5

# 4) 只在主节点看:第 6 关会 SKIP(AM 只在备节点且绑回环)
sudo bash "$C" | grep -A2 '第 6 关'

# 5) 在备节点 / 指定 AM 地址上真跑第 6 关
AM=http://<备节点内网IP>:9093 sudo -E bash "$C"

# 6) 指向非默认的 Prometheus / NATS(例如本机端口被改过)
PROM=http://127.0.0.1:9090 NATS=http://127.0.0.1:8222 sudo -E bash "$C"

# 7) 只看第 5 关的提示(要主动测链路时照它做)
sudo bash "$C" | grep -A2 '第 5 关'
sudo python3 /opt/Cy/services/system.d/nfos/monitoring/sec-notify.py --selftest   # 应 ok:true, feishu:true

依赖与权限

项 内容
解释器 bash(set -uo pipefail,注意没有 -e)
外部命令 curl(带 -m 超时:varz 6s、其它 8s)、python3(内联脚本解析 JSON)、date、grep、hostname、sleep、sudo(sudo -n 只读打开库)、awk 未被使用
第三方模块 无(python3 只用标准库 sys / json / sqlite3)
root 建议用 root(脚本头部的用法就是 sudo bash …);第 3 关通过 sudo -n test -r "$DB" 与 sudo -n python3 - 读 nfarmsd 库。非 root 且 sudo -n 不可用时该关降级为 warn "本机没有 nfarmsd 库(备节点为常态)"
sudo -n 需要(第 3 关)。必须是免密——-n 在需要口令时会直接失败,脚本就退化到 warn 分支
读取 $PROM/api/v1/query、$NATS/varz、$NATS/subsz?subs=1、$NATS/jsz?streams=1、$AM/api/v2/alerts?active=true、$DB(SQLite messages 表)
写入 不写任何文件(输出全在 stdout,带 ANSI 颜色)
网络 仅本机回环 HTTP(Prometheus 9090 / NATS 8222 / Alertmanager 9093)

幂等与失败语义

  • 只读、幂等:无状态文件、无写入、重复执行结果一致(第 2/5 关有 sleep,会让脚本跑 9 秒以上)。
  • 每关独立降级:某一关取不到数据只会 warn(例如 warn "NATS varz 不可达" 之外还有 warn "取不到自检指标"、warn "取不到 adapter 计数"),不会中断后续关卡。
  • 一半状态的风险:没有(不写任何东西)。
  • 退出码:恒为 0。脚本没有 exit 语句,也没有 set -e;最后一条执行的是 printf ' PASS=%d FAIL=%d WARN=%d\n' …(成功)。因此不能用退出码判断链路是否可信任—— 必须解析输出里的结论行与 PASS=/FAIL=/WARN= 计数。

常见错误与排查

现象 代码里的对应分支 排查步骤
FAIL NATS varz 不可达 <NATS> —— 总线整体不可用,所有依赖它的功能都在降级 第 1 关 bad 分支 curl -s -m 6 http://127.0.0.1:8222/varz;sudo podman ps \| grep nats;sudo systemctl status nfos-nats.service --no-pager
FAIL 取不到订阅级投递计数 subsz?subs=1 不可读,无法判断有没有消费者 第 2 关的 bad 分支 直接 curl -s "http://127.0.0.1:8222/subsz?subs=1" \| head -c 400;被防火墙/绑定地址限制时改 NATS=
WARN 6 秒内投递计数未增长 累计投递 N(说明这段时间没有新消息,不等于链路坏;可发一条探针再测) 第 2 关的 warn 分支 这不是坏。按提示主动造一条:sudo python3 …/monitoring/sec-notify.py --selftest,再跑一次 check-pipeline.sh
!! 有消息但无消费者(积压风险) 第 2 关 JetStream 段 说明有流但 consumer_count=0;检查消费方进程(nfarmsd worker 等)是否在跑
WARN 本机没有 nfarmsd 库(备节点为常态) 第 3 关 sudo -n test -r "$DB" 为假 备节点上这是预期;在主节点上出现则说明没有 sudo -n 免密或库路径不对。手工验证:sudo -n test -r /opt/Cy/services/system.d/nfarmsd/data/nfarmsd.db && echo ok
VERDICT=BACKLOG 第 3 关判据 pending+queued+failed > 100 看它打印的「状态分布」与「最老未投递」时间;结合第 2 关判断是「没人消费」还是「消费慢」
FAIL 自检失败 success=<值> —— 链路有问题,须立刻查 nfos-alert-selftest 日志 第 4 关 bad 分支 sudo systemctl status nfos-alert-selftest.service --no-pager、journalctl -u nfos-alert-selftest.service -n 50。注意:timers.md 第 8.1 节记录该单元在线上未安装——此时本关会落到「取不到自检指标」的 warn
WARN 自检超过 36h 未跑 <age>s 前(timer 是否停摆?) 第 4 关的 case "$age" 分支 systemctl list-timers 'nfos-alert-selftest*' --all
WARN 取不到自检指标 Prometheus 未采集 nfos_alert_selftest_*(自检可能从未运行) 第 4 关的 else warn 分支 curl -s 'http://127.0.0.1:9090/api/v1/query?query=nfos_alert_selftest_success';对照 timers.md 第 8.1 节
WARN 取不到 adapter 计数 nfos_alert_adapter_webhooks_total 无数据 第 5 关的 else warn 分支 adapter 可能没在跑(curl -s http://127.0.0.1:9095/metrics),见 alert-adapter.md
第 5 关「adapter 计数不变」被当成故障 脚本自带提示:adapter 计数不变只说明"这段时间没有新告警",不代表链路坏;要主动测用… 按提示跑 sec-notify.py --selftest
第 6 关打印 SKIP if [ "$AM" = "http://127.0.0.1:9093" ] && ! hostname \| grep -q "Tencent" 在主节点这是设计行为;要真跑:登备节点执行,或 AM=http://<备节点内网IP>:9093 sudo -E bash check-pipeline.sh(若备节点也只绑回环,就只能登备节点本机跑)
第 6 关 VERDICT=UNREADABLE 活动告警里有条目缺 labels.alertname 输出会列出前 3 条的 since= 与 labels=;说明有告警规则没写 alertname,需要修规则
--json 传了但输出还是彩色文本 源码未实现(见「命令与参数」) 需要机器可读结果时自行解析 PASS=/FAIL=/WARN= 行与结论行

回滚方式

本脚本完全只读——不发告警、不写文件、不改库、不动 unit。因此:

项 结论
系统状态 无需回滚(脚本无副作用)
脚本本体 若改坏了,用版本化备份覆盖回去即可
唯一的「间接副作用」 第 5 关的提示会让值班去跑 sec-notify.py --selftest,那个会真发一条通知(属 sec-notify.md 的范畴,不是本脚本的副作用)
D=/opt/Cy/services/system.d/nfos
BK=$D/state/unit-backups

# 1) 改之前先备份
TS=$(date +%Y%m%d-%H%M%S)
sudo install -d -m 755 "$BK"
sudo cp -a "$D/check-pipeline.sh" "$BK/check-pipeline.sh.bak-$TS"

# 2) 回滚
sudo cp -a "$BK/check-pipeline.sh.bak-<时间戳>" "$D/check-pipeline.sh"
sudo chown root:root "$D/check-pipeline.sh"
sudo chmod 755       "$D/check-pipeline.sh"

# 3) 验收(只读,跑一遍即可)
sudo bash "$D/check-pipeline.sh" | tail -5
ls -l /etc/systemd/system/ | grep '\.bak'    # 必须为空(否则触发 PS-002)

备份落点铁律:脚本/单元的任何备份放 /opt/Cy/services/system.d/nfos/state/unit-backups/ (命名 <文件名>.bak-YYYYMMDD-HHMMSS)或仓库侧 /opt/Cy/nfarms/.backup/。 绝不能放在 /etc/systemd/system/ —— 会触发 PS-002 完整性告警。见 timers.md 第 11 节。

⚠️ 诚实标注:脚本头部的用法行写了 [--json],但代码里没有实现任何参数解析。 若你依赖 --json 做自动化,请先按上面的「常见错误与排查」最后一行自行解析,或补实现 (补实现属于改脚本,需走 lib-ops.md 之外的常规改名/备份流程)。

相关 CHANGELOG

CHANGELOG 章节标题
docs/CHANGELOG-2026-09-26.md §14.2 补部署 check-pipeline.sh(页面引用了它,但它当时不在节点上)
docs/CHANGELOG-2026-09-26.md §13.6 最后一公里:日志审计的告警改「近期口径」(SL-004 不再复报)(同期的链路可判读性治理)
docs/CHANGELOG-2026-09-26.md §6.1 真实链路实测(生产主节点)
docs/CHANGELOG-2026-09-26.md §9.7 真实链路联合验收(主节点,合成数据已清理)

本脚本被 digest.md 作为汇总通报正文末尾的自证链路命令引用; 定时器与单元全景见 timers.md;adapter 计数见 alert-adapter.md。