深入 Kibana 01 - 架构:浏览器、Node.js server 与 New Platform
上一篇确立了"Kibana 的状态存在 Elasticsearch 里"这条主线。这一篇进入架构层:浏览器、Node.js server 与 Elasticsearch 三层之间发生了什么。三层架构容易被误解成"Kibana server 只是一个反向代理"。更准确的说法是:Node.js 层承载了凭据隔离、后台任务、报表生成、插件生命周期等纯粹属于应用平台的职责,不是透明转发。本文只抓一个问题:为什么 Kibana 需要自己的 Node.js 服务,这个服务在请求链路里做了什么。
三层结构
请求从浏览器出发,经过 Node.js server,最终到达 Elasticsearch。每一层的职责边界如下:
1 | |
浏览器端是一批 React 应用,以插件 bundle 的形式加载。每个 Kibana 功能区(Discover、Lens、Dashboard、Alerting)都是独立插件,在浏览器里注册到 core 提供的客户端框架。
Node.js server 拦截所有浏览器到 ES 的请求,在这里验证凭据、注入 Kibana 自己的认证头,然后以 Kibana 服务账号的身份向 ES 发出实际请求。ES 看到的始终是 Kibana server 的凭据,浏览器端的用户凭据不直接到达 ES。
Elasticsearch 同时扮演两个角色:业务数据的存储和查询后端,以及 Kibana 自身状态的持久化存储(.kibana* 系列索引)。
为什么 Kibana 必须有自己的 server
纯浏览器直连 ES 的架构(Kibana 3 的方式)在以下场景下失效:
凭据安全问题。浏览器直连要求把 ES 的地址和认证信息暴露给前端。任何打开页面的用户都能拿到这些凭据直接访问 ES。加一层 server 后,凭据留在 server 端,浏览器只持有对 Kibana HTTP 端点的会话 token。
后台任务调度。告警规则要定时执行查询——用户不开着浏览器这个任务也得跑。Kibana 的 Task Manager 是一个跑在 server 端的调度器,把任务定义存在 .kibana_task_manager 索引,server 启动后持续轮询待执行任务。这类长周期后台逻辑在浏览器里没有稳定的执行环境。
报表生成。Reporting 功能用 headless Chromium 渲染 Kibana 页面截图,或把 Discover 结果导出为 CSV。headless 浏览器和文件系统 IO 都只能在 server 端完成。
机器学习与数据摄入辅助。自 7.x 起 Kibana 内置了多个数据向导(Fleet、Integrations),这些功能需要代理 ES 的 Index Template API 调用,并在 server 端做配置校验。
New Platform 插件模型
Kibana 7.x 完成了从 Legacy Platform 到 New Platform 的迁移,插件生命周期从简单的"注册一堆全局对象"变成了显式的 setup/start/stop 三段式。
1 | |
同样的三段式在浏览器端也有对应,browser-side 插件的 setup 阶段注册导航菜单项和路由,start 阶段初始化与 server 的连接。
New Platform 的核心约束是:插件间依赖必须在 setup 阶段显式声明,不允许全局单例引用。这一设计让 Kibana 可以在不重启整个 server 的条件下热更新单个插件(自 8.x 起对部分插件生效),也让插件间的依赖关系可以在启动时做图遍历检查,循环依赖会直接报错。
请求链路追踪
一次从 Discover 发出的查询,在三层里的路径如下:
1 | |
浏览器看到的是 Kibana 的 internal API(路径通常是 /internal/ 或 /api/),从不直接调 ES。这一层隔离让 Kibana 可以在 server 端做限流、做结果缓存、做字段格式化,而浏览器插件不需要知道 ES 的实际地址。
实验:通过 Status API 观察 server 信息
Kibana 暴露了一个公开的 status 端点,可以不登录查看 server 侧基本信息:
1 | |
响应里的关键字段:
1 | |
status.core.elasticsearch 反映 Kibana server 与 ES 的连通状态,status.core.savedObjects 反映 .kibana 系列索引的迁移状态。server 启动时会等这两项都变为 available 才开始接受用户请求。
用 ES 的 _nodes/stats 也可以观察到 Kibana 对 ES 的 HTTP 连接:
1 | |
在返回的 nodes.<node_id>.http.total_opened 里,持续打开的 HTTP 连接中有一部分来自 Kibana server 的连接池(自 8.x 起默认使用长连接池,减少 TLS 握手开销)。
将实验结果映射回内部对象
Status API 的 status.core.savedObjects 对应 server 端的 SavedObjectsService——这个服务在 setup 阶段接收各插件注册的类型定义,在 start 阶段完成 .kibana 索引的 mapping 迁移(如有)后才标记为 available。
status.core.elasticsearch 对应 ElasticsearchService,管理 Kibana server 到 ES 的 HTTP 客户端连接池。这个客户端有两个实例:internalClient(用 kibana system 账号,用于操作 .kibana* 系列索引)和 scopedClient(用请求用户的凭据,用于代理用户的业务查询)。
模式提炼
三层架构里值得提炼的两个设计决策:
凭据作用域分离。server 用 internalClient 操作系统索引,用 scopedClient 代理用户请求。两个客户端的凭据完全独立,保证了 Kibana 系统索引的操作权限不会因为一个低权限用户的请求而受影响。
插件生命周期显式化。setup/start/stop 强制插件在正确的时间点注册服务和路由,不允许"在某个函数里偷偷初始化全局对象"。代价是插件开发者要理解两阶段启动的约束,收益是整个系统的依赖关系可静态分析。
工程迁移表
| Kibana 概念 | Grafana 对应 | BI 工具对应 | Next.js / Nuxt 模式 |
|---|---|---|---|
| Node.js server 凭据代理 | Grafana backend plugin data proxy | BI server-side JDBC/ODBC driver | API routes with server-side fetch |
| New Platform setup/start | Grafana plugin activate() |
无显式生命周期 | nuxt.config module setup hooks |
| Task Manager | Grafana internal scheduler (alerting) | BI scheduled reports engine | Vercel Cron Jobs / BullMQ |
| Reporting (headless Chrome) | Grafana image renderer plugin | BI PDF export service | Puppeteer in serverless function |
| Plugin bundle splitting | Grafana lazy panel loading | 无直接对应 | Next.js dynamic imports |
常见误解
Kibana server 只是 ES 的反向代理。实际上 server 端有 Task Manager、Reporting、Alerting executor、Saved Objects migration 等完全不是代理的逻辑。纯粹的代理不需要自己的数据存储,而 Kibana 依赖 .kibana_task_manager 索引来协调多实例下的任务分配。
多个 Kibana 实例会造成配置冲突。多实例模式下(水平扩展),每个实例都连同一个 ES 集群,.kibana 索引是共享存储。Task Manager 用 optimistic locking(版本号 + seq_no)来协调任务认领,保证同一任务只被一个实例执行。
升级 Kibana 时 ES 的业务数据会受影响。Kibana 的迁移只写 .kibana* 系列系统索引,不触碰业务索引。.kibana_8.x.0_001 这类带版本号的索引是每次大版本升级后新建的,旧索引保留一段时间后由迁移脚本归档或删除。
浏览器插件可以直接调 ES API。自 New Platform 起 ES 的地址和凭据对浏览器端插件完全不可见,插件只能通过 data.search 或 http.fetch 这类 core 提供的 client 间接访问 ES。
练习
在 Dev Tools 里执行 GET _cat/indices/.kibana*?v,观察有多少个 .kibana 系列索引,它们的文档数量分别是多少。对比 Kibana 大版本号和索引名中的版本号。
在 Kibana 的 Stack Management → Task Manager Health 页面(路径 /app/management/insightsAndAlerting/taskManager)观察当前实例的任务调度状态,记录 claim_conflicts 指标,理解多实例下的任务竞争模型。
用 curl -s http://localhost:5601/api/status 在 Kibana 启动后的不同阶段多次调用,观察 status.core.savedObjects 从 initializing 变为 available 的过程,理解迁移锁的存在。
系列导航
| 篇 | 主题 | 核心问题 |
|---|---|---|
| 00 | 导读:状态存在 ES 里 | Kibana 是应用平台,不是 GUI |
| 01(本篇) | 架构:三层与 New Platform | 为什么需要 Node.js server |
| 02 | Saved Object:统一状态模型 | type / attributes / references |
| 03 | Data View:查询前的字段抽象 | 时间字段、字段格式化、运行时字段 |
