Graphite 1.1.10 常用 CLI 命令与工具详解

whisper 工具族逐条实测(info/fetch/create/update/resize/merge 与 fill 覆盖补空语义对比/diff/set-aggregation)、carbon-cache 管理与 2003/2004/7002 三协议客户端、statsd admin 8126、render URL API 与常用函数实测回显全收录。

GraphiteGraphite监控2026/10/20

Graphite 1.1.10 常用 CLI 命令与工具详解

目标:把 Graphite 1.1.10 的运维命令面讲全——whisper 工具族(查看/创建/更新/迁移/合并/校验)、carbon 守护进程管理客户端与三种接入协议、statsd 管理接口、graphite-web 的 render URL API 与指标发现 API。全部命令在 Ubuntu 22.04.4 + graphiteapp/graphite-statsd 1.1.10-5 环境逐条实测,输出为实测回显。

一、环境信息

  • 操作系统:Ubuntu 22.04.4 LTS
  • Graphite:graphite-web / carbon / whisper 1.1.10(一体化镜像 graphiteapp/graphite-statsd:1.1.10-5,工具位于容器内 /opt/graphite/bin/,Python 虚拟环境 /opt/graphite/)
  • 指标数据目录:/opt/graphite/storage/whisper/(宿主 bind mount)
  • 服务端口:carbon 明文 2003、pickle 2004、carbonlink 7002、carbon-aggregator 2023/2024、statsd UDP 8125 / 管理 TCP 8126、Web 80(nginx)/8080(gunicorn)
  • 部署方式与端口映射见《Graphite 1.1.10 Docker Compose 部署指南》

二、工具概览

/opt/graphite/bin/ 下与运维直接相关的工具清单:

工具用途
whisper-info.py查看 .wsp 文件元数据与归档结构
whisper-fetch.py从 .wsp 导出数据点
whisper-create.py创建 .wsp(可 --estimate 估算体积)
whisper-update.py手工写入数据点
whisper-resize.py调整既有文件保留策略
whisper-dump.py十六进制级导出文件结构
whisper-merge.py / whisper-fill.py双文件数据合并(覆盖式 / 补空式)
whisper-diff.py比较两文件数据差异
whisper-set-aggregation-method.py / whisper-set-xfilesfactor.py在线改文件头聚合方法 / xFilesFactor
whisper-auto-resize.py / whisper-auto-update.py按 storage-schemas 批量扫描迁移
whisper-delete.py删除指标文件(带索引清理)
rrd2whisper.pyRRDtool 文件转 whisper
find-corrupt-whisper-files.py扫描损坏文件
validate-storage-schemas.py校验 storage-schemas.conf
carbon-cache.py / carbon-relay.py / carbon-aggregator.py守护进程 start/stop/status
rrd2whisper、build-index、update-storage-times.py转换与索引辅助工具
/render URL API(graphite-web)查询与函数计算(HTTP 即 CLI 的 HTTP 形态)

工具操作对象是文件系统里的 .wsp 文件,与 carbon 是否在跑无关(离线可查可改),这是 whisper 形态独有的排障优势。

三、前置条件

  1. 已部署 Graphite 1.1.10 且服务 healthy(见部署篇);已有若干已写入指标(部署篇端到端验证产生的 servers.web1.requests 等)。
  2. 确认指标文件路径:指标路径 test.s7.metric 对应文件 /opt/graphite/storage/whisper/test/s7/metric.wsp:
find /opt/graphite/storage/whisper -name 'metric.wsp' | head -2

实测回显:

/opt/graphite/storage/whisper/test/s7/metric.wsp
  1. whisper 工具已随虚拟环境装好,直接以绝对路径调用即可(下文全部使用容器内路径 /opt/graphite/bin/ 形态书写;在原生 pip 部署中通常已在 PATH 中)。

四、whisper 工具族详解

4.1 whisper-info.py——查看文件结构与元数据

whisper-info.py [options] path [field]

实测(默认 schema 下的文件):

/opt/graphite/bin/whisper-info.py /opt/graphite/storage/whisper/test/s7/metric.wsp

