跳转至

SOP —— Kibana 日志分域视图

变更日期:2026-10-01 | 工程:monitoring-p2-20261001 | 类型:功能新增(C 类) 关联:docs/RUNBOOK.md §4.5(日常使用)、backup/kibana-domain-view-20261001/README.md(备份与回滚台账)

一、背景

EFK(ES + Kibana + Filebeat)已上线并稳定采集 4 个 K8s 节点与集群外 14 台主机, 但 Kibana 侧只有裸的 Discover,没有任何 data view、没有 dashboard——日志停留在 "能搜到"这一层,无法按集群域快速切分,也看不出各域日志的构成与量级。

实测确认的可聚合维度(全部为 keyword 字段):

字段 取值 覆盖范围
cluster slurm / lsf / lustre / hpc / license 集群外 14 台全量(6 类采集全部打标)
log_source syslog / audit / cron / domain / flexlm / vnc 同上
kubernetes.namespace 各命名空间 K8s 容器日志(cluster 为空,占总量最大)

二、目标

Kibana 中提供一个「日志分域视图」Dashboard,可按集群域与日志类别下钻, 并覆盖 K8s 容器的命名空间分布与原始日志明细;全部对象以声明式文件管理,可幂等重放。

三、前置条件

  • Kibana 8.19.0 就绪,kubectl -n monitoring exec elasticsearch-0 -- curl -s http://kibana:5601/api/status 返回 200
  • ES 中已存在 filebeat-* 索引(两族:.ds-filebeat-* 集群内 / .ds-filebeat-host-* 集群外)
  • ~/Documents/k8s_ins/monitoring-p2-20261001/.venv 可用,config/default.yaml 中 kubeconfig 路径有效

四、影响范围

项 说明
目标 仅 Kibana saved objects(monitoring 命名空间内的 Kibana 实例)
是否停服 否。纯新增对象,不改 ES / Filebeat / Traefik,采集与检索不受影响
数据面 不触碰索引与 ILM;cluster 字段口径未做任何变更
回滚 删除新建的 6 个 saved object 即可,见第七章

五、分步操作

步骤 1:下发声明式 saved objects

