Kibana 与 OpenSearch Dashboards 搜索控制台工具手册
“Discover 没数据”可能是五种完全不同的故障
告警说支付接口错误率上升,值班同学打开搜索控制台,Discover 却显示没有结果。他先把时间范围从 15 分钟放到一周,又把索引模式改成 *,页面终于刷出日志,同时把大量历史分片拖进查询,并暴露了其他业务线的字段。真正的故障可能只是浏览器时区、时间字段、data view、space/tenant 或索引权限不匹配;“搜到数据”并不代表排障动作正确。
Kibana 与 OpenSearch Dashboards 都是有状态的 Web 应用,不是 Elasticsearch/OpenSearch 的静态皮肤。浏览器请求先到控制台服务,控制台解析用户会话、space 或 tenant、saved object 和功能权限,再以用户身份或内部服务身份访问搜索集群。Discover、Dev Tools、导出和管理页面只是不同入口,最终能读写哪些索引仍由集群授权决定。
这条链路决定了排障顺序:先确认控制台版本和后端配对,再确认登录身份与工作空间,然后检查 data view/index pattern、时间字段和查询,最后才判断采集链路。反过来扩大时间窗和索引通配符,会把一个显示问题升级成容量和数据越权问题。
先选对产品和版本,再谈页面怎么点
Kibana 与 Elasticsearch 属于同一 Elastic Stack。官方 Kibana 安装说明 要求整套组件保持相同版本;不同 major 不受支持,Kibana minor 高于 Elasticsearch 也不受支持。升级过程中可以短暂让 Elasticsearch minor 高于 Kibana,但最终仍应对齐到相同 patch。Kibana 发行包自带 Node.js,使用外部维护的 Node.js 运行 Kibana不受支持。
OpenSearch Dashboards 应与 OpenSearch 及其插件保持相同的 major、minor、patch。官方 Dashboards 安装入口 提供 Docker、tarball、RPM、Debian、Helm 和 Windows 发布物。发行包自带 Node.js;3.5 及之后的发行线包含 Node.js 22。确需替换运行时时,启动脚本会按 NODE_OSD_HOME、NODE_HOME、发行包和系统 PATH 的顺序寻找,官方兼容区间为 >=14.20.1 <23,但生产上优先使用发行包自带运行时,避免把额外变量引入升级。
两者都常监听 5601,端口相同不代表协议、配置、插件或 saved object 兼容。Kibana 不能连接 OpenSearch,OpenSearch Dashboards 也不是 Kibana 的无缝替换品。选型首先跟随后端搜索引擎和许可策略,而不是比较两个界面哪个更顺手。
在隔离开发机上跑起两个可清理的实验环境
下面的 Docker 命令只绑定回环地址,并关闭安全插件以缩短本地实验链路。关闭安全的实例不能部署到共享开发网、办公网或云主机。内存不足时容器可能退出,先用 docker logs 取证,不要反复重启掩盖原因。
Kibana 与 Elasticsearch 使用同一版本变量:
export STACK_VERSION=9.4.2
docker network create elastic-console-lab
docker run -d --name es-console-lab \
--network elastic-console-lab \
-p 127.0.0.1:9200:9200 \
-e discovery.type=single-node \
-e xpack.security.enabled=false \
-e ES_JAVA_OPTS="-Xms1g -Xmx1g" \
docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
docker run -d --name kibana-console-lab \
--network elastic-console-lab \
-p 127.0.0.1:5601:5601 \
-e ELASTICSEARCH_HOSTS=http://es-console-lab:9200 \
docker.elastic.co/kibana/kibana:${STACK_VERSION}OpenSearch 与 Dashboards 同样固定版本:
export OPENSEARCH_VERSION=3.7.0
docker network create opensearch-console-lab
docker run -d --name opensearch-console-lab \
--network opensearch-console-lab \
-p 127.0.0.1:19200:9200 \
-e discovery.type=single-node \
-e DISABLE_SECURITY_PLUGIN=true \
-e OPENSEARCH_JAVA_OPTS="-Xms1g -Xmx1g" \
opensearchproject/opensearch:${OPENSEARCH_VERSION}
docker run -d --name dashboards-console-lab \
--network opensearch-console-lab \
-p 127.0.0.1:15601:5601 \
-e 'OPENSEARCH_HOSTS=["http://opensearch-console-lab:9200"]' \
-e DISABLE_SECURITY_DASHBOARDS_PLUGIN=true \
opensearchproject/opensearch-dashboards:${OPENSEARCH_VERSION}分别打开 http://127.0.0.1:5601 和 http://127.0.0.1:15601。安装完成的证据是后端根 API 可响应、控制台日志显示服务就绪、浏览器页面可加载;只看到容器 Up 还不够。版本不配对时,日志通常会明确报告不兼容或插件初始化失败。
curl -s http://127.0.0.1:9200/
curl -s http://127.0.0.1:19200/
docker logs --tail 100 kibana-console-lab
docker logs --tail 100 dashboards-console-lab生产连接配置是一份身份与信任契约
自建 Kibana 的 kibana.yml 至少需要明确监听、公开地址、后端端点、服务身份和 CA:
server.host: "127.0.0.1"
server.port: 5601
server.publicBaseUrl: "https://kibana.example.test"
elasticsearch.hosts:
- "https://es-coordinator-a.example.test:9200"
- "https://es-coordinator-b.example.test:9200"
elasticsearch.serviceAccountToken: "${KIBANA_SERVICE_ACCOUNT_TOKEN}"
elasticsearch.ssl.certificateAuthorities:
- "/etc/kibana/certs/elastic-ca.pem"
elasticsearch.ssl.verificationMode: full
xpack.security.encryptionKey: "${KIBANA_SECURITY_KEY}"
xpack.encryptedSavedObjects.encryptionKey: "${KIBANA_SAVED_OBJECTS_KEY}"
xpack.reporting.encryptionKey: "${KIBANA_REPORTING_KEY}"server.host 决定进程监听在哪个网卡。直接设为 0.0.0.0 会扩大暴露面,生产通常只监听回环或受控内网,由反向代理终止外部 TLS。server.publicBaseUrl 决定重定向和外链使用的公共地址,代理路径或协议配错会造成登录循环、Cookie 丢失和分享链接指向内网。
elasticsearch.hosts 是 Kibana 服务到 Elasticsearch 的后端连接,不是浏览器访问地址。服务账号 token 用于 Kibana 自身维护 saved objects、任务和会话,不能替代最终用户权限。CA 与 verificationMode: full 同时校验证书链和主机名;把验证改为 none 会让中间人伪装后端。
三个 encryption key 保护会话、加密 saved objects 和报表任务。多实例部署必须使用相同且持久化的 key,否则用户会随机掉线、连接器秘密无法解密或报表失败。key 不能提交仓库,也不能在每次 Pod 重建时随机生成。轮换要按产品支持流程执行,并预先评估旧对象能否解密。
OpenSearch Dashboards 的等价配置位于 opensearch_dashboards.yml:
server.host: "127.0.0.1"
server.port: 5601
opensearch.hosts:
- "https://os-coordinator-a.example.test:9200"
- "https://os-coordinator-b.example.test:9200"
opensearch.username: "kibanaserver"
opensearch.password: "${OPENSEARCH_DASHBOARDS_PASSWORD}"
opensearch.ssl.certificateAuthorities:
- "/etc/opensearch-dashboards/certs/root-ca.pem"
opensearch.ssl.verificationMode: full
opensearch.requestHeadersAllowlist:
- authorization
- securitytenant
opensearch_security.multitenancy.enabled: true
opensearch_security.readonly_mode.roles:
- "kibana_read_only"opensearch.username/password 是 Dashboards 服务身份。用户登录后,Security 插件仍需传播用户认证和 tenant 请求头;漏掉 authorization 或 securitytenant 会表现为登录后 401/403、tenant 丢失或对象出现在错误空间。只读模式角色主要影响界面行为,真正的索引读写能力仍由 OpenSearch role 的 cluster、index 和 tenant permission 决定。
密码、service token、OIDC client secret 和加密 key 由 secret store 挂载或注入。配置文件只保留变量引用。控制台后端能访问整个集群时,它本身就是高价值入口:网络策略只允许反向代理到 5601、只允许控制台到协调节点 9200,并把管理 API 与普通查询流量分开审计。
正向实验:建立最小数据,再从 API 追到 Discover
在前面的回环实验环境中,用后端 API 写入三条无敏感信息的日志。Kibana 对应端口是 9200,OpenSearch 对应端口是 19200;以下以 Elasticsearch 为例:
NOW="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
curl -sS -X PUT 'http://127.0.0.1:9200/app-lab-logs' \
-H 'Content-Type: application/json' \
-d '{
"mappings": {
"properties": {
"@timestamp": {"type": "date"},
"service": {"type": "keyword"},
"status": {"type": "keyword"},
"trace_id": {"type": "keyword"},
"message": {"type": "text"}
}
}
}'
curl -sS -X POST 'http://127.0.0.1:9200/_bulk?refresh=wait_for' \
-H 'Content-Type: application/x-ndjson' \
--data-binary "{\"index\":{\"_index\":\"app-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"checkout\",\"status\":\"ERROR\",\"trace_id\":\"trace-a\",\"message\":\"payment timeout\"}
{\"index\":{\"_index\":\"app-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"checkout\",\"status\":\"OK\",\"trace_id\":\"trace-b\",\"message\":\"request complete\"}
{\"index\":{\"_index\":\"app-lab-logs\"}}
{\"@timestamp\":\"${NOW}\",\"service\":\"catalog\",\"status\":\"OK\",\"trace_id\":\"trace-c\",\"message\":\"request complete\"}
"NOW 在执行时生成 UTC 时间,refresh=wait_for 让实验等待刷新可见性,避免“刚写完搜不到”的时间竞争。预期 bulk 响应的 errors 为 false,索引有三个文档。
在 Kibana 的 Dev Tools Console 执行:
GET app-lab-logs/_search
{
"size": 10,
"timeout": "5s",
"track_total_hits": 1000,
"_source": ["@timestamp", "service", "status", "trace_id"],
"sort": [{"@timestamp": "asc"}],
"query": {
"bool": {
"filter": [
{"term": {"service": "checkout"}},
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}}
]
}
}
}预期 hits.total.value 为 2,返回字段不包含 message。Console 发送的是集群 REST API,size 只限制返回命中,不限制扫描范围;track_total_hits 限制精确计数的工作量,timeout 给请求设置等待预算,但二者都不能替代服务端并发、断路器、分片和工作负载治理。响应出现 timed_out、分片失败或命中关系为下界时,不能把当前结果当作完整事实。过滤时间和精确 keyword 字段能使意图更明确。
随后创建只匹配 app-lab-logs 的 data view,时间字段选择 @timestamp。进入 Discover 后把时间窗口设为最近五分钟,查询 service: checkout。页面应显示两条记录。若 Console 有两条而 Discover 没有,后端数据和索引权限已经初步成立,问题集中在 space、data view、时间字段、浏览器时区或 Discover 查询语法,不应再怀疑采集器。
OpenSearch 侧把端口换成 19200 即可建立同结构样本,在 Dashboards Dev Tools 执行相同 Query DSL。Discover 默认可使用 DQL 或 Lucene;Query Workbench 面向 SQL/PPL。不同查询语言的字段匹配、管道和聚合语义不同,团队模板要写明语言,不能只保存一段没有入口说明的查询文本。
反向实验一:时间字段选错会制造“零结果”
在同一 data view 中故意把时间字段设为不存在或业务含义不同的字段,或者把 Discover 窗口移到样本时间之外。预期页面显示零结果,但 Dev Tools 的精确 _search 仍返回两条。这组对照把“数据不存在”与“UI 时间过滤掉数据”分开。
取证时记录四项:space/tenant、data view/index pattern、时间字段、绝对起止时间与浏览器时区。不要只截一张“0 hits”的页面,因为截图不会保留所有过滤器。恢复正确时间字段和窗口后应立即看到两条;若仍无结果,再检查 KQL/DQL 过滤器和角色。
反向实验二:危险 API 必须由服务端拒绝
使用生产只读角色登录,在 Dev Tools 输入一个不存在的探针索引,避免影响真实对象:
DELETE app-permission-probe-do-not-create正确结果是 403,错误体包含安全异常和缺失权限。即使索引不存在,授权检查也应先拒绝请求。若返回 404,说明该用户已经越过权限检查并拥有删除能力;若返回 200,立即按安全事件处理。隐藏 Dev Tools 菜单、设置只读 UI 角色或要求员工口头承诺,都不能代替后端的拒绝证据。
Kibana 角色需要同时配置 Elasticsearch 索引权限和 Kibana feature privilege。一个排障角色可以只授予 app-prod-logs-* 的 read、view_index_metadata,Kibana 侧只开放指定 space 的 Discover/Dashboard read,不授予 cluster 管理权限。OpenSearch role 同样拆分 cluster permissions、index permissions 与 tenant permissions;tenant 写权限只允许保存对象,不应顺带获得索引写入、模板或删除权限。
空结果和页面消失要分型
401 通常指向会话过期、代理未转发认证头、OIDC/SAML 回调或服务身份失败。先看浏览器网络响应和控制台服务日志,再检查身份提供方;不要通过共享管理员账号绕过。
403 说明身份通常已识别,但当前动作缺权限。Kibana 要同时检查 feature/space 与 Elasticsearch index/cluster privilege;OpenSearch 要同时检查 role mapping、index pattern 和 tenant permission。错误体中的 action 名称是第一证据,例如读取、字段能力、保存对象和删除索引对应不同授权。
“菜单不存在”可能是 feature privilege 或服务配置关闭了功能。Kibana 可用 console.ui.enabled: false 关闭 Console,改动会触发资源重新生成;OpenSearch Dashboards 也能通过插件和权限控制入口。菜单存在却 403,说明 UI 能进入但后端拒绝,这反而是权限边界正常工作的证据。
“页面有数据但少一部分”要检查文档级安全、字段级安全、tenant/space、data view 通配符和时间窗口。安全过滤会改变聚合基数,排障者不能把受限视图的计数当作全局事实。确需全局统计时,由受控服务账户生成脱敏聚合,而不是扩大个人账号权限。
Space 和 tenant 管的是对象,不自动管业务数据
Kibana Space 隔离 dashboard、visualization、data view 等 saved objects,并让角色按 space 授予功能权限。OpenSearch tenant 保存 index pattern、visualization、dashboard 等对象,默认可有 global、private 和 custom tenant。它们解决“谁能看到或修改哪套控制台资产”,不自动限制索引本身。
一个用户即使看不到某个 Dashboard,只要仍有索引权限,就可能从 API 读取数据;反过来,能打开共享 Dashboard,也可能因索引权限不足看到 403 或空图。设计权限时先画两条轴:对象轴是 space/tenant + feature permission,数据轴是 index/cluster permission。两条轴都满足,功能才真正可用。
团队按业务域和环境创建空间,例如 checkout-prod-readonly,而不是为每个人复制一套。私有 tenant 适合短期探索,不适合作为关键 dashboard 的唯一存储位置。共享对象变更进入代码审查或变更记录;导出 saved objects 时按敏感资料处理,因为对象可能包含索引名、查询、字段、连接引用和内部 URL。
截图、CSV 和 Console 历史都是数据副本
日志经常含 token、Cookie、手机号、邮箱、地址、请求体和内部 ID。Discover 中隐藏列只是视觉设置,原始文档仍可能通过展开行、导出或 API 返回。生产 data view 应默认只展示白名单字段,高敏字段在采集或索引侧脱敏,角色再用字段级安全收紧。
截图前关闭高敏列并检查页面顶栏、域名、用户名、过滤器和 trace ID;CSV 导出要限制字段、时间窗和行数,写入受控目录并设置销毁时间。Console 历史、浏览器缓存、报表队列和 saved search 也应纳入相同数据分类。把截图发到公共群后再删除本地文件,不构成完整清理。
Dev Tools Console 可以把请求导入或导出为文本,这份文件保存的是可再次执行的请求,不是查询结果备份。导出前要删除字面量认证头、真实索引名、个人数据和破坏性请求;导入后先审阅目标集群与每条方法,再逐条执行,不能把一整份历史直接跑向生产。Console 入口权限只决定用户能否打开代理接口,最终的 Elasticsearch/OpenSearch cluster 与 index 权限仍会逐条判定请求。
Dashboard、data view、visualization 等 saved objects 使用管理页面或公开 API 导出为 NDJSON。Kibana 导出可连同引用对象一起带出,导入受 savedObjects.maxImportExportSize 和 savedObjects.maxImportPayloadBytes 限制,并且只支持导入同版本、同一 major 的更新 minor 或下一 major,不能向旧版本回灌。OpenSearch Dashboards 导入时还要显式核对目标 data source、tenant/workspace、插件和冲突处理。NDJSON 可能暴露查询、字段、内部 URL 和引用关系,也不能替代包含系统索引与 feature state 的集群快照。
请求体中可能直接包含个人数据,控制台服务日志和代理访问日志又可能记录 URL、用户和响应状态。开启审计时要在可追溯性与二次采集之间平衡:记录用户、动作、目标索引、状态和 opaque request ID,避免把完整结果集写入应用日志。
项目接入要保存可审查的查询契约
仓库可以保存不含真实域名和秘密的运行手册:
docs/tools/search-console.md
docs/tools/search-query-contract.md
docs/tools/search-export-policy.md查询契约应包含引擎、space/tenant、索引别名、允许字段、时间字段、最大时间窗、size、用途和 owner。示例:
engine: elasticsearch
workspace: checkout-prod-readonly
index_alias: checkout-prod-logs
time_field: "@timestamp"
max_window: "incident-approved"
allowed_fields:
- "@timestamp"
- service
- status
- trace_id
forbidden_fields:
- authorization
- cookie
- request_body
owner: checkout-platform应用程序不应依赖 Kibana 或 Dashboards 作为查询 API。服务代码直接使用 Elasticsearch/OpenSearch 客户端,以独立服务账号、超时、重试、熔断和查询模板访问集群。控制台只承担人工探索、资产展示和受控管理;把浏览器 session Cookie 塞进脚本既不稳定,也绕过服务身份治理。
反向代理要传递正确的 Host、协议和认证头,设置请求体大小、空闲超时和并发保护。长查询被代理超时切断后,仍需检查后端 task 是否继续执行。为排障请求附加 X-Opaque-Id 或平台支持的执行上下文,让代理、控制台和集群日志能关联同一次动作。
清理实验环境和回滚配置
先删除实验索引,再停止控制台,最后删除后端和专用网络:
curl -sS -X DELETE 'http://127.0.0.1:9200/app-lab-logs'
curl -sS -X DELETE 'http://127.0.0.1:19200/app-lab-logs'
docker rm -f kibana-console-lab es-console-lab
docker network rm elastic-console-lab
docker rm -f dashboards-console-lab opensearch-console-lab
docker network rm opensearch-console-lab
unset STACK_VERSION OPENSEARCH_VERSION只对明确命名的实验资源执行这些命令。若实验使用了 volume,先确认绝对路径或 volume 标签,再单独删除;不要用全局 prune 清理共享开发机。生产索引绝不通过手工 DELETE 作为“回滚”,而要遵守 snapshot、alias、生命周期和变更审批流程。
配置回滚要恢复上一版 kibana.yml 或 opensearch_dashboards.yml、插件集合、加密 key 引用和镜像版本。Kibana 不支持滚动升级,多个实例升级时必须遵守官方停机与迁移流程,并确保所有实例版本、配置和插件一致;saved object 迁移前还要检查集群健康与 .kibana、.kibana_task_manager 的可用空间。OpenSearch Dashboards 回滚也必须与后端和插件版本配套,不能只回退前端镜像。
凭证一旦出现在命令行、容器 inspect、日志或工单,删除容器不能撤回泄漏,必须轮换 service token、密码或 OIDC secret。临时角色和 role mapping 也要撤销,并验证旧会话失效。
控制台容量不是只有一个 5601 端口
浏览器的一次搜索会触发字段能力查询、saved object 读取、主查询、自动补全甚至后台会话。用户把时间窗从一小时放大到一月,成本增长可能落在搜索线程池、分片 fan-out、聚合内存、网络和浏览器渲染上。size: 0 的聚合不返回文档,也不等于没有扫描成本。
容量观测要同时看控制台进程的 Node.js 堆、事件循环利用率、请求延迟和错误率,后端的 search thread pool、rejected、task、慢日志、断路器、分片数与磁盘水位,以及报表、告警和 saved object 迁移的后台任务。阈值来自压测基线和 SLO;示例中的时间窗与 size 只能作为实验值。
控制台水平扩展需要共享 encryption key、会话和一致配置,反向代理还要处理长连接与上传。增加 Kibana/Dashboards 副本只能缓解 Web 层瓶颈,不能减少后端搜索成本。查询风暴应从默认时间窗、data view、索引别名、并发限制、异步搜索和角色配额治理。
成本治理还包括许可证/订阅、托管服务规格、报表与告警功能、插件维护、身份集成和升级窗口。Elastic 的细粒度 sub-feature privilege 可能受订阅层级影响;选型时要把必须的权限粒度列成验收项,不能部署后才发现“只允许生成链接但不能编辑”需要不同许可。
长期治理从证据和所有权开始
每个生产控制台要有 owner、版本基线、后端端点、身份提供方、space/tenant 命名、角色模板、数据分类、导出政策和升级演练记录。日常用户默认只读;Dev Tools 可以关闭,或仅向经审批角色开放。删除索引、改 template/mapping、bulk 写入、生命周期与快照操作使用独立短期角色,并要求双人复核。
定期做两类回归:正向角色能在预算内看到允许索引和字段;反向角色对禁止索引、危险 API 和跨空间对象稳定收到 403。只检查菜单和截图不足以证明权限。审计还要抽样确认没有共享账号、长期管理员 token、未轮换加密 key、失主 dashboard 和无限保留导出。
当团队需要的是人工日志探索、共享仪表盘和搜索资产管理,跟随后端选择 Kibana 或 OpenSearch Dashboards。只需要发一条 API 验证时,curl 或语言客户端更容易自动化和审查;跨多个数据源做 BI 时,应选择专门分析平台;高敏生产排障则可以提供预定义 dashboard 和受控查询服务,减少开放任意 Console 的必要性。
最终验收不是“所有人都能打开 5601”,而是能回答:浏览器身份如何映射到数据权限,space/tenant 里保存了什么,危险 API 为什么会被拒绝,一次搜索消耗了多少资源,导出的副本去了哪里,升级失败如何恢复。能持续给出这些证据,搜索控制台才是工程工具,而不是一扇权限过大的网页。