实测回显:

aggregationMethod: average
maxRetention: 155520000
xFilesFactor: 0.30000001192092896
fileSize: 3240052

Archive 0
offset: 52
secondsPerPoint: 10
points: 2160
retention: 21600
size: 25920

Archive 1
offset: 25972
secondsPerPoint: 60
points: 8640
retention: 518400
size: 103680

Archive 2
offset: 129652
secondsPerPoint: 600
points: 259200
retention: 155520000
size: 3110400

读法:文件头记录聚合方法(average)、最大保留(155520000 秒 = 1800 天)与 xFilesFactor(0.3);三个归档区依次 10 秒×6 小时、1 分钟×6 天、10 分钟×1800 天;offset 是各区数据起始字节、size 是各区字节数。支持 --json 输出与末尾指定单一字段(如 whisper-info.py path secondsPerPoint)。

4.2 whisper-fetch.py——导出数据点

whisper-fetch.py [--from=_FROM] [--until=UNTIL] [--json] [--pretty] path
/opt/graphite/bin/whisper-fetch.py --from=$(($(date +%s) - 1200)) /opt/graphite/storage/whisper/test/s7/metric.wsp | tail -8

实测回显:

1792471000      None
1792471010      None
1792471020      42.500000
1792471030      None
1792471040      None
1792471050      None
1792471060      None
1792471070      None
  • 每行「时间戳 TAB 值」,None 表示空槽;
  • --from/--until 接 UNIX 时间戳;⚠️ 实测语义:--from 为排他起点——若打点时间戳恰为 T,--from=T 不含该点(桶对齐后起点落到下一桶),排查单点时用 --from=$((T-10));
  • --json 输出 [["timestamp", value], ...];--pretty 人读时间格式。

4.3 whisper-create.py——创建文件

whisper-create.py path timePerPoint:timeToStore [timePerPoint:timeToStore]*

实测创建双归档文件并查看:

/opt/graphite/bin/whisper-create.py /opt/graphite/storage/whisper/test/cli-demo.wsp 10s:6h 1m:7d --xFilesFactor 0.5 --aggregationMethod sum

实测回显:

Created: /opt/graphite/storage/whisper/test/cli-demo.wsp (146920 bytes)
/opt/graphite/bin/whisper-info.py /opt/graphite/storage/whisper/test/cli-demo.wsp | head -4

实测回显:

aggregationMethod: sum
maxRetention: 604800
xFilesFactor: 0.5
fileSize: 146920

要点:

  • 多归档精度需整数倍递进(见配置篇 5.1);
  • --xFilesFactor(0–1)与 --aggregationMethod(average/sum/last/max/min/avg_zero/absmax/absmin)缺省时读 storage-aggregation.conf 匹配;
  • 同名文件已存在时拒绝创建(报 [ERROR] File ... already exists!),加 --overwrite 覆盖,--sparse 生成稀疏文件省初始占盘。

体积估算(不落盘):

/opt/graphite/bin/whisper-create.py --estimate 5s:10m,1m:1d

实测回显:

Archive 0: 120 points of 5s precision
Archive 1: 1440 points of 60s precision

Estimated Whisper DB Size: 18.320KB (20480 bytes on disk with 4k blocks)

4.4 whisper-update.py——手工写点

whisper-update.py path [timestamp:value]*
now=$(date +%s)
/opt/graphite/bin/whisper-update.py /opt/graphite/storage/whisper/test/cli-demo.wsp $now:7.5

多点与同桶覆盖实测(同一 10 秒桶连续写 111 再写 222):

t=$(( $(date +%s) / 10 * 10 ))
/opt/graphite/bin/whisper-update.py /tmp/ov.wsp $t:111
/opt/graphite/bin/whisper-update.py /tmp/ov.wsp $t:222
/opt/graphite/bin/whisper-fetch.py --from=$((t-10)) /tmp/ov.wsp | grep -v None

实测回显:

1792471200      222.000000

结论:同一时间桶后写覆盖先写。时间戳可省略(省略即 now),写未来时间戳会在文件内留空洞。

