深入 Kibana 01 - 架构:浏览器、Node.js server 与 platform
Kibana 3 曾让浏览器直接连接 Elasticsearch,Kibana 4 以后却在中间加入了 Node.js server。这个变化不只是部署形态调整:凭据隔离、后台任务、报表生成和插件生命周期都需要一个应用服务层。沿着一次浏览器请求向下追踪,才能看清三层架构各自承担的职责。
三层结构
请求从浏览器出发,经过 Node.js server,最终到达 Elasticsearch。每一层的职责边界如下:
1 | |
浏览器端是一批 React 应用,以插件 bundle 的形式加载。每个 Kibana 功能区(Discover、Lens、Dashboard、Alerting)都是独立插件,在浏览器里注册到 core 提供的客户端框架。
本文按 8.13 的历史锚点展开;截至 2026-08-25,Elastic 文档里的 current 线已经是 9.x,最新公开 GA 版本为 9.4.4。
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 端做配置校验。
platform 插件模型
Kibana 7.x 完成了从 Legacy Platform 到 platform 的迁移,插件生命周期从简单的"注册一堆全局对象"变成了显式的 setup/start/stop 三段式。
1 | |
同样的三段式在浏览器端也有对应,browser-side 插件的 setup 阶段注册导航菜单项和路由,start 阶段初始化与 server 的连接。
平台的核心约束是:插件间依赖必须在 setup 阶段显式声明,不允许全局单例引用。这一设计让 Kibana 的依赖关系可以在启动时做图遍历检查,循环依赖会直接报错;开发态的 bundle 变化可以更快反馈,但生产部署仍以完整实例重启为准,不存在通用的“单插件热更新”。
请求链路追踪
一次从 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 系列索引的迁移状态。当前的 /api/status 响应外层还包含 metrics、name、status、uuid、version;其中 status.core.savedObjects 仍然是判断迁移是否完成的关键字段。
用 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 客户端。当前 API 名称是 core.elasticsearch.client.asInternalUser 和 context.core.elasticsearch.client.asCurrentUser;前者用于系统级操作(如 .kibana*),后者在路由处理器里代表当前请求用户。
模式提炼
三层架构里值得提炼的两个设计决策:
凭据作用域分离。server 用 asInternalUser 操作系统索引,用 asCurrentUser 代理用户请求。两个客户端的凭据完全独立,保证了 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 |
| 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。自 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)观察当前实例的任务调度状态,重点看 drift、load 和容量相关字段,理解多实例下的任务竞争模型。
用 curl -s http://localhost:5601/api/status 在 Kibana 启动后的不同阶段多次调用,观察 status.core.savedObjects 从 initializing 变为 available 的过程,理解迁移锁的存在。
