关于 groundcover-cli
用于通过命令行操作 Groundcover 可观测性平台的技能,适合管理遥测数据、仪表板、监控器和平台设置。它结合类型化 SDK 命令与原始端点访问,覆盖 SDK 尚未支持的操作。
主要功能
- 01类型化平台资源增删改查
- 02搜索日志、追踪和指标
- 03访问更多原始端点
- 04管理认证配置和凭据
- 05管理 Grafana 仪表板与设置
适用场景
- 01查询遥测数据并检查服务状态
- 02创建或更新监控器和仪表板
- 03访问 SDK 尚未提供的端点明细
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ paymog/ groundcover-cli --skill groundcover-cli安装后在 Claude Code 中输入/groundcover-cli确认已加载。
详细介绍
Groundcover CLI
调用 groundcover 二进制文件(通过 brew install paymog/tap/groundcover 安装)。权威来源是 paymog/groundcover-cli。
身份验证(任何命令执行前都必须完成)
export GROUNDCOVER_API_KEY=gcsa_... # must be a service-account key
密钥必须是服务账号密钥:前缀为 gcsa_,长度恰好为 40 个字符。 其他格式均会被拒绝,并显示明确消息——Invalid token prefix. Expected 'gcsa_' 或 Invalid token length (N, expected 40)。请勿混淆密钥类型:
gcsa_…— 服务账号密钥。查询和 CRUD 命令需要使用此密钥。gcik_…— 数据摄取密钥(用于推送数据)。无法用于 API 身份验证;类型错误。
如果使用格式正确的 gcsa_ 密钥却收到 401 错误,说明此密钥属于与你正在查询的租户不同的租户。
如果手头没有 gcsa_ 密钥,可以使用已有的有效密钥运行 groundcover service-accounts create 来创建一个,然后在当前会话中执行 export GROUNDCOVER_API_KEY=gcsa_…。
内置默认值:
--base-url https://api.groundcover.com--backend-id groundcover
没有默认租户 UUID — 设置 --tenant-uuid / GROUNDCOVER_TENANT_UUID,即可在非 Grafana 的 raw … 直通调用中发送 X-Tenant-UUID 标头(例如跨租户访问)。内嵌的 Grafana raw grafana … 端点会忽略它(参见下文的 Grafana 部分)。
raw grafana … 命令完全不使用 gcsa_ 密钥 — 它们需要 Grafana 服务账号令牌(glsa_…)。请设置 GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN / --grafana-token。
覆盖环境变量:GROUNDCOVER_BACKEND_ID、GROUNDCOVER_TENANT_UUID、GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN、GROUNDCOVER_BASE_URL(或对应的 GC_* 变量)。相同名称也可用作 --api-key、--backend-id、--tenant-uuid、--grafana-token、--base-url 标志。
已保存的配置(可替代环境变量)
无需每次会话都导出 GROUNDCOVER_API_KEY,也可以将凭据保存在命名配置中。API 密钥存储在操作系统密钥环中;元数据(后端 ID、基础 URL、租户 UUID)存储在 ~/.config/groundcover/profiles.yaml 中。
groundcover auth login [name] --backend-id <id> # prompts for key (or --key); validates before saving
groundcover auth list # * marks default
groundcover auth default <name> # change default
groundcover --profile <name> <command> # use a profile for one command
groundcover auth status # show resolved source
groundcover auth token # print resolved key
groundcover auth logout <name> [-f]
优先级:--api-key/GROUNDCOVER_API_KEY 环境变量 → --profile <name> → 默认配置。显式指定密钥的同时使用 --profile 会被视为存在歧义并遭到拒绝。GROUNDCOVER_PROFILE 可通过环境变量设置配置。
其他全局标志:--timeout(默认值为 30s)、--raw(不重新格式化 JSON 响应)。
两种调用方式
| 方式 | 适用场景 |
|---|---|
基于 SDK(groundcover <resource> <verb>) | 首选。契约稳定,请求正文类型明确。 |
基于 HAR 的原始调用(groundcover raw …) | SDK 中缺少相应端点,或需要 SDK 不具备的运行器功能(见下文)。 |
始终优先尝试 SDK 方式。
原始调用提供但 SDK 不支持的功能
--set dotted.path=value— 在正文基础上进行深度合并覆盖--query key=value(可重复)— 任意查询字符串覆盖- 内置默认正文,采集自 Web 应用 HAR(SDK 要求显式指定
--body-file/--body-json) - 在设置
--tenant-uuid/GROUNDCOVER_TENANT_UUID时,非 Grafana 原始调用会发送X-Tenant-UUID(用于跨租户访问;否则 SDK 传输会从 API 密钥推导租户)。Grafanaraw grafana …命令不会使用它 — 它们改用glsa_令牌进行身份验证。 - 使用
groundcover raw list发现所有已采集的端点
两种调用方式都支持 --body-file(json/yaml)、--body-json 和 --raw 输出。
目前只能使用 raw 的端点(尚无 SDK 命令)
对于以下端点,请使用 groundcover raw …;SDK 提供了父级资源,但没有下钻端点:
- 日志:
filters、velocity(SDK 仅提供search) - 追踪:
attributes、details、errors、filters、insights、latencies、requests、values-distribution(SDK 仅提供search) - 指标:
cardinality、cardinality-graph、discovery、labels-cardinality、query-range、resources errors|latencies|list|requests(SDK 提供query、names、keys、values) - prometheus:
prometheus api query(原始 Prom 透传——可通过--query query='up'方便地运行临时 PromQL 查询) - Grafana 仪表板:
grafana search、grafana dashboards get|save|delete、grafana dashboards permissions get|update、grafana dashboards versions|get|restore、grafana folders list|get|create|update|delete、grafana folders permissions get|update、grafana annotations list|create|update|delete、grafana prometheus rules、grafana datasources label-values、grafana ds query - 监控器下钻:
instances filters|query|timeline、labels keys、silences、summary filters|query、timeline(SDK 仅提供 CRUD) - k8s 下钻:
configmaps|cronjob|daemonsets|deployments|jobs|pods|pvcs|replicasets|statefulsets list、container info、context events、namespaces info|list、nodes info-with-resources|list|resources|usage top10、pod container usage、pods status-over-time、workloads availability|events|usage top10、network connections|cross-az|cross-az-regions|partners|throughput|top-connections、events search-time-series(SDK 仅提供clusters、workloads、events-search、events-over-time) - 基础设施:
infra hosts info-with-resources - 资源 / RUM:
resources apis errors|filters|latencies|list|requests、rum sessions filters|query、sources list - 流水线统计:
pipelines logs current-stats、pipelines traces current-stats(SDK 仅支持配置 CRUD) - 租户 / 计费 / RBAC 读取:
rbac seatsUsage、rbac tenant ai-settings、rbac tenant settings、backend settings、billing method、agent token-budgets|token-usage|token-usage history|token-usage tenant - 存储管理:
storage-management get|update --data-type <logs|traces|events|measurements|monitor_instance>,用于设置保留策略、gcQL 异常规则和索引层级 - 其他:
graph、graph filters、views member、views member defaults、migrations、connectors list org|personal、synthetics rules、aggregations metrics config|config default、integrations data config
运行 groundcover raw list / groundcover raw list <group>,确认准确名称后再调用。
基于 SDK 的 CRUD 模式
groundcover <resource> list [--query '<filter>']
groundcover <resource> get <id>
groundcover <resource> create --body-file <file> # or --body-json '<json>'
groundcover <resource> update <id> --body-file <file>
groundcover <resource> delete <id>
请求体文件:.json 或 .yaml。
资源(均遵循上述模式)
- 仪表板——另有
archive <id>、restore <id> - 监控器——
--query 'monitor_name = "cpu"' - 静默规则——
list --active - 定期静默规则
- 已连接的应用——
--query 'type:slack-webhook' - 通知路由——
--query 'prod' - 合成监控
- 密钥——另有
hash <id> - 工作流
身份验证 / RBAC
groundcover api-keys list
groundcover api-keys create --body-file key.json
groundcover service-accounts list
groundcover service-accounts create --body-file sa.json
groundcover ingestion-keys list
groundcover policies list
groundcover policies apply --body-file policy.json
groundcover policies audit-trail <id>
流水线单例(get / create / update / delete,无 id)
groundcover logs-pipeline get
groundcover metrics-pipeline get
groundcover traces-pipeline get
groundcover metrics-aggregator get
集成(类型化)
groundcover integrations list
groundcover integrations describe <type>
groundcover integrations create <type> --body-file config.json
groundcover integrations update <type> <id> --body-file config.json
可观测性 / 读取命令(由请求体文件驱动)
groundcover logs search --body-file body.json
groundcover traces search --body-file body.json
groundcover metrics query --body-file body.json # PromQL range/instant
groundcover metrics names --body-file body.json # list metric names
groundcover metrics keys --body-file body.json # label keys for a metric
groundcover metrics values --body-file body.json # label values for a key
groundcover search discovery --body-file body.json # facet / discovery
groundcover search keys --body-file body.json
groundcover search values --body-file body.json
groundcover k8s clusters --body-file body.json
groundcover k8s workloads --body-file body.json
groundcover k8s events-search --body-file body.json
groundcover k8s events-over-time --body-file body.json
使用管道传递给 jq 提取字段,或传入 --raw 输出原始响应。
可观测性请求体模板
时间使用 RFC 3339 UTC 格式(2026-05-28T00:00:00Z)。不内置当前时间获取功能——请使用 date -u 计算。所有搜索采用相同结构(logs search、traces search、k8s events-search):
{
"start": "2026-05-28T00:00:00Z",
"end": "2026-05-28T01:00:00Z",
"query": "<GCQL>",
"filters": "",
"sources": []
}
metrics query(注意首字母大写的字段名——这是 API 的要求):
{
"Promql": "rate(http_requests_total[5m])",
"Start": "2026-05-28T00:00:00Z",
"End": "2026-05-28T01:00:00Z",
"Step": "60",
"QueryType": "range",
"Conditions": []
}
进行单点查询时,使用 "QueryType": "instant" 并省略 Step。
k8s workloads / k8s clusters:
{
"conditions": [],
"sources": [],
"gcqlFilter": "namespace = 'prod'",
"limit": 100
}
Raw 命令
groundcover raw list # list groups
groundcover raw list <group> # list commands in group
groundcover raw k8s clusters list
groundcover raw dashboards get --dashboard-id <id>
groundcover raw metrics query-range --body-file body.json
groundcover raw prometheus api query --query query='up'
groundcover raw grafana dashboards get --dashboard-uid <uid>
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana ds query --body-file query.json
存储管理
管理员可以读取和更新 logs、traces、events、measurements(APM)和 monitor_instance(监控问题)的生命周期设置:
groundcover raw storage-management get --data-type logs --raw \
| jq '{retention,version,cold_move_duration,cold_volume,custom_rules:(.custom_rules // [])}' \
> storage.json
# Edit storage.json, preserving every writable field and the complete rule list.
groundcover raw storage-management update --data-type logs --body-file storage.json
更新采用乐观并发控制,并替换可写设置文档。始终从 get 开始,沿用当前的 version,并保留 retention、cold_move_duration、cold_volume 和完整的 custom_rules 列表;省略 custom_rules 会移除现有规则。成功更新后,version 会递增。每条已观测到的自定义规则都包含 name、retention 和 gcQL filters 表达式。
Grafana 原生仪表板
Groundcover 还在 /grafana 嵌入了 Grafana。这些仪表板与 Groundcover 一等的 dashboards SDK 资源并不相同,因此,如果需要原生 Grafana JSON 仪表板、文件夹、权限、由数据源支持的变量值或面板查询执行,请使用 raw grafana …。
**身份验证:**嵌入式 Grafana 位于受会话控制的代理之后,该代理会忽略 gcsa_ API 密钥、后端 ID 和租户 UUID。向 /grafana/api/* 发送 bearer/gcsa_ 请求只会返回约 980KB 的 Grafana SPA index.html(HTTP 200,text/html),而不会返回 JSON。这些命令需要 Grafana 服务账号令牌(glsa_…):请设置 GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN(或使用 --grafana-token)。运行任何不带令牌的 raw grafana … 命令时,CLI 都会打印完整的设置指南。
生成令牌(需要 groundcover 租户管理员权限)。令牌由 groundcover 的官方 CLI(github.com/groundcover-com/cli)提供,但遗憾的是它也会安装一个名为 groundcover 的二进制文件。安装程序会将其放在 ~/.groundcover/bin/groundcover,并将该目录添加到 PATH 的开头;此后,groundcover 会指向官方 CLI,并覆盖此 CLI。请使用官方二进制文件的完整路径来避免冲突:
sh -c "$(curl -fsSL https://groundcover.com/install.sh)" # installs to ~/.groundcover/bin
~/.groundcover/bin/groundcover auth login # auth flow
~/.groundcover/bin/groundcover auth generate-service-account-token # prints glsa_… once
export GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN=glsa_... # hand it to this CLI
若要继续将此 CLI 用作 groundcover,请从 shell rc 中移除安装程序添加的 ~/.groundcover/bin PATH 行。
常用命令:
groundcover raw grafana search --query query='service slo' --query folderUIDs=general
groundcover raw grafana dashboards get --dashboard-uid streamling-pipeline-slo
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana datasources label-values --datasource-uid <uid> --label project_id --query start=<unix> --query end=<unix>
groundcover raw grafana ds query --query ds_type=prometheus --body-file query.json
Grafana raw 命令默认使用 https://app.groundcover.com(Web 应用主机),而常规 API/SDK 命令默认使用 https://api.groundcover.com。仅在非标准部署中传递 --base-url。
Raw 标志:--body-json、--body-file、--set dotted.path=value、--query key=value(可重复)、生成的路径标志(例如 --dashboard-id)、--raw。许多 raw 命令附带从 Web 应用 HAR 中捕获的默认请求体,因此无需传入任何 --body-* 即可运行,也可以使用 --set 覆盖特定字段。
操作示例
查询某个服务最近一小时的错误日志
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ) # GNU: date -u -d '1 hour ago' +%FT%TZ
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND level = \"error\"",
filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin | jq '.[] | {ts:.timestamp, pod:.pod_name, body:(.body|fromjson?)}'
logs search在根路径返回一个原始 JSON 数组——请使用jq '.[]',而不是.logs[]。人类可读的消息位于body字段中,且为 JSON 字符串(使用fromjson解析)。有用的顶层字段包括:pod_name、container_name、namespace、workload、level(已标准化为小写——error/warning/debug,而不是body中显示的WARN/DEBG)、cluster、timestamp、trace_id。
查询某个工作负载的 p99 延迟(最近 30 分钟,PromQL)
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
Promql: "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[5m])))",
Start: $s, End: $e, Step: "60", QueryType: "range", Conditions: []
}' | groundcover metrics query --body-file /dev/stdin
查询发生崩溃循环或重启的 Pod
jq -n '{ conditions: [], sources: [], gcqlFilter: "status != \"Running\"", limit: 200 }' \
| groundcover k8s workloads --body-file /dev/stdin \
| jq '.workloads[] | select(.restarts > 0) | {name, namespace, status, restarts}'
查询某个命名空间最近的 k8s 事件
END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "namespace = \"prod\" AND type = \"Warning\"",
filters: "", sources: []
}' | groundcover k8s events-search --body-file /dev/stdin
查找慢速跟踪,然后转向查看其日志
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND duration_ms > 1000",
filters: "", sources: []
}' | groundcover traces search --body-file /dev/stdin \
| jq -r '.[] | .trace_id' | head -5 \
| while read tid; do
jq -n --arg s "$START" --arg e "$END" --arg t "$tid" '{
start:$s, end:$e, query: ("trace_id = \"" + $t + "\""), filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin
done
查找某个流中有哪些标签/字段
# What metric names match a prefix?
jq -n '{ prefix: "http_" }' | groundcover metrics names --body-file /dev/stdin
# What labels does a specific metric have?
jq -n '{ metric: "http_request_duration_seconds" }' | groundcover metrics keys --body-file /dev/stdin
# What values does a label take?
jq -n '{ metric: "http_request_duration_seconds", key: "service" }' \
| groundcover metrics values --body-file /dev/stdin
GCQL 快速参考(日志/追踪/事件查询)
- 相等:
service_name = "api"(JSON 中的双引号 →\") - 比较:
duration_ms > 1000、status_code >= 500 - 布尔运算:
AND、OR、NOT - 子字符串:
message ~ "timeout",正则表达式:message ~* "^panic:" - 成员关系:
level IN ("error", "fatal")— 仅适用于命名字段 - **全文短语:**不带字段名的带引号字符串,例如
container = "my-service" AND "database unavailable"。IN(...)不能用作全文搜索(会报错freetext 'in(...)' is not supported)——请展开为level = "error" OR level = "fatal"。 - 切勿发送空的
query—query: ""→ 500pipeline cannot be nil。请始终至少提供一个条件。 - 空值检查:
trace_id IS NOT NULL
枚举字段:search discovery/search keys 比较棘手(discovery 需要 Limit + Type,且仍可能返回 400)。可靠的做法是获取一条示例日志,并检查 keys 和解析后的 body。
查找并静默一个监控项
groundcover monitors list --query 'monitor_name ~ "ingest"'
groundcover silences create --body-json '{"monitor_id":"<id>","duration_seconds":3600,"reason":"deploy"}'
导出所有仪表板
列表字段是 uuid,不是 id(.id 为 null)。每个条目还包含 archivedTimestamp — 跳过已归档的条目(活动状态 = 0001-01-01T00:00:00.000Z)。
groundcover dashboards list \
| jq -r '.[] | select(.archivedTimestamp == "0001-01-01T00:00:00.000Z") | .uuid' \
| while read id; do groundcover dashboards get "$id" > "dashboards/$id.json"; done
构建预填模板变量的仪表板深层链接
groundcover dashboards get <uuid> 会在 .preset 下返回变量定义(一个JSON 字符串——用 fromjson 解析)。查找仪表板并检查其变量:
groundcover dashboards list | jq -r '.[] | select(.name|test("prod";"i")) | "\(.uuid)\t\(.name)"'
groundcover dashboards get <uuid> | jq -r '.preset | fromjson | .variables'
# each var: { kind:"list", spec:{ values:{default:["*"]}, datasource:{key,kind,metric}, variableName } }
Web 应用会将选定的变量值编码到 variables URL 查询参数中——这是一个以 $<variableName> 为键的 URL 编码 JSON 对象。每个条目都需要 name、key、values(数组;支持 myapp* 这样的通配符)、datasource(通常为 "metrics")和 refiner.metric(变量 datasource.metric 中的源指标)。
**请固定所有你关心的变量——包括你希望不受限制的变量。**任何省略的变量都会回退到仪表板的已保存默认值,而它通常不是 *(例如,硬编码的 deployment 哈希值或 shard: primary)。要获取真正宽泛的视图,请明确将这些变量设为 ["*"]。使用前务必确认标签值确实存在——例如,app_kubernetes_io_instance 可能没有单独的 myapp,只有 myapp-web-0 等值,因此请使用 myapp* 通配符。
使用 jq 构建,确保编码正确:
VARS=$(jq -cn '{
"$cluster":{name:"cluster",key:"cluster",values:["example-cluster"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$deployment":{name:"deployment",key:"deployment",values:["*"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$app_kubernetes_io_instance":{name:"app_kubernetes_io_instance",key:"app_kubernetes_io_instance",values:["myapp*"],datasource:"metrics",refiner:{metric:"my_metric_total"}}
}')
ENC=$(jq -rn --arg v "$VARS" '$v|@uri')
echo "https://app.groundcover.com/dashboards?backendId=groundcover&tenantUUID=<your-tenant-uuid>&duration=Last+1+hour&viewId=<uuid>&qb-disable-auto-focus=true&variables=${ENC}"
注意:仪表板通过 viewId=<uuid> 打开(而不是 /dashboards/<uuid> 路径);duration 接受 Last+1+hour / Last+5+minutes 这样的 UI 字符串。
通过 raw 发起临时 Prom 查询
groundcover raw prometheus api query --query query='up{namespace="prod"}'
构建和编辑仪表板
仪表板的关键不在于 CRUD 操作(dashboards get/list/create/update/archive/restore),而在于 preset,整个面板布局都存放在一个字段中。
Preset 结构
dashboards get <uuid> 会返回仪表板,其中 .preset 是一个JSON 字符串(用 fromjson / json.loads 解析;写回前重新序列化为字符串)。结构:
{
"spec": { "layoutType": "ordered", "crosshairSyncEnabled": true },
"layout": [ /* one entry per TOP-LEVEL item, placed on a 24-col grid */ ],
"widgets": [ /* the actual panels, referenced by id from layout */ ],
"duration": "Last 6 hours", // UI duration string
"variables": [ /* template vars, same shape as the deep-link recipe */ ],
"schemaVersion": 7
}
layout— 顶层网格布局。每个条目的格式为{ "id":"A", "h":12, "w":24, "x":0, "y":0, "minH":8 }。网格宽 24 列(w:8= 三分之一,w:12= 一半);id必须与某个组件的id匹配。需要手动计算y来堆叠行——没有自动流式布局。widgets—{ id, name, type, queries:[{id, expr, dataType:"metrics", editorMode:"editor"}], visualizationConfig:{ type, selectedUnit, ... } }。expr是带有$var替换的 PromQL。- 组件类型:
time-series、stat、text(content中为 Markdown,无查询——可用于标题/分隔线)、section。
分区(可折叠组)
分区是一个组件 { id, name, type:"section", color, isCollapsed:false }。它的 layout 条目包含一个 children 数组,其中是采用相对位置的面板条目:{ id, h, w:24, x:0, y:0, children:[ {h,w,x,y,id,minH}, ... ] }。标题占据顶部约 2 行,因此子项从 y:2 开始。
更新契约
dashboards update <uuid> 请求体必须包含 currentRevision(当前的 .revisionNumber)——这是乐观并发控制。省略它会导致 Field validation for 'CurrentRevision' failed on the 'required_if' tag。每次成功更新后都要重新执行 get,读取递增后的版本号,再进行下一次写入。
groundcover dashboards get <uuid> > d.json
# NEW_PRESET must be the preset object re-serialized to a STRING
jq -n --slurpfile d d.json --arg preset "$NEW_PRESET" '{
name: $d[0].name, viewType: ($d[0].viewType // "explore"),
status: "active", currentRevision: $d[0].revisionNumber, preset: $preset
}' | groundcover dashboards update <uuid> --body-file /dev/stdin
验证规则(API 只会返回 Dashboard validation failed)
更新端点会返回不透明的 400 {"message":"Dashboard validation failed"}——没有指出字段,也没有指出哪个组件有问题。遇到这种情况时,使用二分法排查:部署最小预设(一个分区 / 一个面板),然后逐步添加内容,直到再次出错。以下两条不太明显的规则,排查起来都很费时间:
- 统计组件(
visualizationConfig.type:"stat")只有作为顶层扁平layout条目时才能通过验证——绝不能放在分区的children[]中。 时序组件则在这两种位置都可用。要构建概览统计行,请将统计面板作为顶层扁平条目放置(并在其上方放置一个text标题组件),将时序面板保留在分区中。 - 分区的
color仅接受固定调色板中的颜色。 已验证有效:gray、purple、teal。已验证无效:green、blue、red、yellow、orange。
滞后指标的统计面板显示“No Results” → 用 last_over_time 包装
统计面板会将“现在”作为时间点执行即时查询。实时数据有延迟的指标会导致该即时窗口为空 → 面板显示 “No Results”,尽管它在时序面板中能正常绘图(时序面板会对整个窗口执行范围查询)。从轮询式集成(例如 CloudWatch 等云服务提供商集成)拉取的指标通常会滞后实时数据数分钟,因此直接基于这些指标构建的统计面板会显示空白。
解决方法:在聚合表达式内部用 last_over_time(<selector>[30m]) 包装选择器,让即时查询向前回溯,越过数据陈旧时段:
max(last_over_time(my_metric{...}[30m])) # NOT max(my_metric{...})
*_over_time 包装函数在统计面板中可以正常渲染(例如,100 * avg_over_time((...)[7d:5m]) 可以正常显示运行时间统计)。实时抓取的集群内指标(约 1 秒内更新)不需要这样处理。
不通过 UI 验证仪表板
如果无头浏览器遇到 Groundcover 的 SSO 登录墙(没有会话),就无法通过截图验证。可以改用 API 验证:
- 每条查询都返回数据: 拉取已部署的预设,在每个
expr中填入具体值(将$var替换为实际标签值;将通配符变量替换为=~".+"正则表达式),然后通过metrics query执行范围查询。查询窗口应不短于指标的生成间隔(稀疏的集成指标可能每隔几分钟才生成一次)。 - 几何布局: 确认没有两个布局矩形相互重叠,并且每个
x+w ≤ 24(顶层条目和每个分区的children均需检查)。 - 注意: CLI 无法可靠地执行即时查询(参见“常见问题”),因此无法通过 CLI 复现统计面板的即时查询路径——应根据数据陈旧情况和范围查询数据进行判断。
轮询式集成指标(例如 CloudWatch):标签方案注意事项
从轮询式云集成接入的指标,在构建仪表板时与集群内导出器指标有所不同,容易造成问题:
- 它们会滞后实时数据(参见上面的统计面板说明)——延迟以分钟计,而不是秒。
- 它们通常会按
stat维度成倍出现(stat=average/maximum/minimum)——每个序列都会出现 3 次。务必筛选到所需的一个值(例如stat="average"),否则聚合结果会被重复计算两到三次。 - 环境 / 范围标签采用集成命名方式,而不是统一的
env。如果同一资源也由集群内导出器采集,那么这两组指标会用不同的标签键表示同一概念(例如instance_id与instanceid)。请为每种标签方案分别使用模板变量,不要强行让一个变量同时适配两者。
生产环境可观测性排查流程
当有人问“为什么 X 在生产环境中出故障了”或“服务 Y 怎么了”时:
- 确定时间范围。 默认使用最近 1h。用
date -u计算START/END(参见操作指南)。 - 先查日志。 使用
groundcover logs search,通过service_name和level = "error"设置 GCQL 过滤条件。成本低,通常就能找到答案。 - 延迟高或运行缓慢时查追踪。 使用
groundcover traces search,按duration_ms、status_code或service_name过滤。获取几个trace_id,再通过trace_id = "..."回查日志。 - 查看指标随时间的变化。 使用
groundcover metrics query和 PromQL 查询错误率、p99、饱和度等。绘图时使用range,检查单个时间点时使用instant。 - 确认“是否仍在运行”时查 K8s。 使用
groundcover k8s workloads查看状态和重启情况;使用groundcover k8s events-search查找Warning事件(OOMKilled、FailedScheduling、BackOff)。 - 不知道字段名? 使用
groundcover search discovery、metrics names、metrics keys、metrics values列举字段。 - 需要在事件期间静默嘈杂的告警? 执行
groundcover monitors list --query …→groundcover silences create。
不确定有哪些命令时
- 查看上方的资源列表。
- 执行
groundcover --help和groundcover <resource> --help。 - 对于原始资源:执行
groundcover raw list,然后执行groundcover raw list <group>。
常见问题
401 Unauthorized、令牌前缀/长度错误或结果为空
Invalid token prefix/Invalid token length→ 密钥不是gcsa_服务账号密钥(参见 身份验证)。常见错误是使用了gcik_摄取密钥。- 使用看似有效的
gcsa_密钥却收到401→ 该密钥属于其他租户。对于其他租户,还要设置GROUNDCOVER_TENANT_UUID,并在适用时设置GROUNDCOVER_BASE_URL。 - 否则,请确认当前 shell 中确实已导出该环境变量。
SDK 命令拒绝请求体
请求体结构与 SDK 契约不一致。使用 get <id> 获取现有资源,并将其结构用作模板。
logs search / traces search / events-search 返回空结果
几乎总是以下原因之一:
- 时间范围不正确或过窄。
start必须早于end,且两者都必须是 RFC 3339 UTC 时间。 - GCQL 字段名在此租户中不存在。使用
search discovery/search keys列举字段。 - 查询字符串未转义——在 JSON 中,
service_name = "api"应写成"service_name = \"api\""。
metrics query 未返回数据,但指标确实存在
- 字段名区分大小写(
Promql、Start、End、Step、QueryType)——JSON 会静默接受小写字段名,但会忽略它们。 Step是字符串("60"),不是数字。- 对于即时查询,设置
"QueryType": "instant"并省略Step。已观察到的问题:测试中,CLI 的metrics query对即时查询请求体返回过400 metricsQueryBadRequest(尝试过的所有结构均如此——Start==End、Time、设置或不设置QueryType),即使指标是新创建的也是如此。需要单个当前值时,可改用短时间范围的范围查询(QueryType:"range"+Step),并取最后一个数据点。(真正的即时 PromQL 也可通过raw prometheus api query访问,但该直通接口可能查询的是与metrics query不同的数据存储。) - 解析响应:结果位于
.data.result // .result;每个values条目都是[unix_ts, "stringvalue"]。使用[.values[][1]] | map(tonumber) | add对范围内的值求和;使用strftime格式化时间戳。
logs search 超时(context deadline exceeded)
高日志量的工作负载会产生大量日志,因此即使查询范围仅为约 60–90s,宽泛查询或单 Pod 查询也可能超出时限。解决方法:
- 始终加入具有区分度的词项(一个短语 +
container/pod),不要只查询 Pod 本身。 - 缩短时间范围并提高
--timeout(例如--timeout 120s)。 - 避免在一个查询中使用较长的
OR链——分别运行这些查询。 - 对于“频率如何/随时间如何变化”这类问题,优先使用
metrics query,而不是抓取日志。
monitors 编辑与搜索
monitors get/update使用 YAML(即使带有--raw也是如此)。工作流:monitors get <uuid> > mon.yaml,编辑后执行monitors update <uuid> --body-file mon.yaml。成功更新会返回{"status":202}(异步应用——重新执行get以确认)。- Slack 应用目标需要同时设置
connectedApps: [<app-id>]和connectedAppParams.<app-id>.channels: [{id, name}]。Web 应用 PUT 使用具有该结构的 YAMLtext/plain。SDK ≥ v1.376 会编码connectedAppParams;较旧的 SDK 会将其丢弃,API 则返回400 connectedApps[0]: channels is required for slack-app route params。 - 监控器模型位于
model.queries[].sqlPipeline.filters.conditions[]下——每个条件都是{key, origin: root, type, filters:[{op, value}]};自由文本使用type: freetext+op: phrase_search。其他参数:instantRollup(例如1 minute)、thresholds[]、evaluationInterval.{interval, pendingFor}。 monitors list --query 'monitor_name ~ "..."'→ 400regexp requires a string column type。请使用monitor_name = "exact",或列出全部结果后用jq过滤。
分组内原始命令“未找到”
该端点未包含在捕获的 HAR 中。如果存在对应形式,请使用 SDK;否则重新生成——重新生成脚本位于 groundcover-cli 仓库中(go run ./scripts/generate-commands.go <har>)。