4.5 whisper-resize.py——调整保留策略

whisper-resize.py path timePerPoint:timeToStore [timePerPoint:timeToStore]*

实测把 10s:6h,1m:7d 的文件调整为 5s:1h,30s:30d:

/opt/graphite/bin/whisper-resize.py /tmp/resize-demo.wsp 5s:1h 30s:30d

实测回显:

Retrieving all data from the archives
Creating new whisper database: /tmp/resize-demo.wsp.tmp
Created: /tmp/resize-demo.wsp.tmp (1045480 bytes)
Migrating data without aggregation...
Renaming old database to: /tmp/resize-demo.wsp.bak
Renaming new database to: /tmp/resize-demo.wsp

旧文件自动保留为 .bak(加 --nobackup 不留),迁移过程把旧点按新精度聚合(本次 10s→5s 反向精度提升,超出部分用聚合方法计算,可能产生不可逆精度换算)。变更 storage-schemas.conf 后的存量迁移即用此工具(批量用 whisper-auto-resize.py:whisper-auto-resize.py /opt/graphite/storage/whisper /opt/graphite/conf)。

4.6 whisper-dump.py——文件级导出

/opt/graphite/bin/whisper-dump.py /tmp/resize-demo.wsp | head -14

实测回显(节选):

Meta data:
  aggregation method: sum
  max retention: 2592000
  xFilesFactor: 0.5

Archive 0 info:
  offset: 40
  seconds per point: 5
  points: 720
  retention: 3600
  size: 8640

Archive 1 info:
  offset: 8680

比 info.py 多输出每个归档区全部数据点的明文段落,用于离线取证与损坏排查。

4.7 whisper-merge.py 与 whisper-fill.py——合并语义对比

两者都是「从 src 拷数据到 dst」,区别在覆盖语义:

工具语义典型场景
whisper-merge.pysrc 的点覆盖 dst 同桶值权威副本回灌
whisper-fill.py只填充 dst 为空的槽位补齐缺段,不破坏 dst 已有数据

两文件归档结构必须一致,否则 merge 直接报错(实测):

NotImplementedError: /tmp/merge-src.wsp and /tmp/merge-dst.wsp archive configurations are unalike. Resize the input before merging

同结构覆盖式合并实测(src 有 t1:11/t2:12,dst 仅有 t1:99):

whisper-merge.py /tmp/m-a.wsp /tmp/m-b.wsp
whisper-fetch.py --from=$((t1-10)) /tmp/m-b.wsp | grep -v None

实测回显(t1 桶由 99 被覆盖为 11,t2 桶 12 为新补入):

1792471040      11.000000
1792471080      12.000000
1792471100      12.000000

补空式实测(dst 为全新空文件):

whisper-create.py /tmp/f-b.wsp 10s:6h >/dev/null
whisper-fill.py /tmp/m-a.wsp /tmp/f-b.wsp
whisper-fetch.py --from=$((t1-10)) /tmp/f-b.wsp | grep -v None

实测回显:

1792471040      11.000000
1792471080      12.000000
1792471100      12.000000

💡 技术拓展:数据合并工具不止这两者——whisper-auto-update.py 按 carbon.conf 定时批量同步;集群场景下 carbonate 工具集(remote_bulk_query 等)跨节点拉取合并;手工场景 fill 优先(只补空不覆盖),确需以 A 为准回灌 B 时才用 merge。

4.8 whisper-diff.py——差异比较

whisper-diff.py [--summary] [--ignore-empty] path_a path_b
/opt/graphite/bin/whisper-diff.py --summary /tmp/m-a.wsp /tmp/f-b.wsp

实测回显(两文件数据一致,仅归档 0 有 4 个点):

archive     total differing
      0         4         0

不带 --summary 时逐点列出差异行;--ignore-empty 跳过任一侧为 None 的点(比较真实数据差异时建议加)。

4.9 聚合方法与 xFilesFactor 在线修改

/opt/graphite/bin/whisper-set-aggregation-method.py /opt/graphite/storage/whisper/test/cli-demo.wsp max

实测回显:

