在 Kibana 里造一把“运维瑞士军刀”:用插件模式打造专属 ES 可视化管理工具
你有没有过这样的经历?凌晨三点,告警群炸了,说是某个索引分片全红了。你赶紧打开 Cerebro 看集群状态,切到 Kibana 查监控图表,再翻出笔记本执行一串_cat/shards命令排查问题……几个系统来回切换,眼睛都花了。
这其实是很多 ES 运维团队的日常写照——工具不少,但“信息孤岛”林立。而更让人头疼的是,业务方想看一眼索引增长趋势,还得找人手动生成报表,沟通成本居高不下。
那有没有可能把所有这些操作,统一收进一个界面里?就像在 Kibana 里装上一把“定制化的运维瑞士军刀”,点一下就能巡检集群、分析瓶颈、甚至一键修复?
答案是:完全可以,而且官方早就给你留好了“扩展口”——Kibana 插件机制。
为什么非得是“插件”?直接做个独立 Web 应用不行吗?
当然可以做个独立系统,但你会发现很快就会陷入“重复造轮子”的泥潭:
- 用户要登录两次(一次 Kibana,一次你的系统);
- 权限模型得自己搞一套,和 Kibana 的 RBAC 对不上;
- UI 风格永远和 Kibana 差那么一点,体验割裂;
- 想调用 Elasticsearch?还得自己处理认证代理。
而如果你选择基于 Kibana 插件模式开发,这些问题全都迎刃而解:
✅身份统一:用户登录 Kibana 后,天然继承其角色权限,无需二次鉴权。
✅体验一致:UI 使用@elastic/eui组件库,风格和原生页面几乎无差别。
✅安全可控:通过asCurrentUser调用 ES,真正做到“你能查什么,就只能看到什么”。
✅维护省心:随 Kibana 升级自动更新,不用操心兼容性问题。
说白了,插件不是“外挂”,而是成为 Kibana 的一部分。你要做的,只是往这辆功能强大的车上,加装一个属于你自己的“驾驶舱”。
插件怎么工作?拆开看看它的“五脏六腑”
别被“插件”两个字骗了,它可不是简单的前端脚本注入。Kibana 的插件架构其实是一套完整的前后端协同扩展机制,分为两个核心部分:
1. 服务端插件(Server Plugin)——背后的“大脑”
运行在 Node.js 环境中,负责:
- 暴露自定义 API 接口(比如/api/cluster-health)
- 定时任务调度(例如每小时自动巡检)
- 调用 Elasticsearch 客户端获取数据
- 执行敏感操作(如修改索引设置)
它通过setup和start方法接入 Kibana 生命周期,就像给发动机加了个副油箱。
2. 客户端插件(UI Plugin)——前台的“脸面”
基于 React 构建,负责:
- 注册新菜单项(比如左侧导航栏加个“运维助手”)
- 渲染可视化页面(表格、图表、按钮操作区)
- 调用后端 API 并展示结果
两者通过标准 HTTP API 通信,结构清晰,职责分明。
📌 小知识:从 Kibana 7.x 开始,插件必须声明在
plugins/目录下,并提供kibana.json文件描述元信息,否则根本不会被加载。
动手实战:三步搭建一个“集群健康看板”原型
我们来写一个最简版本的 es 可视化管理工具,目标很简单:显示当前 ES 集群的基本信息。
第一步:定义插件身份
// plugins/es_admin_tool/kibana.json { "id": "es_admin_tool", "version": "1.0.0", "kibanaVersion": "8.11.0", "name": "ES Admin Tool", "description": "专为运维定制的可视化管理面板", "server": true, "ui": true, "requiredPlugins": ["data"] }就这么一个文件,Kibana 就知道你要注册一个叫es_admin_tool的插件,带前后端功能,依赖data插件(用于访问 ES 客户端)。
第二步:服务端暴露数据接口
// plugins/es_admin_tool/server/routes.ts import { IRouter } from 'kibana/server'; export function defineRoutes(router: IRouter) { router.get( { path: '/api/es_admin_tool/cluster_info', validate: false, }, async (context, request, response) => { try { const client = context.core.elasticsearch.client.asCurrentUser; // 获取集群基本信息 const info = await client.info(); // 获取索引统计 const stats = await client.indices.stats({}); return response.ok({ body: { cluster_name: info.body.cluster_name, version: info.body.version.number, node_count: info.body.nodes, indices_count: Object.keys(stats.body.indices).length, total_docs: stats.body._all.total.docs.count, total_store_size_mb: Math.round(stats.body._all.total.store.size_in_bytes / 1024 / 1024), }, }); } catch (error) { return response.customError({ statusCode: 500, body: error.message, }); } } ); }这里的关键在于asCurrentUser—— 它确保请求是以当前登录用户的权限发起的。如果这个用户没有查看.kibana索引的权限,那相关数据自然就不会返回,权限控制水到渠成。
第三步:前端渲染可视化界面
// plugins/es_admin_tool/public/application.tsx import React, { useEffect, useState } from 'react'; import { EuiPage, EuiPageBody, EuiTitle, EuiText, EuiLoadingContent } from '@elastic/eui'; import { useKibana } from 'src/plugins/kibana_react/common'; const EsAdminApp = () => { const kibana = useKibana(); const [data, setData] = useState<any>(null); useEffect(() => { const fetchData = async () => { const resp = await kibana.services.http.fetch('/api/es_admin_tool/cluster_info'); setData(resp); }; fetchData(); }, []); if (!data) { return ( <EuiPage> <EuiPageBody> <EuiLoadingContent lines={6} /> </EuiPageBody> </EuiPage> ); } return ( <EuiPage> <EuiPageBody> <EuiTitle size="l"> <h1>🎯 Elasticsearch 运维看板</h1> </EuiTitle> <EuiText> <ul> <li><strong>集群名称:</strong>{data.cluster_name}</li> <li><strong>ES 版本:</strong>{data.version}</li> <li><strong>节点数量:</strong>{data.node_count}</li> <li><strong>索引总数:</strong>{data.indices_count}</li> <li><strong>文档总量:</strong>{data.total_docs.toLocaleString()}</li> <li><strong>存储占用:</strong>{data.total_store_size_mb} MB</li> </ul> </EuiText> </EuiPageBody> </EuiPage> ); }; export const renderApp = (appMountParameters: any) => { const { element } = appMountParameters; ReactDOM.render(<EsAdminApp />, element); return () => ReactDOM.unmountComponentAtNode(element); };最后,在主插件文件中注册应用入口:
// plugins/es_admin_tool/public/plugin.ts import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { Plugin } from '@kbn/core/public'; export class EsAdminToolPlugin implements Plugin { public renderApp = (appMountParams: AppMountParameters) => { renderApp(appMountParams); }; public setup(core: CoreSetup) { core.application.register({ id: 'es_admin_tool', title: '运维助手', category: { id: 'management', label: '管理', order: 1, }, async mount(params) { this.renderApp(params); return () => {}; }, }); } public start() {} }刷新 Kibana,你会在左侧「管理」菜单下看到一个新的“运维助手”入口,点击进去就是我们刚刚写的页面。
如何避免把插件做成“性能炸弹”?
插件虽好,但也容易踩坑。我在实际项目中总结了几条血泪经验:
❌ 错误做法:前端频繁轮询/cluster_info
useEffect(() => { const interval = setInterval(fetchData, 1000); // 每秒一次!灾难! return () => clearInterval(interval); }, []);这样干轻则拖慢浏览器,重则把 ES 打垮。毕竟每个用户都在高频请求。
✅ 正确姿势:后端异步计算 + 缓存共享
// 利用 Kibana Scheduler 异步采集 core.scheduler.schedule({ id: 'health_monitor_task', taskType: 'es_admin.health_check', schedule: { interval: '5m' }, // 五分钟一次就够了 params: {}, }); // 数据存入 Redis 或内存缓存,所有用户共享一份最新结果前端只需读缓存,压力瞬间下降90%。
❌ 错误做法:前端拼接 DSL 查询
有些开发者图省事,直接让前端传一个 query 字符串过来,后端原样发给 ES。
这是典型的DSL 注入漏洞!恶意用户可以构造"query": { "match_all": {} }把整个集群扫一遍。
✅ 正确做法:后端做语义校验
// 白名单字段 + 分页限制 + 超时保护 const safeQuery = { query: { bool: { must: [{ term: { type: input.type } }], filter: [{ range: { '@timestamp': { gte: 'now-7d' } } }] } }, size: 100, _source: ['message', 'level'] };永远不要相信前端传来的原始查询。
实际应用场景:不止于“看”,更要能“管”
真正的价值不在于展示数据,而在于把运维动作可视化、流程化。举几个我们在生产环境落地的例子:
场景一:索引生命周期异常检测
- 自动扫描所有 ILP(Index Lifecycle Policy)关联的索引;
- 标记处于
red状态或分片未分配的索引; - 提供“一键触发 Rollover”按钮,后端校验条件后执行。
场景二:存储倾斜分析
- 计算各节点的数据分布差异;
- 用热力图展示哪个节点快满了;
- 推荐迁移命令或自动触发 shard reroute。
场景三:慢查询治理看板
- 结合 ES 的 slow log 和 APM 数据;
- 展示 Top 10 最耗时的查询语句;
- 关联到具体的服务名和负责人(来自 CMDB),推动优化。
这些功能单独看都不复杂,但一旦集成进 Kibana,就成了团队日常运维的“标配工具”,效率提升立竿见影。
写在最后:你的插件,可能是下一个爆款功能的起点
我见过太多团队,明明写了很实用的管理工具,却因为“只是个内部小系统”而不愿投入优化。结果每次分享都靠截图演示,别人想用还得单独部署。
但如果你把它做成 Kibana 插件呢?
它会出现在每个人的左侧菜单里,点开即用,风格统一,权限合规。久而久之,大家发现:“原来查集群状态不用去 Cerebro 了,Kibana 里就能搞定。”
这才是技术赋能业务的真正体现。
所以,别再满足于写脚本、贴文档、做 Excel 表格了。
拿起插件这把“手术刀”,把你那些零散的运维智慧,缝合成一个真正可用、好用、爱用的可视化系统。
当你某天看到同事熟练地点击你写的“一键诊断”按钮时,那种成就感,远比修完一个 bug 来得深刻。
如果你也正在构建类似的工具,欢迎在评论区交流心得。我们可以一起探讨如何让 Kibana 不只是“看数据的地方”,更是“管数据的地方”。