绘制 Grafana 指标图表
概述
渲染一个深色主题、Grafana 风格的 PNG 折线图,从 PromQL 查询获取数据,并通过 Grafana 数据源代理 获取(因此无需直接访问 Prometheus 网络,只需 Grafana SA 令牌即可)。可重复且可脚本化,不像手动截图。可以将结果直接附加到 Jira 问题。
如果面板已经显示了该图表,请优先使用快照。 Grafana Cloud 堆栈提供服务器端渲染,因此 gcx dashboards snapshot 会返回真实面板的 PNG。本技能用于解决快照无法提供的内容:跨查询或时间范围的并排比较(这些比较未编码在任何单个面板中),以及没有 Image Renderer 插件的自托管实例,在这些实例中快照会报错。
预检
此处的每个 Grafana 调用都通过 gcx 进行。每个会话检查一次:
command -v gcx >/dev/null || { echo "install: brew install gcx"; exit 1; }
gcx config check || { echo "not ready: gcx login"; exit 1; }
在绘制之前,请确保查询正确。 本技能会渲染你提供的任何 PromQL 查询,错误的查询在图表中的后果比在仪表盘中更严重:PNG 会作为冻结的证据留在工单中,没有人会重放,而且它看起来很正式。关于查询本身所需的背景知识: 请参考 grafana-dashboards 技能中的 PromQL 要点、聚合陷阱 和 读取分布和尾部 部分。在比较图表中容易出错的地方:聚合应在 rate 之后而非之前;在滚动更新导致每个 Pod 有两个序列时,使用 max by (...) 包裹两侧;不要对以 2 为底的指数直方图使用 histogram_quantile;为每个子查询提供显式步长。
使用时机
- 比较 2 个或多个序列:版本 A 与 B,金丝雀与对照组,变更前与变更后。
- 向 Jira 工单、MR 或事件报告附加证据。
- Grafana 面板渲染失败(
gcx dashboards snapshot报错:该自托管环境没有 Image Renderer)。
不适合:交互式探索(请使用 Grafana)、单一值/表格数据、或对 Grafana Cloud 上现有面板的截图(gcx dashboards snapshot)。
设置(一次性)
需要 matplotlib。如果缺失,请使用隔离的 venv(不要污染系统 Python):
python3 -m venv ${CLAUDE_SKILL_DIR}/.venv
${CLAUDE_SKILL_DIR}/.venv/bin/pip install -q matplotlib
快速参考
| 需要 | 标志 |
|---|---|
| 哪个数据源 | --datasource-uid(通过 gcx datasources list 或面板查询找到) |
| 查询 | --expr 'PromQL' |
| 时间窗口 | --start now-6h --end now --step 60 |
| 序列标签来源 | --legend-key instance(默认;任何指标标签) |
| 更美观的名称 | --rename '{"raw-value":"Nice Name"}' |
| 每个序列的均值/最小值/最大值 | 保存后打印到标准输出(无需额外查询) |
| 峰值标注 | --annotate-max "text" |
| 哪个 Grafana | --grafana-url https://<host>:使用服务器地址与该主机匹配的 gcx 上下文(默认:gcx 的当前上下文) |
| 哪个 gcx 上下文 | --gcx-context <name>(默认 $GCX_CONTEXT);对于没有 URL 的实例,从 gcx config list-contexts 中选择 |
| 附加到 Jira | --attach-jira ABC-123(请参阅下面的环境变量和工作流程中的审批门控) |
环境
没有任何硬编码:没有主机,没有地址。请在您的 shell 配置文件中设置这些。
| 变量 | 所需环境 | 备注 |
|---|---|---|
GRAFANA_URL + GRAFANA_TOKEN | 静态令牌 | 令牌仅发送到 GRAFANA_URL 的主机(然后是 GRAFANA_SERVICE_ACCOUNT_TOKEN,GTOK) |
GCX_CONTEXT | gcx | 强制使用的 gcx 上下文,与 --gcx-context 相同 |
JIRA_API_TOKEN JIRA_EMAIL JIRA_BASE | 仅 --attach-jira | 三个都需要,无默认值 |
分辨率顺序,在所有 Grafana 技能中相同:--token;--gcx-context / $GCX_CONTEXT;--grafana-url($GRAFANA_URL 对应主机的环境令牌,否则是服务于该主机的 gcx 上下文,否则退出并运行 gcx login);gcx 的当前上下文;当 gcx 缺失时的环境令牌。通过 gcx,其自身的 OAuth 刷新适用,此脚本从不自行读取或缓存凭证。分辨率器有意在每个技能中重复,以便任何一个技能可以单独安装。
工作流程
- 从仪表盘获取数据源 UID 和 PromQL(
gcx api /api/dashboards/uid/<uid>,读取panels[*].targets[*].expr)或自己编写查询。 - 运行脚本(通过 venv python)。颜色按序列顺序从 Grafana 调色板自动分配;图例显示每个序列的均值/最大值。
- 在分享之前,先肉眼检查生成的 PNG。
- 可选地使用
--attach-jira附加到 Jira。这是一个对全团队可见的追踪器写入操作:请先遵循ticket-conventions技能的审批门控(显示工单 ID 和 PNG,等待明确的同意)。上传失败会以非零状态退出。
示例(典型:HAProxy 3.2 与 2.7 节点内存对比)
VENV=${CLAUDE_SKILL_DIR}/.venv/bin/python
SKILL=${CLAUDE_SKILL_DIR}
export JIRA_API_TOKEN=$(zsh -l -c 'echo $JIRA_API_TOKEN') # 仅在附加时使用
export [email protected] JIRA_BASE=https://example.atlassian.net
DS_UID=abc123XYZ # 来自 `gcx datasources list`
$VENV $SKILL/plot_grafana.py \
--datasource-uid $DS_UID \
--expr '(1 - (node_memory_MemAvailable_bytes{instance=~"lb-edge-dc1-(1|4)"} / node_memory_MemTotal_bytes{instance=~"lb-edge-dc1-(1|4)"})) * 100' \
--start now-6h --step 60 \
--title "lb-edge - node memory used % · 3.2 vs 2.7 (dc1, last 6h)" \
--ylabel "Memory used %" --ymin 0 --ymax 45 \
--rename '{"lb-edge-dc1-1":"HAProxy 3.2.20 (dc1-1)","lb-edge-dc1-4":"HAProxy 2.7.11 (dc1-4, control)"}' \
--annotate-max "reload: old+new worker coexist → node mem ~2x" \
--grafana-url https://grafana.example.com \
--out ./mem-3.2-vs-2.7.png \
--attach-jira ABC-123
常见错误
- 基于未经检查的查询绘制的图表 → 请参阅上面的 PromQL 指引;一个看似合理但错误的值是故障模式,而不是错误。
- 来自代理的 401 → 对于静态令牌,它不属于该主机;通过 gcx,刷新令牌已过期(大约每月一次),请运行
gcx login <context>。 - 使用系统
python3→ModuleNotFoundError: matplotlib。请使用 venv 中的 python。 - 空线/单条平坦线而预期多条 →
--expr正则表达式匹配了单个序列;请放宽标签匹配器。 - 序列未命名/全是 "series" →
--legend-key指向的标签指标没有该标签;请选择一个存在的标签(检查原始查询结果)。 - 图例名称中包含
:9100(或任何:port) → 从导出的序列名称中自动移除,因此--rename的目标是干净的主机名(例如lb-edge-dc1-1,而不是lb-edge-dc1-1:9100)。无需列出两种形式。 - 相对时间接受一个数字和一个单位,s/m/h/d(
now-90m,now-6h,now-2d)。不支持复合时间(now-1h30m)、周(now-1w)和舍入(now/d):请转换它们(now-90m,now-7d)。