Updated aggregation method: /opt/graphite/storage/whisper/test/cli-demo.wsp (sum -> max)
/opt/graphite/bin/whisper-set-xfilesfactor.py /opt/graphite/storage/whisper/test/cli-demo.wsp 0.2
/opt/graphite/bin/whisper-info.py /opt/graphite/storage/whisper/test/cli-demo.wsp | head -3

实测回显:

Updated xFilesFactor: /opt/graphite/storage/whisper/test/cli-demo.wsp (0.5 -> 0.2)
aggregationMethod: max
maxRetention: 604800
xFilesFactor: 0.20000000298023224

两者只改文件头元数据,立即生效(影响后续降精度聚合),不改历史数据。

4.10 rrd2whisper.py 与辅助工具

RRD 文件迁移:

rrd2whisper.py --xFilesFactor 0.5 --aggregationMethod average <file>.rrd

(--xFilesFactor 默认继承源 RRD;实测环境无 .rrd 样例文件,参数形态来自工具 --help 实测回显。)损坏扫描:

/opt/graphite/bin/find-corrupt-whisper-files.py --help

实测回显:

usage: find-corrupt-whisper-files.py [-h] [--delete-corrupt] [--verbose]
                                     WHISPER_DIR [WHISPER_DIR ...]

Find and (optionally) delete corrupt Whisper data files

positional arguments:
  WHISPER_DIR       Directory containing Whisper files

配置校验(配置篇已详述):

/opt/graphite/bin/validate-storage-schemas.py /opt/graphite/conf/storage-schemas.conf

实测回显:

Storage-schemas configuration '/opt/graphite/conf/storage-schemas.conf' is valid

⚠️ 注意:update-storage-times.py 与 build-index 在一体化镜像内实测报 ModuleNotFoundError: No module named 'configobj' / No module named 'graphite'(镜像精简了依赖),如需使用需在容器内 pip install configobj(后者还需 PYTHONPATH=/opt/graphite/webapp),或在原生部署形态使用。

五、carbon 系工具与协议客户端

5.1 carbon-cache.py——守护进程管理

carbon-cache.py <start|stop|status> [--config=CONFIG] [--instance=INSTANCE]

--help 实测回显(节选):

Options:
  -h, --help            show this help message and exit
  --debug               Run in the foreground, log to stdout
  --nodaemon            Run in the foreground
  --config=CONFIG       Use the given config file
  --instance=INSTANCE   Manage a specific carbon instance

状态查询实测:

/opt/graphite/bin/carbon-cache.py status

实测回显:

carbon-cache (instance a) is running with pid 75

⚠️ 注意:一体化镜像由 runit 托管 carbon 进程,容器内手工 stop 后 runit 会拉起,停服务请用 docker compose stop 或置 <服务名>_DISABLED=1 环境变量。

5.2 明文行协议客户端(2003)

最轻量的打点客户端就是 nc,协议为 <指标路径> <值> <UNIX时间戳>\n:

now=$(date +%s)
echo "servers.web1.requests 100 $now" | nc -w 2 127.0.0.1 8129

(无回显即成功;用 4.2 的 whisper-fetch 或 render API 验证落盘。)批量场景把多行写入一次连接发送,或改用 pickle 协议。

5.3 pickle 协议客户端(2004)

批量高效协议,报文格式:4 字节大端长度前缀 + pickle 载荷,载荷为 [(指标名, (时间戳, 值)), ...] 列表。实测脚本(容器内执行):

import pickle, socket, struct, time
now = int(time.time())
series = [("pickle.demo.count", (now - 10, 1.0)), ("pickle.demo.count", (now, 2.0))]
payload = pickle.dumps(series, protocol=2)
packet = struct.pack("!L", len(payload)) + payload
s = socket.create_connection(("127.0.0.1", 2004), 5)
s.sendall(packet); s.close()
print("sent", len(series), "datapoints via pickle")

实测输出与验证:

sent 2 datapoints via pickle
curl -sS 'http://127.0.0.1:8128/render?target=pickle.demo.count&from=-3min&format=json'

实测回显(两个点已落盘可查):

