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 与常用函数实测回显全收录。
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.py | RRDtool 文件转 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 形态独有的排障优势。
三、前置条件
- 已部署 Graphite 1.1.10 且服务 healthy(见部署篇);已有若干已写入指标(部署篇端到端验证产生的
servers.web1.requests等)。 - 确认指标文件路径:指标路径
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
- 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.py | src 的点覆盖 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, ...]]}]
5.4 carbonlink 缓存查询(7002)
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") 即「缩放→聚合→改名」的完整管道,书写顺序从内向外即执行顺序。
七、注意事项
- whisper 工具直接读写文件,对生产库操作前先备份对应 .wsp(resize 默认留 .bak,merge/fill 不会自动备份);
- whisper-fetch/merge 的时间参数桶对齐有排他性,单点取证统一用
目标时间戳-精度; - carbonlink 无缓存点时不回包是设计行为;statsd admin 命令后需稍等连接读回(脚本里 sleep 再收包);
- render API 的 from/until 与 storage-schemas 归档精度联动:超出最高精度保留时长会自动落到低精度归档;
- 一体化镜像中 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 等),选型时按「要速率还是要计数」取用。
九、参考链接
- whisper 官方文档(工具与格式):https://graphite.readthedocs.io/en/latest/whisper.html
- render API 函数参考:https://graphite.readthedocs.io/en/latest/functions.html
- feeding-carbon(明文/pickle 协议格式):https://graphite.readthedocs.io/en/latest/feeding-carbon.html
- statsd admin 接口:https://github.com/statsd/statsd/blob/master/docs/admin_interface.md
- carbonate 集群工具集:https://github.com/graphite-project/carbonate
- 本库系列:《Graphite 1.1.10 介绍与选型指南》《Graphite 1.1.10 Docker Compose 部署指南》《Graphite 1.1.10 配置参数详解》