RUNBOOK — 环境主机监控(env-monitor-deploy)¶
本文件回答「这个服务平时怎么用、怎么查、怎么救」,面向接手值班的人。 本次变更的过程与回滚见同目录
SOP.md;两者不重复写同一件事。
目录¶
1. 服务概览¶
干什么用的:把集群外的 14 台主机(Slurm / Lustre / LSF / HPC / FlexLM)的主机层 指标采集进本环境的 Prometheus,并提供统一大盘与邮件告警。
由哪些组件构成
| 组件 | 位置 | 作用 |
|---|---|---|
node-exporter(包)/prometheus-node-exporter.service(单元) |
14 台目标主机,systemd 单元监听 :9100 |
暴露主机层指标 |
领域采集器 env-<领域>-metrics(Slurm / Lustre / LSF / FlexLM) |
目标机 /usr/local/bin/ + /etc/systemd/system/env-<领域>-metrics.{service,timer} |
每 60s 采一次领域指标,写 agent.textfile_dir 下的 <领域>.prom,由 node-exporter 的 textfile collector 顺带暴露(不新增端口) |
| systemd drop-in | 目标机 /etc/systemd/system/prometheus-node-exporter.service.d/10-env-monitor.conf |
固定采集器开关与端口 |
| Service / Endpoints / ServiceMonitor | monitoring 命名空间 external-node-exporters |
静态接入 Prometheus 抓取 |
| PrometheusRule | monitoring/ 下 env-host-baseline-alerts、env-cluster-baseline-alerts、env-slurm-alerts、env-lsf-alerts、env-domain-alerts |
主机基础告警 + 集群侧告警 + 领域告警(Slurm 与 LSF 各自成组;Lustre 与 FlexLM 合并为 env-domain-alerts) |
| ConfigMap(Grafana 大盘) | monitoring/ 下 env-monitor-overview、env-slurm-dashboard、env-lustre-dashboard、env-lsf-dashboard、env-flexlm-dashboard、node-exporter-full-1860 |
环境总览 + 四个角色详情盘 + 社区单机盘(sidecar 自动导入) |
| Alertmanager 路由 | monitoring 命名空间,helm release kube-prometheus-stack |
主机告警分级邮件 |
依赖谁:kube-prometheus-stack(Prometheus / Alertmanager / Grafana / Operator); 目标机可达的 EPEL 仓库(仅装包时需要);操作机的 SSH 免密。
异常时的业务影响:
- 采集中断 → 只是「看不见」,不影响被监控主机上的业务;
- 目标机
node-exporter是只读采集进程,不参与 Slurm/Lustre/LSF 的任何调度或数据路径; - 唯一的写操作是防火墙放行与 systemd 单元,回滚即恢复。
重要性等级:中(可观测性组件;失效不直接中断业务,但会导致故障不可见)。
2. 接入与凭据¶
| 项 | 值 |
|---|---|
| 目标机 SSH | root@192.168.31.16 ~ .29(免密,仅操作机 192.168.31.249 可用) |
| 指标端口 | 9100/tcp,仅对 10.244.0.0/16(Calico Pod 网段)放行 |
| Prometheus 查询 | kubectl --kubeconfig /home/rain/.kube/config exec -n monitoring <prom-pod> -c prometheus -- wget -qO- --post-data='query=<expr>' http://localhost:9090/api/v1/query |
| Grafana | https://grafana.rainchen.top(管理员口令见下) |
| 工程配置 | config/env-monitor.yaml |
| 日志 | logs/env-monitor-deploy.log(按日切割,保留 30 天) |
| 报告 | reports/precheck-*.md、reports/drift-*.md |
凭据存放与取用
| 用途 | 位置 | 取用方式 |
|---|---|---|
| Grafana 管理员 | ~/.secrets/prod.env 的 PROD_GRAFANA_ADMIN_USER / PROD_GRAFANA_ADMIN_PASSWORD |
set -a; . "$HOME/.secrets/prod.env"; set +a |
| Alertmanager SMTP 授权码 | ~/.secrets/dell-r620-monitor.env 的 DELL_R620_SMTP_AUTH_PASSWORD |
由实现体运行时读取,不要手工 cat |
红线:~/.secrets/ 下文件权限 600,禁止 Read/cat 其内容,
只允许在命令内 source 或引用变量;非交互 shell 必须显式 source。
客户端接入示例(手工核对某台机器的原始指标):
timeout 10 curl -s http://192.168.31.16:9100/metrics | grep -E '^node_load1|^node_boot_time_seconds'
3. 日常速查表¶
cd /home/rain/Documents/k8s_ins/env-monitor-deploy
# 1. 链路体检(只读):抓取目标是否全 up
.venv/bin/python main.py verify
# 2. 漂移检测(只读):实机是否偏离期望态(0 无漂移 / 1 有漂移 / 2 检测失败)
.venv/bin/python main.py drift
# 3. 单台三要素快检(以 .16 为例)
ssh root@192.168.31.16 "systemctl is-active prometheus-node-exporter; systemctl is-enabled prometheus-node-exporter; ss -lntH | grep ':9100'"
# 4. 单台查看服务日志
ssh root@192.168.31.16 "journalctl -u prometheus-node-exporter -n 50 --no-pager"
# 5. 查看防火墙是否已放行
ssh root@192.168.31.16 "firewall-cmd --list-rich-rules | grep 9100"
# 6. Prometheus 里的目标状态
kubectl --kubeconfig /home/rain/.kube/config exec -n monitoring \
$(kubectl --kubeconfig /home/rain/.kube/config get pods -n monitoring -l app.kubernetes.io/name=prometheus -o jsonpath='{.items[0].metadata.name}') \
-c prometheus -- wget -qO- --post-data='query=up{job=~"external-node-exporters.*"} == 0' \
http://localhost:9090/api/v1/query
# 7. 当前 firing 的主机告警
kubectl --kubeconfig /home/rain/.kube/config exec -n monitoring \
$(kubectl --kubeconfig /home/rain/.kube/config get pods -n monitoring -l app.kubernetes.io/name=prometheus -o jsonpath='{.items[0].metadata.name}') \
-c prometheus -- wget -qO- --post-data='query=ALERTS{alertstate=\"firing\", category=\"host\"}' \
http://localhost:9090/api/v1/query
# 8. 重跑收敛(幂等;已达标主机会被跳过)
.venv/bin/python main.py deploy
# 9. 预检查(只读,含抓取来源推导)
.venv/bin/python main.py precheck
# 10. 忽略预检缓存,强制重跑预检
.venv/bin/python main.py deploy --refresh-precheck
关于预检与复用:deploy / verify / drift 都要先过预检(门禁)。预检结论缓存在
reports/.precheck-cache.json,默认 300 秒内复用;缓存指纹=配置+全部实现体源码
的 sha256,任一改动自动失效。一轮预检约 11 秒,命中缓存约 0.3 秒。
把 ops.precheck_cache_ttl_seconds 设为 0 可永久关闭复用。
4. 日常操作¶
状态检查(完整)
cd /home/rain/Documents/k8s_ins/env-monitor-deploy
.venv/bin/python main.py verify # 期望:全部 14 台五要素通过
单台启停(高危:启停服务)
ssh root@192.168.31.16 "systemctl restart prometheus-node-exporter" # 重启
ssh root@192.168.31.16 "systemctl stop prometheus-node-exporter" # 停止(该台 target 会 down)
ssh root@192.168.31.16 "systemctl start prometheus-node-exporter"
停止后 Prometheus 会在 5 分钟后触发 EnvHostDown(critical)并发邮件。
新增一台主机:改 config/env-monitor.yaml 的 nodes 段加一行 →
precheck → drift → deploy。新主机会被装包/放行,已有主机全部跳过。
移除一台主机:从 nodes 段删除 → precheck → deploy(清单会重渲染,该端点消失)→
再手工 ssh root@<ip> "systemctl disable --now prometheus-node-exporter && dnf remove -y node-exporter"。
看日志
tail -n 100 logs/env-monitor-deploy.log # 实现体日志
ssh root@192.168.31.16 "journalctl -u prometheus-node-exporter --since '30 min ago'"
大盘与日志互跳(2026-10-02 新增)¶
5 个环境大盘(总览 / Slurm / Lustre / LSF / FlexLM)右上角均有「查看日志(Kibana)」链接, 跳转到 EFK 的日志分域视图。
- 链接声明在精简源
dashboards/*.yaml的顶层extra.links,由生成器透传到大盘 JSON。 改链接请改精简源并重跑python main.py build-dashboards,勿手改manifests/产物。 ⚠overview.yaml原本已有顶层extra(version),新增键必须合并进同一个extra—— YAML 同名顶层键后写覆盖先写且不报错。 - 为支撑互跳,14 台集群外主机的指标新增
hostname标签(裸主机名,无端口), 与日志侧host.name同口径;由modules/k8s_ingest.py的 relabel 逐台生成。 验证:count(up{job="external-node-exporters", hostname!=""})应为14。 - 动态跳转(第二阶段,2026-10-02):总览大盘「CPU / 内存 / 根分区使用率 Top15」悬停柱形会出现
「查看该主机日志(Kibana)」,点击按该主机
host.name打开 Kibana 分域视图。 data link 写在精简源对应面板的fieldConfig.defaults.links,用${__field.labels.hostname}取值; ⚠ 被聚合掉hostname的查询(如 CPU 面板原为avg by (instance, cluster))须先补回该标签, 否则链接取不到值。 - 详见
log-metrics-crosslink-20261002/SOP.md§9。
5. 配置管理¶
配置项位置:config/env-monitor.yaml(唯一真源,含全部注释)。关键段:
| 段 | 管什么 |
|---|---|
nodes |
受管主机(键名=集群名→cluster 标签,name→instance,role→role 标签) |
agent |
包名、systemd 单元、drop-in 路径、collector 开关、端口 |
firewall |
是否放行、源网段(auto = 由 Calico IPPool 推导)、端口 |
kubernetes |
kubeconfig、命名空间、release、本地 chart 包、清单模板、大盘制品 |
alertmanager |
收件人、SMTP、抑制规则 |
ops |
批次规模、并发、失败阈值、各类超时 |
precheck |
期望 OS 主版本、磁盘/时钟阈值、可跳过的检查项 |
修改流程:改配置 → .venv/bin/python main.py precheck(先看结论)→
.venv/bin/python main.py deploy(幂等收敛)。
大盘(生成物):5 个环境大盘的唯一真源是 dashboards/*.yaml 精简源,
manifests/ 里的大盘 YAML 由 main.py build-dashboards 生成,不得手改:
$EDITOR dashboards/<name>.yaml # 只写差异行(不写 id / gridPos / datasource / refId)
.venv/bin/python main.py build-dashboards # 生成(幂等:内容未变则报「未变」)
.venv/bin/python main.py build-dashboards --check # 只校验精简源与 manifests 是否一致,不写文件
.venv/bin/python main.py deploy # 下发
pre_check 会校验二者一致性,手改 manifests 会被 BLOCK。详见 docs/SOP.md 附录 G.9。
生效方式:
- 主机侧参数(collector、端口)→ 写 drop-in 后
systemctl daemon-reload && restart; - 清单类(Endpoints/规则/大盘)→
kubectl apply; - Alertmanager →
helm upgrade(必须带--no-hooks,理由见第五节末尾)。
易错点
nodes段改主机名会改变instance标签,历史看板与告警的instance引用会一起变; 且主机名必须是小写 RFC1123 label(它同时作为 Endpoints 的 hostname, 大写会被 K8s API 拒绝,实测Flexlm被拒);firewall.sources取auto时推导的是 K8s 节点 IP,不是 Pod 网段 (CaliconatOutgoing会把 Pod 出集群流量 SNAT 成节点 IP);- chart 包路径是相对工程根解析的,挪动
prometheus_deploy/charts/*.tgz会破坏升级; helm upgrade必须带现场 values 作基线(-f <现场 values> -f <overlay>)。 只传本工程 overlay 会让其余配置回落到 chart 默认值,触发全栈重建 —— 本次实施已实际发生并造成 P1 事故,见INCIDENT-20260930-monitoring-fullstack-rollout.md。实现体已加前置校验: 缺基线直接拒绝执行;- Alertmanager 运行配置在 Secret 里以
alertmanager.yaml.gz(gzip)存放, 不是alertmanager.yaml;取配置时需按 gzip 解压。
6. 备份与恢复¶
备份策略:变更前自动备份,位置 env-monitor-deploy/backup/,台账 backup/README.md。
| 对象 | 备份位置 |
|---|---|
| Alertmanager live values | backup/cluster/kube-prometheus-stack-values.live.yaml.bak.<时间戳> |
| 改前 Alertmanager 运行配置 | backup/cluster/alertmanager-config.yaml.bak.<时间戳> |
| 目标机 drop-in(若原本存在) | 目标机原位 /etc/systemd/system/prometheus-node-exporter.service.d/10-env-monitor.conf.bak.<时间戳> |
手工备份
cd /home/rain/Documents/k8s_ins/env-monitor-deploy
helm --kubeconfig /home/rain/.kube/config get values kube-prometheus-stack -n monitoring -o yaml \
> backup/cluster/kube-prometheus-stack-values.live.yaml.bak.$(date +%Y%m%d-%H%M%S)
ssh root@192.168.31.16 "cp -a /etc/systemd/system/prometheus-node-exporter.service.d/10-env-monitor.conf \
/etc/systemd/system/prometheus-node-exporter.service.d/10-env-monitor.conf.bak.$(date +%Y%m%d-%H%M%S)"
恢复:完整回滚步骤见 docs/SOP.md 第 7 章;单台 drop-in 恢复:
ssh root@192.168.31.16 "cp -a <备份文件> /etc/systemd/system/prometheus-node-exporter.service.d/10-env-monitor.conf && systemctl daemon-reload && systemctl restart prometheus-node-exporter"
恢复后验证:ss -lntH | grep ':9100' 有监听;.venv/bin/python main.py drift 退出码 0。
演练记录:截至本文成文,单台 drop-in 恢复路径尚未实战演练(首次变更前无备份可恢复);
下一次变更前建议在 .16 上演练一次。
7. 监控与告警¶
关键指标与查看入口
| 想确认 | 去哪 | 看什么 |
|---|---|---|
| 采集是否正常 | Grafana →「环境总览」 | 「在线主机」= 19、「失联主机」= 0 |
| 主机资源水位 | Grafana →「环境总览」→ 资源水位 | CPU/内存/根分区 Top15,>85% 标红 |
| 单机全量 | Grafana →「Node Exporter Full」 | 变量 node 选主机名(如 slurm-work-01) |
| Slurm 集群在干什么 | Grafana →「Slurm 集群」 | 分区/节点 CPU 与内存、作业队列、控制端与调度器健康 |
| Slurm 作业效率与卡点 | Grafana →「Slurm 集群」 | 运行时长 vs 时限、时长分布、近 1h 完成/失败作业、CPU 效率(TotalCPU/CPUTime) |
| Slurm 是否卡在 License | Grafana →「Slurm 集群」→ 跨域联动 | 运行作业数 vs FlexLM 许可占用、slurm_sched_blocked_on_licenses |
| 谁在用集群(项目维度) | Grafana →「Slurm 集群」→ 公平性与账户 | Account 的 RawUsage 排行、NormShares 与 FairShare |
| Lustre 存储健康 | Grafana →「Lustre 存储」→ 服务与采集健康 | 采集状态、客户端设备(mgc/lov/lmv/mdc/osc)、MDT/OST 挂载客户端数、驱逐趋势 |
| Lustre 容量与水位 | Grafana →「Lustre 存储」→ 容量与水位 | 客户端/OST/MDT 已用率、inode 使用率、grant 空间(含受阻 pending) |
| Lustre 元数据性能 | Grafana →「Lustre 存储」→ 元数据性能(MDS) | md_stats 操作速率与平均延迟、近 1h 操作分布 |
| Lustre 数据 IO | Grafana →「Lustre 存储」→ 数据 IO 性能(OSS) | OST 操作速率与平均延迟、近 1h 操作分布 |
| Lustre 磁盘层 IO | Grafana →「Lustre 存储」→ 磁盘层 IO | brw_stats 的 IO 大小/延迟/队列深度/块映射分布(空载时无数据,非故障) |
| Lustre 客户端体验 | Grafana →「Lustre 存储」→ 客户端体验 | llite 操作速率与端到端延迟、RPC 重传、客户端 grant 使用 |
| Lustre 与作业联动 | Grafana →「Lustre 存储」→ 跨域联动 | 存储用量 vs Slurm/LSF 作业数、客户端连接数 vs 许可占用 |
| LSF 集群与调度器健康 | Grafana →「LSF 调度」→ 集群与调度器健康 | 采集状态、集群/主控/mbatchd 状态、服务器状态、作业与用户、集群容量 |
| LSF 队列积压与等待 | Grafana →「LSF 调度」→ 队列压力与等待 | 各队列 PEND、Pending 原因 TopN、调度等待时延 |
| LSF 调度器与 License 阻塞 | Grafana →「LSF 调度」→ 调度器与 License 阻塞 | 估计队列与作业信息缓存、Pending 原因趋势、待调度资源需求 |
| LSF 作业效率 | Grafana →「LSF 调度」→ 作业运行与资源效率 | 运行时长分布、槽位/内存需求、近 1 小时吞吐与 Slot/Memory 利用率、周转与 hog/expansion 因子 |
| LSF 主机与负载 | Grafana →「LSF 调度」→ 主机与节点资源 | 主机状态表、r1m 负载、CPU 利用率、可用内存与临时空间 |
| LSF 公平性与用户 | Grafana →「LSF 调度」→ 公平性与用户 | 队列 FAIRSHARE 份额表、用户/组作业数与限额 |
| LSF 是否卡在 License | Grafana →「LSF 调度」→ 跨域联动 | LSF 运行作业数 vs FlexLM 许可占用、Lustre 容量 |
| 许可服务健康 | Grafana →「FlexLM 许可」→ 服务与采集健康 | lmgrd / vendor daemon 状态、角色(MASTER/BACKUP)、版本、采集耗时与解析健康 |
| 许可池容量与水位 | Grafana →「FlexLM 许可」→ 许可池容量与水位 | 发行总量 / 在用 / 空闲、各厂商利用率、Top 10 feature 利用率 |
| 谁在占用许可 | Grafana →「FlexLM 许可」→ 占用与持有者 | 占用用户数、Top 用户占用数、最久未释放的 feature(无 checkout 时为空属正常,非故障) |
| 许可被拒 | Grafana →「FlexLM 许可」→ 拒绝与争抢 | flexlm_license_denied_total 的 15 分钟增量与累计、利用率与拒绝联看 |
| 许可到期 | Grafana →「FlexLM 许可」→ 到期与版本 | 最近到期倒计时、各窗口内到期 feature 数、许可文件 present / served 一致性 |
| 许可结构 | Grafana →「FlexLM 许可」→ 许可结构 | 各厂商 counted / uncounted / node-locked 的 feature 数(Cadence 侧全部为 uncounted,无容量语义) |
| 许可与作业联动 | Grafana →「FlexLM 许可」→ 跨域联动 | 许可占用 vs Slurm/LSF 运行作业数、许可空闲 vs Slurm License 阻塞 |
| Slurm 调度等待 | Grafana →「Slurm 集群」 | Pending 当前等待、运行作业提交到启动的 avg/max |
| 原始查询 | Prometheus(prometheus.rainchen.top) |
up{job=~"external-node-exporters.*"} |
| 有 Pod 起不来 | Grafana → Alerting,或 Prometheus | kube_pod_status_phase{phase="Pending"} == 1 |
| 告警历史 | Grafana → Alerting,或 Alertmanager 界面 | firing / resolved |
需要重点盯的告警
| 告警 | 阈值 | 级别 | 含义 |
|---|---|---|---|
ExternalHostNodeExporterDown |
up == 0 持续 5m |
critical | 主机失联或采集进程挂了(注意:不是 EnvHostDown,抑制规则 inhibit_rules 以该名为准) |
| 磁盘可用空间 | <15% (15m) / <8% (5m) | warning / critical | 分区将写满 |
| inode 使用率 | <10% / <5% | warning / critical | 小文件写满 |
| 内存可用 | <10% (10m) | warning | 可能触发 OOM |
| CPU iowait / steal | 均值 >20% (15m) | warning | 存储或虚拟化层饱和 |
| systemd 单元失败 | state="failed" (10m) |
critical | 基础设施服务异常 |
| 时间同步偏移 | >0.05s (10m) / >0.5s | warning / critical | 指标时间戳失真 |
Env{Pod}StuckPending / EnvPodContainerCreateFailed |
10m | warning | 集群侧:Pod 卡在挂载/调度/Init 或镜像拉不到 |
EnvSlurmMetricsDown / EnvLustreMetricsDown / EnvLsfMetricsDown / EnvFlexlmMetricsDown |
对应 *_up == 0 持续 10m |
warning | 领域采集器失效(该领域面板会停更,见场景 7) |
EnvSlurmCtldDown |
slurm_ctld_up == 0 持续 5m |
critical | Slurm 控制端不可用,作业提交/调度受影响 |
EnvSlurmPartitionDown |
slurm_partition_avail == 0 15m |
warning | 分区被置 down |
EnvSlurmJobsBacklog |
slurm_jobs{state="PENDING"} > 20 30m |
warning | Slurm 作业积压 |
EnvSlurmPendingWaitHigh |
Pending 最大等待 >30m 持续 15m | warning | 排队作业等待时间过长 |
EnvSlurmDispatchWaitHigh |
平均调度等待 >10m 持续 15m | warning | 提交到开始运行的调度延迟过高 |
EnvLustreFsUsageHigh |
客户端已用率 >85% 持续 15m | warning | Lustre 文件系统容量告急(客户端视角) |
EnvLustreClientDeviceDown |
lustre_client_device_up == 0 持续 5m |
critical | 客户端设备(mgc/lov/lmv/mdc/osc)非 UP,作业读写该文件系统会直接报错 |
EnvLustreClientEviction |
15m 内有新增驱逐 | warning | 客户端被服务端驱逐(RPC 超时 / 锁冲突 / 网络抖动) |
EnvLustreClientRpcResend |
15m 内重传新增 >10 | warning | 客户端到 OSS 的 RPC 重传偏多 |
EnvLustreOstCapacityHigh / EnvLustreMdtCapacityHigh |
服务端已用率 >85% 持续 15m | warning | OST / MDT 服务端容量告急(OSS/MDS 自查,不依赖客户端) |
EnvLustreInodeUsageHigh |
inode 使用率 >85% 持续 15m | warning | 小文件写满 inode(EDA 场景常见) |
EnvLustreOstGrantPending |
lustre_ost_grant_bytes{type="pending"} > 0 持续 15m |
warning | 写入授权受阻,写入将变慢(容量耗尽前兆) |
EnvLustreMdtLatencyHigh / EnvLustreOstLatencyHigh |
平均延迟 >20 ms 持续 15m | warning | 元数据 / OST 操作延迟显著劣化(阈值偏保守,待有负载后校准) |
EnvLsfMetricsDown |
lsf_up == 0 持续 10m |
warning | LSF 采集失败(LSF 环境变量缺失、二进制不可用或查询失败) |
EnvLsfClusterUnavailable |
lsf_cluster_status == 0 持续 5m |
critical | LSF 集群状态非 ok |
EnvLsfMasterDown |
lsf_master_up == 0 持续 5m |
critical | LSF 主控不可达,全集群无法提交/调度 |
EnvLsfMbatchdDown |
lsf_mbatchd_up == 0 持续 5m |
critical | mbatchd 不可用,作业调度停止 |
EnvLsfHostAbnormal |
unreach/unavail/unusable 15m |
warning | LSF 主机不可用(closed_Full 不算异常) |
EnvLsfServersNotOk |
服务器 unreachable/unavailable > 0 持续 15m |
warning | 调度器侧有服务器不可达/不可用 |
EnvLsfQueueBacklog |
队列 PEND > 50 持续 30m | warning | LSF 单队列积压 |
EnvLsfJobsBacklog |
全局 PEND > 20 持续 30m | warning | LSF 全集群排队作业积压 |
EnvLsfPendingWaitHigh |
最长排队等待 > 30m 持续 15m | warning | 排队等待时间过长 |
EnvLsfDispatchWaitHigh |
平均调度等待 > 10m 持续 15m | warning | 提交到开始运行延迟过高 |
EnvLsfPendingReasonSpike |
单个 Pending 原因 > 10 持续 30m | info | 某类等待原因持续升高(先观察) |
EnvLsfJobExitedHigh |
窗口内异常退出作业 > 5 持续 30m | warning | 近 1 小时异常退出作业偏多 |
EnvFlexlmServerDown |
flexlm_server_up == 0 5m |
critical | 许可服务器不可用,EDA 工具会取不到许可 |
EnvFlexlmDaemonDown |
flexlm_vendor_daemon_up == 0 5m |
critical | vendor daemon 挂了,该厂商全部 feature 取不到许可(lmgrd 仍显示 UP,不能只看服务器状态) |
EnvFlexlmOutputParseFailed |
flexlm_parse_ok == 0 and flexlm_collect_success == 1 10m |
warning | lmstat 能跑但解析不出 feature,容量面会显示为空(与「确实没有 feature」表现相同) |
EnvFlexlmCheckoutParseFailed |
flexlm_checkout_parse_failed > 0 10m |
warning | 用户行格式与解析器不一致,此时「无占用」不可信,须抓样本修正解析器 |
EnvFlexlmDeniedLogUnavailable |
flexlm_license_denied_log_available == 0 15m |
warning | lmgrd 日志读不到,denied 恒为 0,「没有拒绝」与「统计不到」无法区分 |
EnvFlexlmCollectSlow |
flexlm_collect_duration_seconds > 30 10m |
warning | 采集耗时接近 60s 周期,指标可能更新不及时 |
EnvFlexlmLicenseExhausted |
in_use >= total 持续 10m |
warning | 许可用尽,新请求排队或被拒 |
EnvFlexlmLicenseDenied |
increase(flexlm_license_denied_total[15m]) > 0 持续 5m |
warning | 最近 15 分钟出现新的许可拒绝 |
EnvFlexlmLicenseExpiring |
flexlm_license_expiring_features{window="30d"} > 0 持续 1h |
warning | 有 feature 将在 30 天内到期,到期后该 feature 无法检出 |
EnvFlexlmLicenseFileUnreadable |
file_present=1 and file_readable=0 30m |
warning | 许可文件在盘上但解析不了,到期统计与结构缺失 |
Watchdog(心跳) |
恒定 firing,每 24h 一封 | info(P3) | 设计如此:收到它说明告警链路本身是活的;超过 24h 收不到才说明链路断了 |
8. 故障处置¶
场景 1:某台主机 target down(Grafana「失联主机」> 0)¶
症状:up{instance="slurm-work-01:9100"} == 0;收到 EnvHostDown 邮件。
诊断
# 1) 主机是否可达
timeout 6 ssh -o BatchMode=yes -o ConnectTimeout=3 root@192.168.31.18 hostname
# 2) 采集进程与端口
ssh root@192.168.31.18 "systemctl is-active prometheus-node-exporter; ss -lntH | grep ':9100'"
# 3) 服务日志
ssh root@192.168.31.18 "journalctl -u prometheus-node-exporter -n 50 --no-pager"
# 4) 防火墙规则是否还在
ssh root@192.168.31.18 "firewall-cmd --list-rich-rules | grep 9100"
处置
- 1) 失败 → 主机或网络问题,属主机侧故障,不在本工程范围;
- 2)
inactive/failed→ssh root@<ip> "systemctl restart prometheus-node-exporter"; 仍失败看 3) 的日志(bind: permission denied走场景 2); - 端口无监听但服务 active → 检查 drop-in 是否被改动:
ssh root@<ip> "systemctl cat prometheus-node-exporter", 与配置期望比对后重跑deploy; - 4) 规则缺失(例如 firewalld 被重载/重建)→
.venv/bin/python main.py deploy会补回。
验证:.venv/bin/python main.py verify 通过;Prometheus 中该 instance 的 up 回到 1(约 30s 内)。
场景 2:node-exporter 启动失败,日志报 bind: permission denied¶
症状:systemctl status prometheus-node-exporter 显示反复重启;journalctl 出现
listen tcp :9100: bind: permission denied。
诊断
ssh root@192.168.31.16 "getenforce; journalctl -u prometheus-node-exporter -n 30 --no-pager | grep -iE 'denied|avc'"
ssh root@192.168.31.16 "grep 'type=AVC' /var/log/audit/audit.log | tail -5"
处置(高危:改 SELinux 端口标签,全局生效)
实测结论(2026-09-30):本环境的 EPEL
node-exporter以User=prometheus运行, 未落入受限域,8 台Enforcing主机上均正常绑定 9100 —— 无需改端口标签。 以下步骤仅在该结论被推翻(换了包、换了策略)时使用。
先确认 9100 当前标签与实际占用:
ssh root@192.168.31.16 "semanage port -l 2>/dev/null | grep -w 9100 || echo 'semanage 不可用'"
- 若
semanage不可用,先装dnf install -y policycoreutils-python-utils; - 若需放行:
semanage port -m -t http_port_t -p tcp 9100(注意:9100 现属hplip_port_t, 改标签会影响 hplip;本环境未使用打印服务); - 改完
systemctl restart prometheus-node-exporter。
验证:端口监听 + main.py verify 通过。
若与本 SOP 第 5.3 步的实测结论不一致,请把新结论回写到 docs/SOP.md 与配置中。
场景 3:收不到告警邮件¶
症状:Grafana/Alertmanager 已显示 firing,但邮箱无信。
诊断
# 1) Alertmanager 运行配置里是否有我们的 receiver
kubectl --kubeconfig /home/rain/.kube/config get secret \
alertmanager-kube-prometheus-stack-alertmanager-generated -n monitoring \
-o jsonpath='{.data.alertmanager\.yaml}' | base64 -d | grep -A3 env-monitor-email
# 2) Alertmanager 是否在报发送失败
kubectl --kubeconfig /home/rain/.kube/config logs -n monitoring \
alertmanager-kube-prometheus-stack-alertmanager-0 --tail=80 | grep -iE 'smtp|error|notify'
处置
- 1) 无
env-monitor-email-*→ 路由未生效:.venv/bin/python main.py deploy重新应用 (含 helm upgrade,--no-hooks); - 2) 出现
535/authentication failed→ SMTP 授权码过期,更新~/.secrets/dell-r620-monitor.env中的DELL_R620_SMTP_AUTH_PASSWORD后重跑deploy; - 出现
connection refused/timeout→ 操作机能出网但 SMTP 被限,检查smtp.163.com:25连通性; - 告警是
info级 → 设计上不发信,去 Grafana 大盘看。
验证:邮箱收到主题形如 [主机P1 FIRING] EnvHostDown @ <instance> 的邮件。
场景 4:大盘无数据或变量为空¶
症状:打开「环境总览」所有面板 No data;或 cluster/role 变量下拉为空。
诊断
# 1) 指标是否有数据(先排除采集问题)
.venv/bin/python main.py verify
# 2) ConfigMap 是否存在且带 sidecar 标签
kubectl --kubeconfig /home/rain/.kube/config get configmap env-monitor-overview \
-n monitoring -o jsonpath='{.metadata.labels}'
# 3) Grafana sidecar 是否已导入
kubectl --kubeconfig /home/rain/.kube/config logs -n monitoring \
-l app.kubernetes.io/name=grafana -c grafana-sc-dashboard --tail=50 | grep -i overview
# 4) 大盘生成物与精简源是否一致(手改过 manifests 或改完精简源没重新生成时先查这条)
.venv/bin/python main.py build-dashboards --check
处置
- 1) 有主机 down → 先按场景 1 处理;
- 2) 标签缺
grafana_dashboard: "1"→ 重跑deploy修正; - 3) sidecar 报错/未导入 → 重启 Grafana Pod 触发重新扫描;
- 仍无数据 → 检查面板 datasource uid 是否为
prometheus(与 chart 默认一致)。
验证:变量下拉能列出 5 个集群、面板出现曲线。
场景 5:drift / deploy 报「状态探测失败(unknown)」¶
症状:命令直接中止并提示某些主机探测失败。
原因:这是刻意设计——三态探测里 unknown 代表「连不上/探不到」,
按规范必须中止,绝不当成「需要变更」去盲目改动。
处置
# 先确认是不是操作机 SSH 通道问题
timeout 6 ssh -o BatchMode=yes -o ConnectTimeout=3 root@192.168.31.16 hostname
ls -l /root/.ssh/id_* # 以 root 运行工程时,密钥取自 /root/.ssh
- SSH 不通 → 排查免密/密钥/网络;
- 只有个别主机失败 → 该主机可能正在重启或 sshd 异常,恢复后重跑;
- 确认是集群侧探测失败 → 检查
kubectl --kubeconfig <配置文件里的路径> version。
验证:.venv/bin/python main.py drift 退出码 0 或 1(1 = 有漂移但不致命)。
场景 6:Pod 长时间卡在 Init:0/1 或 Terminating¶
症状:收到 EnvPodStuckPending 邮件;Pod 卡在 Init:0/1(或 Terminating)超过 10 分钟;
事件里 kubelet 的最后一条是 operationExecutor.VerifyControllerAttachedVolume started,之后无进展。
重启 kubelet、清理孤儿 sandbox、强制删 Pod 重建均无效。
诊断(先按顺序排除;2026-09-30 的实例已逐项排除过,可直接复用这套清单)
# 1) 卷到底挂上了没有(挂上了说明不是存储问题)
ssh root@192.168.31.14 "journalctl -u kubelet --since '20 min ago' | grep -i 'MountDevice succeeded'"
# 2) 是不是镜像拉不到(本环境集群侧 ghcr.io 不可达,属已知约束)
ssh root@192.168.31.14 "journalctl -u kubelet --since '20 min ago' | grep -ciE 'ErrImagePull|ImagePullBackOff'"
# 3) 节点侧是否有该 Pod 的容器/sandbox 残留
ssh root@192.168.31.14 "crictl pods | grep <pod 名片段>"
# 4) 有无 NFS/挂载层内核报错
ssh root@192.168.31.14 "journalctl -k --since '20 min ago' | grep -iE 'nfs|rpc|stale'"
# 5) 磁盘/内存是否够
ssh root@192.168.31.14 "df -h /var/lib/containerd; free -g"
处置
- 首选:把 Pod 换到其它节点(2026-09-30 实测立即恢复),不要在单节点上反复
force delete / 重启 kubelet 耗时间;临时做法是给 Deployment/StatefulSet 加
nodeSelector指向健康节点; - 该现象 2026-09-30 在 k8s-node-02 出现,复现实验(新建 PVC + 挂载 + init 容器)
11 秒即通过、无脏状态残留,故判为当时的临时状态而非节点缺陷;
完整排除清单与预防建议见
node02-init-stuck-20260930/20260930-k8s-node-02-Pod卡Init故障报告.md; - Grafana 已用
manifests/06-grafana-node-pin.yaml钉在k8s-node-03,暂不要让它回到 node-02(解除条件见该故障报告第 8 节); - 不要在未确认节点侧无容器时对 PVC 动手(可能触发不可逆的卷删除)。
验证:Pod 进入 Running 且 READY 到位;kubectl get pvc -n monitoring 全部 Bound;
告警自动 resolved。
场景 7:某个领域的指标缺失或停更¶
症状:Grafana 的「Slurm 集群」/「Lustre 存储」/「LSF 调度」/「FlexLM 许可」某块面板显示
「无数据」;或收到 EnvSlurmMetricsDown / EnvLustreMetricsDown / EnvLsfMetricsDown /
EnvFlexlmMetricsDown 告警。
诊断(以 Lustre MDS .21 为例;其它领域把 lustre 换成 slurm / lsf / flexlm)
FlexLM 专有分支:本领域有四种「看着像没有数据」的情形,前三种是故障、后一种是正常,
先看自省指标再动手(.29 上执行):
ssh root@192.168.31.29 'grep -E "^flexlm_(up|collect_success|parse_ok|lmstat_rc|collect_duration|checkout_parse_failed|license_denied_log_available)" /var/lib/node_exporter/textfile/flexlm.prom'
parse_ok=0而collect_success=1→ 故障:lmstat 能跑但输出格式变了, 容量面为空是解析失败所致(对应EnvFlexlmOutputParseFailed);checkout_parse_failed>0→ 故障:用户行解析失败,「当前无占用」不可信 (对应EnvFlexlmCheckoutParseFailed),须抓lmstat -a的用户行样本修正解析器;license_denied_log_available=0→ 故障:lmgrd 日志读不到,denied 恒 0 属统计缺失, 不是「没有拒绝」;collect_duration接近 60 → 隐患:采集赶不上周期,指标会时断时续;- 正常:「占用与持有者」整组为空(当前无任何 checkout 时本就无数据); Cadence 厂商的容量类面板为空(该厂商 604 个 feature 全部是 uncounted,没有占用率概念)。
# 1) 采集器的单元状态(oneshot 服务跑完即退,inactive 属正常,关键看 timer)
ssh root@192.168.31.21 'systemctl status env-lustre-metrics.timer --no-pager | head -8'
# 预期:Active: active (waiting)
# 2) 最近一次执行的输出与报错
ssh root@192.168.31.21 'journalctl -u env-lustre-metrics.service -n 30 --no-pager'
# 3) 手工跑一次看采集内容(参数取自 unit 的 ExecStart)
ssh root@192.168.31.21 '/usr/local/bin/env-lustre-metrics --bin-dir=/usr/sbin --cluster=lustre --stdout'
# 预期:首行 lustre_up{cluster="lustre"} 1,其后为各指标
# 4) 指标文件是否生成、node-exporter 是否带出
ssh root@192.168.31.21 'ls -l /var/lib/node_exporter/textfile/'
ssh root@192.168.31.21 'curl -s localhost:9100/metrics | grep -c "^lustre_"'
# 预期:文件存在且非空;计数 > 0
处置
- 若第 3 步输出
lustre_up 0:该领域命令不可用(如客户端未挂载 Lustre、二进制路径变更、 NFS 上的 LSF 目录不可达)→ 核对config/env-monitor.yaml的collectors.<领域>.bin_dir与probe_cmd,必要时先跑precheck看门禁结论; - LSF 专有:若
lsf_up 0且journalctl -u env-lsf-metrics.service里出现lsb_init:或Unable to open file lsf.conf,说明 systemd 单元缺少 LSF 环境变量 → 核对config/env-monitor.yaml的collectors.lsf.extra_env(应含LSF_ENVDIR与LSF_SERVERDIR),改后重跑.venv/bin/python main.py deploy; 现场快速判断可执行ssh root@192.168.31.23 'systemctl cat env-lsf-metrics.service | grep Environment'; - Lustre 专有:本领域按节点角色自动裁剪采集段,排查时先确认角色 ——
lctl get_param -l 'mdt.*'(MDS)/'obdfilter.*'(OSS)/'llite.*'(客户端)是否存在对应参数; 另有两处看起来像故障、其实是正常的现象: ① 「磁盘层 IO」(brw_stats)在空载时本来就没有数据行,.22上没有lustre_ost_disk_io_ios属正常,有作业负载后才会出现; ② 「客户端体验」与「容量」类指标只在挂载了 Lustre 的节点(.28hpc-submit)上产生, 且其cluster标签是hpc(由 ServiceMonitor relabel 按节点所属集群生成,会覆盖指标文件 里的同名值)—— Lustre 大盘已对这两类面板取消集群过滤,若手工改过大盘把过滤加回去, 选中lustre时它们会整片消失; - 若 journalctl 报
unrecognized arguments:目标机上的脚本版本旧于配置(脚本接口变更后未更新) → 重跑.venv/bin/python main.py deploy,脚本 sha256 变化会自动触发重新下发; - 若指标文件正常但 Prometheus 无数据 → 按场景 1 查抓取链路(
up{instance="<name>:9100"}); - 若只是某一块面板空:先确认该指标名是否真实存在(
curl -s localhost:9100/metrics | grep '^lustre_'), 再看大盘查询表达式是否写错标签。
验证:curl -s localhost:9100/metrics | grep '^lustre_' 有输出,且 Grafana 对应面板恢复。
9. 安全与权限¶
账号与权限模型
| 范围 | 账号 | 权限 |
|---|---|---|
| 目标主机 | root(SSH 免密,仅操作机可用) |
装包、systemd、firewalld、SELinux |
| K8s | 配置中 kubernetes.kubeconfig 指向的凭证 |
monitoring 命名空间读写 + 集群级读 CRD |
| Grafana/Alertmanager | 见第 2 节凭据表 | — |
端口与访问控制:目标机仅新增 9100/tcp,且限定源 10.244.0.0/16;
node-exporter 以 cmdline collector 关闭运行,不暴露进程命令行。
证书/密钥:本工程不签发证书;helm 与 kubectl 使用既有 kubeconfig;
SMTP 授权码轮换步骤:更新 ~/.secrets/dell-r620-monitor.env → 重跑
.venv/bin/python main.py deploy(会重新渲染并 helm upgrade)。
风险变更标记:删除文件、覆盖配置、启停服务属高危动作, 实现体会在日志中以「【高危】」前缀醒目标记,并在 precheck 报告里列出。
10. 容量与性能¶
当前水位基线(2026-09-29 实测)
| 项 | 数值 |
|---|---|
| Prometheus head series | 76,135 |
| 采样速率 | 2,680 samples/s |
| TSDB 占用 | 4.02 GiB / PVC 100 GiB |
| Prometheus Pod 内存 | 634 Mi(request 2Gi / limit 4Gi) |
| 保留期 | 15 天 / retentionSize 90 GiB |
| 本次新增预估 | +15~22%(约 +1.1~1.7 万 series、+400~600 samples/s) |
扩容触发条件:head series 超过 13 万、或 Prometheus Pod 内存持续 >3 GiB、
或 TSDB 占用超过 70 GiB 时,按顺序考虑:① 提高 scrape_interval;
② 增加 recording rules 降基数;③ 扩 PVC / 调 retentionSize。
扩容操作:改 prometheus_deploy/values.yaml 后按该工程 SOP 执行
(不在本工程范围)。
已知瓶颈:Prometheus 单副本、TSDB 落在 nfs-csi-hdd(宿主 Dell R620),
与宿主 NFS 属同源故障域——宿主故障会同时影响被监控对象与监控数据。
11. 附录¶
术语
| 术语 | 说明 |
|---|---|
| USE | Utilization / Saturation / Errors,主机层分析框架 |
cluster / role |
本工程给目标主机打的标签,值来自配置 nodes 段的键名与 role 字段 |
| 三态探测 | done(已达标,跳过)/ pending(需变更)/ unknown(探不到,中止) |
| 漂移 | 实机与配置期望态之间的差异,用 main.py drift 查看 |
相关文档与交付物
| 文档 | 用途 |
|---|---|
docs/SOP.md |
本次变更的实施、验证与回滚 |
docs/RUNBOOK.md |
本文件 |
config/env-monitor.yaml |
配置唯一真源 |
backup/README.md |
备份台账 |
reports/ |
预检查与漂移报告 |
utils/probe/ |
变更前的前置只读实测脚本 |
修订记录
| 日期 | 修改人 | 变更内容 |
|---|---|---|
| 2026-09-29 | rain | 首版。随 P0(14 台非 K8s 主机接入)产出 |
来源:
/home/rain/Documents/k8s_ins/env-monitor-deploy/docs/RUNBOOK.md| 最后更新:2026-10-02 12:43(由build/sync.py同步,请勿在站点侧修改)