[{"target": "pickle.demo.count", "datapoints": [[1.0, 1792471210], [2.0, 1792471220], [null, ...]]}]

carbonlink 供 graphite-web 补取「已进 carbon 缓存、尚未落盘」的最新点,报文为 4 字节大端长度前缀 + pickle 三元组 (指标名, step, now)。实测脚本骨架:

import pickle, struct, socket, time
now = int(time.time())
payload = pickle.dumps(("carbonlink.demo.metric2", 10, now), protocol=2)
c = socket.create_connection(("127.0.0.1", 7002), 5)
c.sendall(struct.pack("!L", len(payload)) + payload)
# 读取 4 字节长度前缀再读 pickle 载荷 ...

实测结论:仅当目标指标在缓存中有未落盘点时才返回 pickle 点列表;缓存已刷盘或无该指标时服务端静默关闭连接、无响应字节——这是正常行为不是故障(实测打点后立即查询多次验证)。排障时不要把「无响应」误判为 carbonlink 不通。

5.5 statsd 管理接口(8126)

TCP 文本协议,stats 与 counters 等命令(statsd admin 接口)。容器内实测:

(printf 'stats\n'; sleep 2) | nc -w 3 127.0.0.1 8126

实测回显:

uptime: 538
messages.last_msg_seen: 7
messages.bad_lines_seen: 0
graphite.last_flush: 8
graphite.last_exception: 1792471455
graphite.flush_time: 0
graphite.flush_length: 2045
END

graphite.last_exception 非 0 表示上次 flush 报错(常见于下游 carbon 不可达);graphite.flush_length 是上次 flush 的点数。counters 命令列出全部计数器当前值,delcounters <前缀> 清理计数器。

六、render URL API 实测

6.1 数据端点与输出格式

/render 必带 target,时间范围用 from/until(支持 -2min、1h、绝对时间 HH:MM_YYYYMMDD),输出格式用 format:

curl -sS 'http://127.0.0.1:8128/render?target=servers.web1.requests&from=-1min&format=csv' | head -5

实测回显:

servers.web1.requests,2026-10-20 12:42:00,50.0
servers.web1.requests,2026-10-20 12:42:10,60.0
servers.web1.requests,2026-10-20 12:42:20,
servers.web1.requests,2026-10-20 12:42:30,
servers.web1.requests,2026-10-20 12:42:40,

format 取值:json(默认,含 tags 元数据)、json2、csv、raw(二进制序列复用格式)、svg/png(渲染图);不传 format 直接得到 PNG 曲线。from 桶对齐语义与 whisper-fetch 一致。

6.2 指标发现 API

指标树逐级浏览:

curl -sS 'http://127.0.0.1:8128/metrics/find?query=servers.*'

实测回显:

[{"text": "web1", "id": "servers.web1", "allowChildren": 1, "expandable": 1, "leaf": 0}, {"text": "web2", "id": "servers.web2", "allowChildren": 1, "expandable": 1, "leaf": 0}]

全量指标索引(一次拉平所有指标名):

curl -sS 'http://127.0.0.1:8128/metrics/index.json' | python3 -c 'import json,sys; m=json.load(sys.stdin); print(len(m), "metrics"); print(m[0])'

实测回显:

125 metrics
carbon.agents.graphite-server-a.activeConnections

6.3 常用 render 函数逐条实测

以下实测均为同一批底数:servers.web1.requests 6 个点 10→60(步长 10)、servers.web2.requests 6 个点 5→30。

alias——图例改名:

curl -sS 'http://127.0.0.1:8128/render?target=alias(servers.web1.requests,"web1-req")&from=-2min&format=json'

实测回显(target 已改名,12 个槽位):

web1-req 12

summarize——时间窗聚合(1 分钟窗求和):

curl -sS 'http://127.0.0.1:8128/render?target=summarize(servers.*.requests,"1min","sum")&from=-2min&format=json'

实测回显:

[{"target": "summarize(servers.web1.requests, \"1min\", \"sum\")", "datapoints": [[100.0, 1792471260], [110.0, 1792471320]]},
 {"target": "summarize(servers.web2.requests, \"1min\", \"sum\")", "datapoints": [[50.0, 1792471260], [55.0, 1792471320]]}]