声明源位于 kibana/saved_objects/*.json(一个文件 = 一个 saved object):

文件 类型 id 作用
01-data-view.json index-pattern efk-all-logs 数据视图 filebeat-*,时间字段 @timestamp
02-viz-cluster-count.json lens efk-viz-cluster-count 按集群域统计日志量
03-viz-cluster-source.json lens efk-viz-cluster-source 域 × 日志类别堆叠
04-viz-namespace-count.json lens efk-viz-namespace-count K8s 命名空间分布(query: kubernetes.namespace: *)
05-search-recent-logs.json search efk-search-recent-logs 原始日志明细
06-dashboard-domain-overview.json dashboard efk-dashboard-domain-overview 组合视图(9 面板)+ 6 个 Options list 控件;默认时间窗 now-24h
07-viz-level-trend.json lens efk-viz-level-trend 日志级别趋势(后续追加)
08-viz-error-by-host.json lens efk-viz-error-by-host 异常来源 · 按主机(后续追加)
09-viz-error-by-pod.json lens efk-viz-error-by-pod 异常来源 · 按 K8s Pod(后续追加)
10-viz-service-count.json lens efk-viz-service-count 日志量 · 按服务(2026-10-02 追加,见步骤 4)
11-viz-service-level.json lens efk-viz-service-level 日志构成 · 服务 × 级别(2026-10-02 追加,见步骤 4)

下发通道:Kibana 未对集群外暴露 REST,统一经 ES Pod 内的 curl 访问集群内 kibana:5601。 等价的底层命令(以「写入单个对象」为例,实际由实现体批量下发):

kubectl --kubeconfig /home/rain/.kube/config -n monitoring exec -i elasticsearch-0 -- \
  bash -lc 'E="elastic:$ELASTIC_PASSWORD"; \
            curl -s -u "$E" -H "kbn-xsrf: true" -H "Content-Type: application/json" \
                 -X POST "http://kibana:5601/api/saved_objects/_bulk_create?overwrite=true" \
                 --data-binary @-' < <(python3 -c 'import json,sys;print(json.dumps([json.load(open("kibana/saved_objects/02-viz-cluster-count.json"))]))')

预期:返回 {"saved_objects":[...]},无 errors 字段。 失败判断:非 200 或出现 errors → 检查 body 的 JSON 合法性(Kibana 不校验 attributes,畸形数据会静默写入)。

实现体:modules/kibana_views.py(KibanaViews.apply)。

步骤 2:dashboard 的面板组装(本步是本次唯一的坑)

dashboard 的面板不手写内联,而是在 06-*.json 里声明:

"panels": [
  {"panel": "p1", "ref": "efk-viz-cluster-count", "x": 0, "y": 0, "w": 24, "h": 15, "title": "日志量 · 按集群域"}
]

modules/kibana_views.py::_expand 会读入被引用对象的 attributes,按 Kibana 8.x by-reference 面板格式生成 panelsJSON 与 references:

[{"type":"lens",
  "embeddableConfig":{"savedObjectId":"efk-viz-cluster-count","title":"日志量 · 按集群域"},
  "panelIndex":"p1",
  "gridData":{"x":0,"y":0,"w":24,"h":15,"i":"p1"}}]

注意事项与坑(务必留痕):

  1. savedObjectId 必须放在 embeddableConfig 内,不能放面板顶层。 放在顶层时 Kibana 不报错,但面板渲染为空并抛出 Cannot read properties of undefined (reading 'map')——极难定位。
  2. references.name 必须等于该面板的 panelIndex(如 p1),不是 panel_p1。
  3. 本格式取自 Kibana Dashboards API 的实测产物(PUT/POST /api/dashboards/dashboard[/{id}]), 而非文档推断:先用该 API 创建一枚探针 dashboard,再 GET /api/saved_objects/dashboard/<id> 读回它生成的 panelsJSON,即得标准形态。
  4. dashboard 的 references 由 _expand 一并生成,声明文件里写 "references": [] 即可。
  5. index-pattern 的 fields 会被 Kibana 回写,比对时已在 IGNORED_ATTRS 中跳过, 否则每次 apply 都会误判为漂移。

步骤 3:执行下发(幂等)

cd /home/rain/Documents/k8s_ins/monitoring-p2-20261001
.venv/bin/python main.py kibana --dry-run    # 只列差异,零副作用
.venv/bin/python main.py kibana              # 真实下发

预期(首次建立):除 index-pattern 外全部 pending → 已写入 N 个对象 → 复检通过。 预期(复跑):全部 done → 全部对象已与声明一致,无变更(幂等跳过)。

实现体:main.py 子命令 kibana → deployer.py::cmd_kibana。

步骤 4:服务维度可视化补齐(2026-10-02,两轮)

背景:服务维度(service.name)此前只落地为「筛选控件 + 明细表一列」,没有任何按服务分组的图表,界面上看不出效果。第一轮补一枚「日志量 · 按服务」面板;上线后用户反馈「只有一根柱、且界面看不到服务控件」,据此做第二轮:收时间窗、改控件级联、补「服务 × 级别」面板。只改声明文件,不动采集链路。

做法(两轮,均只改声明文件,不动采集链路):

第一轮(补 p8「日志量 · 按服务」):

  1. 新建 kibana/saved_objects/10-viz-service-count.json——照抄 02-viz-cluster-count.json 的结构(已在 Kibana 实测渲染通过),只改 id/title、sourceField: cluster → service.name、query 设为 service.name: *(滤掉空值,避免空桶占位)。
  2. 在 06-*.json 的 panels 声明里挂载 p8,明细面板 p6 由 y=36 下移到 y=48。

第二轮(上线后发现「界面只有一根柱、且「服务」控件不渲染」,据此修可见性与信息量):

  1. 新建 kibana/saved_objects/11-viz-service-level.json——照抄 03-viz-cluster-source.json 的双维度堆叠结构(splitAccessors),cluster → service.name、log_source → log.level;挂载为 p9,p6 再下移到 y=60。
  2. 06-*.json 另改两处:
  3. timeFrom: now-7d → now-24h——服务维度只对上线后新采日志有值,7 天窗口覆盖率仅 3.9%,面板与控件近乎空白;
  4. controlGroupInput.chainingSystem: HIERARCHICAL → NONE——级联模式下「服务」控件在界面不渲染(详见坑 3),改独立筛选后 6 个控件恒显。⚠ 第四轮已改回 HIERARCHICAL(关级联会让"选主机后仍列全部服务")。
{"panel": "p9", "ref": "efk-viz-service-level", "x": 0, "y": 48, "w": 48, "h": 12, "title": "日志构成 · 服务 × 级别"}

第三轮(让下钻同屏可见):p8 / p9 改为并排(各 24 宽、同一行 y=36),「最近日志明细」上移到该行正下方(y=48、48×16)并改标题为「最近日志明细(点上方服务柱 → 只看该服务的日志)」——此前明细表在最底部,点服务柱后联动结果看不到,"点服务看日志"的体验无法感知。

第四轮(恢复级联):chainingSystem 由 NONE 改回 HIERARCHICAL。触发:用户选中主机后「服务」控件仍列出全部 54 个服务(含 K8s 侧服务)——根因即第二轮为修控件显示而关掉的级联。改后重放 main.py kibana,幂等通过。本轮未做浏览器复验(无头浏览器卡在 /internal/data_views/fields 请求上),需人工确认两点:① 6 个控件仍全部显示;② 选中主机后「服务」只列该主机出现过的服务。

注意事项与坑:

  1. seriesType 不要自创:沿用已验证的 bar_stacked;双维度堆叠用 splitAccessors(参照 03-*.json),不是加第二层 x 轴。Kibana 不校验 saved object 结构,格式写错会静默入库、只在浏览器里渲染为空(见步骤 2 坑 1)。
  2. 数据口径:面板只反映「时间窗内 service.name 非空」的文档;实测 24h 内 8.2 万条中 48% 的 log.level 为空(auditd / openldap / traefik 等主机侧日志),故「服务 × 级别」图例会带一段 (空),属正常而非故障。
  3. 「服务」控件的级联取舍(第二轮 ⇄ 第四轮的往返,务必看):Kibana 侧对象里 ctrl-service 始终存在(GET /api/saved_objects/dashboard/<id> 可查,panelsJSON 含 6 个控件),但界面只渲染 5 个。第二轮据此把 chainingSystem 由 HIERARCHICAL 改成 NONE,控件随即显示——代价是丢掉级联:选中主机后「服务」仍列出全部 54 个服务(含 K8s 侧服务),第四轮已改回 HIERARCHICAL。
  4. 教训:不要把「某控件不显示」直接归因为级联——当时并未排除浏览器缓存、上游控件空值等其它可能,也没做硬刷新复判就改了配置,代价是用户看到"选主机还列全部服务"。
  5. 回退姿势:级联开关就是一个字段,声明里改一行 + main.py kibana 重放即可(第四轮即如此)。
  6. 改完控件配置必须硬刷新(关键):Kibana 的 dashboard 只在页面加载时读取 saved object;下发后若只是点「Refresh」(刷新数据),前端仍在用旧配置——用户两次误判"级联没生效"均源于此。实测(2026-10-02,浏览器实机):硬刷新后选中 slurm-master-01,「服务」由 54 options 收敛为 11 options(auditd 2,698 / systemd 1,596 / systemd-logind 71 / user@0 56 / sshd 48 …),与 ES 直接聚合一致。
cd /home/rain/Documents/k8s_ins/monitoring-p2-20261001
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana --dry-run   # 预期:2 个 pending,其余 done
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana             # 真实下发

预期:已写入 2 个对象 → 复检通过;复跑一次全部 done(幂等)。 失败判断:写入后复检未通过 → 检查声明 JSON 是否畸形(Kibana 不回显结构错误,看渲染需人工)。

实现体:modules/kibana_views.py::_expand;备份与局部回滚台账 backup/service-panel-20261002/README.md。

步骤 5:Discover 服务浏览入口(2026-10-02,HANDOFF 组三 #11)

背景:分域视图只能在看板内点选,缺一条「进 Discover 自由按服务/主机检索」的路径;而 Kibana 8.19 未开放 drilldown 的声明式 API(见 §9 结论),原生下钻无法下发。故补「保存的搜索 + 入口链接」作为替代,只新增对象、不动采集链路。

做法:

  1. 新建 kibana/saved_objects/16-search-service-browse.json:type search、id efk-search-service-browse,kuery service.name: *,列 service.name / host.name / log.level / log_source / message,按 @timestamp 倒序。
  2. 新建 kibana/saved_objects/15-markdown-discover-links.json:type visualization、markdown 面板,两条链接——打开上述保存搜索 / 在 Discover 打开全部日志(index:'efk-all-logs')。
  3. 06-dashboard-domain-overview.json 的 panels 追加 p13(该 markdown,y=96 w=48 h=8)。
cd /home/rain/Documents/k8s_ins/monitoring-p2-20261001
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana --dry-run   # 预期:3 个 pending(dashboard + 2 个新建)
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana             # 预期:已写入 3 个对象 → 复检通过
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana             # 复跑:无变更(幂等跳过)

预期:已写入 3 个对象 → 复检通过:3 个对象均已与声明一致;复跑 无变更(幂等跳过)。 失败判断:复检不通过 → 检查两个新 JSON 是否畸形(Kibana 不校验结构,畸形会静默入库、渲染为空); 回滚见第 7 章与 backup/kibana-discover-20261002/README.md。 实现体:modules/kibana_views.py(_expand 把 dashboard 的 panels 声明展开为 panelsJSON + references)。

注意事项与坑:

  1. markdown 链接含 ( ) ' 等字符,必须用 <URL> 尖括号包裹,否则 markdown 解析会截断链接。
  2. 保存搜索的 id(efk-search-service-browse)与 markdown 链接路径必须一致,改名要同步两处。
  3. ⚠ Discover 打开保存搜索的 URL 形式(#/view/<id>)未做浏览器实测(无头浏览器不可用); 若点开未落在该搜索上,改走「Discover → 选 data view efk-all-logs」路径。

六、验证

  1. 幂等:连续执行两次,第二次零变更(见步骤 3 预期)。
  2. 对象齐备: bash kubectl -n monitoring exec elasticsearch-0 -- bash -lc 'E="elastic:$ELASTIC_PASSWORD"; \ curl -s -u "$E" "http://kibana:5601/api/saved_objects/_find?type=dashboard&per_page=10" | head -c 300' 预期:total ≥ 1,包含 efk-dashboard-domain-overview。
  3. 渲染(人工):浏览器打开 https://kibana.rainchen.top/app/dashboards#/view/efk-dashboard-domain-overview 预期:顶部 7 个控件(主机 / K8s Pod / 服务 / 级别 / 集群域 / 类别 / 业务服务,均应可见)+ 13 个面板全部有内容 (含「日志量 · 按服务」与「日志构成 · 服务 × 级别」;默认时间窗 24h 即有画面,收到最近 1 小时最清晰), 无 Cannot read properties of undefined 报错。
  4. 漂移检测并入只读命令: bash .venv/bin/python main.py drift # 退出码 0=无漂移 / 1=有漂移 / 2=探测失败

七、回滚

删除本次新建的 11 个对象即可,ES 数据与采集链路不受影响:

kubectl -n monitoring exec elasticsearch-0 -- bash -lc 'E="elastic:$ELASTIC_PASSWORD";
for t in lens:efk-viz-cluster-count lens:efk-viz-cluster-source lens:efk-viz-namespace-count \
         lens:efk-viz-level-trend lens:efk-viz-error-by-host lens:efk-viz-error-by-pod \
         lens:efk-viz-service-count lens:efk-viz-service-level search:efk-search-recent-logs \
         dashboard:efk-dashboard-domain-overview index-pattern:efk-all-logs \
         visualization:efk-markdown-discover-links search:efk-search-service-browse; do
  curl -s -o /dev/null -w "$t %{http_code}\n" -u "$E" -H "kbn-xsrf: true" \
       -X DELETE "http://kibana:5601/api/saved_objects/${t%%:*}/${t##*:}"
done'

预期:每行 200。随后删除 kibana/ 目录并把 main.py / deployer.py 回退到改动前状态 (改动清单与手工回退步骤见 backup/kibana-domain-view-20261001/README.md)。

仅撤销 2026-10-02 新增的「日志量 · 按服务」面板(局部回滚,不影响其余 7 个面板):

cd /home/rain/Documents/k8s_ins/monitoring-p2-20261001
rm kibana/saved_objects/10-viz-service-count.json                       # 高危:删除文件,路径已明确
cp -p backup/service-panel-20261002/kibana/saved_objects/06-dashboard-domain-overview.json.bak.20261002-002221 \
      kibana/saved_objects/06-dashboard-domain-overview.json            # 高危:覆盖配置,原件已备份
PYTHONPATH="$HOME/py_public_modules" .venv/bin/python main.py kibana     # dashboard 回到 7 面板
kubectl --kubeconfig /home/rain/.kube/config -n monitoring exec elasticsearch-0 -- \
  bash -lc 'E="elastic:$ELASTIC_PASSWORD"; curl -s -u "$E" -H "kbn-xsrf: true" \
  -X DELETE "http://kibana:5601/api/saved_objects/lens/efk-viz-service-count"'

详见 backup/service-panel-20261002/README.md。

八、风险说明

风险 缓解
Kibana 不校验 saved object 结构,畸形数据静默写入 面板格式由 _expand 统一生成,不手写;渲染以浏览器实测为准
Kibana 版本升级后 embeddable 格式变化 面板字段集中在 _expand 一处,升级后只需改这一处并复跑验证
时间窗默认 7 天(timeRestore: true)可能让人误判无数据——2026-10-02 实测:service.name 仅上线约 1h,7d 窗口覆盖 3.9%,「服务」控件近乎为空 Dashboard 描述里写明;时间选择器可随时改;排查「新字段无数据」时先把窗口收到 1 小时
控件依赖 dataViewId=efk-all-logs,改 data view id 会失效 两处同源声明,改 id 时一并改

附录:交付物清单

文件 说明
kibana/saved_objects/01..11-*.json 声明式 saved objects(单一真源)
modules/kibana_views.py 幂等下发器(三态探测 + 子集比对 + 展开组装)
main.py / deployer.py 新增 kibana 子命令;drift 并入视图漂移检测
backup/kibana-domain-view-20261001/README.md 备份与回滚台账(首次建立)
backup/service-panel-20261002/README.md 备份与回滚台账(2026-10-02 新增「日志量 · 按服务」面板)

来源:/home/rain/Documents/k8s_ins/monitoring-p2-20261001/docs/SOP-kibana-domain-view.md | 最后更新:2026-10-02 23:06(由 build/sync.py 同步,请勿在站点侧修改)