(每分钟 6 点:10+20+30+40+50+60=100/110;第二窗 50/55 为半窗聚合值。)聚合函数可选 sum/avg/max/min/last/count 等。

groupByNode——按路径层级分组聚合(第 1 级即 web1/web2):

curl -sS 'http://127.0.0.1:8128/render?target=groupByNode(servers.*.requests,1,"sum")&from=-2min&format=json'

实测回显:

[{"target": "web1", "datapoints": [[10.0, ...], [20.0, ...], [30.0, ...], [40.0, ...], [50.0, ...], [60.0, ...]]},
 {"target": "web2", "datapoints": [[5.0, ...], [10.0, ...], [15.0, ...], [20.0, ...], [25.0, ...], [30.0, ...]]}]

highestCurrent——Top-N 圈选(取当前值最大 1 条):

curl -sS 'http://127.0.0.1:8128/render?target=highestCurrent(servers.*.requests,1)&from=-2min&format=json'

实测回显:

['servers.web1.requests']

movingAverage——滑动平均(3 点窗口):

实测回显(前两点窗口不足用可用点均值起步,逐点滚动):

[10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 55.0, 60.0]

scale——数值缩放(毫秒转秒):

实测回显(10→60 全体 ×0.001):

[0.01, 0.02, 0.03, 0.04, 0.05, 0.06]

keepLastValue——空值保持(空洞填充为最后有效值):

实测回显:non-null 从 6 增至 10(共 12 槽,空洞被最后值补齐)。

组合管道:函数可嵌套,target=alias(summarize(scale(servers.web1.requests,0.001),"1min","sum"),"qps") 即「缩放→聚合→改名」的完整管道,书写顺序从内向外即执行顺序。

七、注意事项

  1. whisper 工具直接读写文件,对生产库操作前先备份对应 .wsp(resize 默认留 .bak,merge/fill 不会自动备份);
  2. whisper-fetch/merge 的时间参数桶对齐有排他性,单点取证统一用 目标时间戳-精度;
  3. carbonlink 无缓存点时不回包是设计行为;statsd admin 命令后需稍等连接读回(脚本里 sleep 再收包);
  4. render API 的 from/until 与 storage-schemas 归档精度联动:超出最高精度保留时长会自动落到低精度归档;
  5. 一体化镜像中 update-storage-times.py / build-index 缺依赖(实测 ModuleNotFoundError),依赖补齐前用 whisper 原生工具链即可覆盖绝大多数运维场景。

八、常见问题

Q1:whisper-info.py 报 PermissionError 或 FormatError? PermissionError 是文件属主不对(容器内进程以 root 运行,宿主手工创建的文件属主不一致,chown 对齐);FormatError 说明文件损坏或被 resize 半途中断,用 find-corrupt-whisper-files.py 扫描后从备份恢复。

Q2:whisper-update 写入后 fetch 不到? 时间戳写成了毫秒(13 位)或未来时间;whisper 不做单位换算,13 位时间戳落点远超归档范围静默丢弃。统一用 date +%s 秒级时间戳。

Q3:merge 报 configurations are unalike? 两文件归档结构(精度×点数)不一致,先 whisper-info.py 对比,再对齐参数 resize 后重试;跨 schema 的「形状不等」合并没有意义。

Q4:render 查询 404 或空 JSON? 404 多为 nginx 前缀配置问题(GRAPHITE_URL_ROOT 改过);空 JSON 无报错通常是时间范围内无数据——先用 metrics/find 确认指标名存在,再核对 from/until 与 schema 保留区间。

Q5:statsd 指标名为什么是 stats. 与 stats_counts. 两套?** statsd 刷新时同时上报速率(stats.<key>,按 flushInterval 折算)与原始计数(stats_counts.<key>,窗口内累加值),timer 类指标还有 stats.timers.<key>.* 家族(mean/upper/lower/count_ps 等),选型时按「要速率还是要计数」取用。

九、参考链接