Kibana 是咱们日常摆弄 Elasticsearch 最常见的那块“仪表盘”,不管是看报表、搭监控,还是调试日志,最后都得靠它来展示。可它有个容易让人栽跟头的地方:所有配置,比如你做好的 Dashboard、Visualization、Index Pattern,甚至保存的搜索条件,其实都存放在 Elasticsearch 里的一堆内部索引中。也就是说,Kibana 自己本身不“记住”这些东西,它只是绘图的“前台”,真正的“仓库”在 ES 那边。一旦有人误操作把这些索引删了,或者升级 Kibana 时搞坏了版本,那辛辛苦苦攒出来的看板可能说没就没,比丢了钱包还难受。今天咱们就来聊聊怎么给 Kibana 做一套稳妥的配置备份和恢复方案,把这种意外风险压到最低。
一、Kibana 配置到底藏在哪儿
先别急着备份,得先搞清楚东西放在哪。Kibana 的配置项,包括你创建的图表、搜索、索引模式,全都保存在 Elasticsearch 的 .kibana 开头的索引里。类比一下,这就像你虽然用电脑上的 Excel 画表格,但文件实际存在 U 盘里,U 盘要是坏了,Excel 再漂亮也没用。
打开终端,执行下面这条命令,就能看到这些索引长什么样。
# 查看所有 .kibana 开头的索引,顺便看看占了多少空间
curl -s -X GET "http://localhost:9200/_cat/indices/.kibana*?v&s=index" \
-H "Content-Type: application/json"
输出结果里会出现类似 .kibana_1、.kibana_security_1 这样的名字,这些索引就是 Kibana 的全部家当。知道这一点后,备份思路就清晰了:要么把这些索引里的数据“导出来”,要么直接给整个索引做“快照”。
二、最偷懒的备份方法:导出 Saved Objects
Kibana 自带一个“保存对象”功能,英文叫 Saved Objects。所有你手动做出来的仪表盘、可视化图表、索引模式,都算“保存对象”。这个功能允许你一键导出成一个文件,恢复的时候再导回去。虽然它不能覆盖全部配置,但对于大多数用户来说,这已经是最常用的“救命稻草”。
2.1 通过界面导出
在 Kibana 左侧菜单找到“Stack Management”,进入后点“Saved Objects”,能看到所有已保存对象。点“Export”按钮,就能下载一个 .ndjson 文件。这个操作当然很好用,但问题是没法自动化。如果你有几十套 Kibana 环境,手动一个个点,手指都得抽筋。
2.2 用 API 自动导出
好在 Kibana 提供了 API 接口,用一条命令就能把全部保存对象导出来。咱们用 curl 来搞定。先写上技术栈,免得后面乱套。
#!/bin/bash
# 技术栈:Bash Shell + curl
# 用途:导出Kibana所有Saved Objects
KIBANA_URL="http://localhost:5601"
KIBANA_USER="elastic"
KIBANA_PASS="changeme"
OUTPUT_FILE="kibana_saved_objects.ndjson"
# 发送导出请求
# 请求体里的 type 是数组,空数组表示导出所有类型
# includeReferencesDeep 设为true,表示把被引用的对象也一起带上,避免恢复后缺依赖
curl -s -X POST "${KIBANA_URL}/api/saved_objects/_export" \
-u "${KIBANA_USER}:${KIBANA_PASS}" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-d '{
"type": [],
"includeReferencesDeep": true
}' \
-o "${OUTPUT_FILE}"
# 检查导出文件是否为空
if [ -s "${OUTPUT_FILE}" ]; then
echo "导出成功,文件名为 ${OUTPUT_FILE}"
else
echo "导出失败,请检查Kibana地址或账号权限"
fi
这段脚本会把所有 Dashboard、Visualization 等打包到一个文件里。注意 kbn-xsrf 这个请求头是 Kibana 防止跨站请求的机制,不加它 API 会拒绝服务。
这个方法虽然省事,但有一个大坑:它不包含你修改过的“高级设置”,比如日期的显示格式、默认查询时间范围等。这些设置也存在 .kibana 索引里,只不过不属于 Saved Objects。所以想要更稳妥,还得靠下一招。
三、最踏实的备份:给 Elasticsearch 索引做快照
Elasticsearch 自带一个叫“快照”的功能,简单说就是把某个时刻的索引状态整体“拍张照片”存起来,以后想恢复就能一键还原。这比导 Saved Objects 要全面得多,因为快照是存储层级的备份,不管配置文件藏多深,统统给你照下来,就像把整个档案柜都搬走,而不是只挑几个文件夹。
3.1 先准备一个“仓库”
快照不能随便放,得先指定一个地方来存,这个地方叫仓库。仓库可以挂在本机磁盘、共享存储,甚至云对象存储上。下面演示用本地文件系统做仓库。
#!/bin/bash
# 技术栈:Bash Shell + curl
# 用途:创建用于存储Kibana快照的仓库
ES_URL="http://localhost:9200"
ES_USER="elastic"
ES_PASS="changeme"
# 创建仓库。location 是ES节点上的绝对路径,需要确保该路径存在且ES有写权限
curl -s -X PUT "${ES_URL}/_snapshot/kibana_backup_repo" \
-u "${ES_USER}:${ES_PASS}" \
-H "Content-Type: application/json" \
-d '{
"type": "fs",
"settings": {
"location": "/mnt/backups/es_snapshots",
"compress": true
}
}'
有一点要记牢:如果 Elasticsearch 是集群模式,这个路径必须能被所有数据节点访问到。否则有的节点写不进去,快照就会失败。
3.2 给 .kibana 索引拍快照
仓库建好之后,就可以给带有 .kibana 前缀的索引做快照了。做快照时最好带上时间戳,这样以后能看到每一天的版本。
# 技术栈:Bash Shell + curl
# 用途:对 .kibana 索引执行快照
ES_URL="http://localhost:9200"
ES_USER="elastic"
ES_PASS="changeme"
SNAPSHOT_NAME="kibana_$(date +%Y%m%d_%H%M%S)"
# 执行快照。indices 指定为 .kibana*,代表所有相关索引
# ignore_unavailable 设为true,避免某些索引不存在时报错
curl -s -X PUT "${ES_URL}/_snapshot/kibana_backup_repo/${SNAPSHOT_NAME}" \
-u "${ES_USER}:${ES_PASS}" \
-H "Content-Type: application/json" \
-d '{
"indices": ".kibana*",
"ignore_unavailable": true,
"include_global_state": false
}'
执行完之后,可以用下面这条命令查看快照状态,直到它变成 SUCCESS 才算是真的完成。注意,快照是异步的,发出请求不代表马上完成。
# 查看某个快照的执行状态
curl -s -X GET "http://localhost:9200/_snapshot/kibana_backup_repo/${SNAPSHOT_NAME}" \
-u "elastic:changeme" | jq '.snapshots[0].state'
快照的完整程度比 Saved Objects 高不少,因为连 Kibana 内部存储的“高级设置”也都一并备份了。但它的缺点是比较重,而且和 Elasticsearch 版本强相关。等到你需要恢复的时候,得保证版本差不多,否则可能起不来。
四、把备份自动化:自己写一个一键脚本
手动执行命令还是不够安稳,万一人忘了呢?最好写成一个脚本,让服务器定时跑。下面这个脚本把“导出 Saved Objects”和“做快照”合并到一起,一条命令全搞定。
#!/bin/bash
# 技术栈:Bash Shell + curl + jq
# 用途:一键备份Kibana配置(Saved Objects + 快照)
set -euo pipefail
# ===== 以下配置请根据实际环境修改 =====
ES_URL="http://localhost:9200"
KIBANA_URL="http://localhost:5601"
ES_USER="elastic"
ES_PASS="changeme"
BACKUP_DIR="/opt/kibana_backups"
SNAPSHOT_REPO="kibana_backup_repo"
# 每次运行用时间戳生成唯一快照名
SNAPSHOT_NAME="kibana_$(date +%Y%m%d_%H%M%S)"
# =====================================
mkdir -p "${BACKUP_DIR}"
LOG_FILE="${BACKUP_DIR}/backup.log"
# 简单的日志函数,顺便把内容输出到终端
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}"
}
# 第一步:导出Saved Objects
export_saved_objects() {
local output="${BACKUP_DIR}/saved_objects_${SNAPSHOT_NAME}.ndjson"
log "开始导出Saved Objects..."
curl -s -X POST "${KIBANA_URL}/api/saved_objects/_export" \
-u "${ES_USER}:${ES_PASS}" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-d '{"type": [], "includeReferencesDeep": true}' \
-o "${output}"
# 如果文件不是空的,就说明导出成功
if [ -s "${output}" ]; then
log "Saved Objects导出成功:${output}"
else
log "警告:导出文件为空,请检查Kibana服务或权限!"
fi
}
# 第二步:确保快照仓库存在
ensure_repo() {
log "检查快照仓库 ${SNAPSHOT_REPO} ..."
# 简单判断仓库是否已存在
local exists
exists=$(curl -s -u "${ES_USER}:${ES_PASS}" \
"${ES_URL}/_snapshot/${SNAPSHOT_REPO}" | grep -c '"type"')
if [ "${exists}" -eq 0 ]; then
log "仓库不存在,现在创建..."
curl -s -X PUT "${ES_URL}/_snapshot/${SNAPSHOT_REPO}" \
-u "${ES_USER}:${ES_PASS}" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"fs\",
\"settings\": {
\"location\": \"/mnt/backups/es_snapshots\",
\"compress\": true
}
}"
log "仓库创建成功。"
else
log "仓库已存在,直接复用。"
fi
}
# 第三步:对.kibana索引做快照
create_snapshot() {
log "正在创建快照 ${SNAPSHOT_NAME} ..."
curl -s -X PUT "${ES_URL}/_snapshot/${SNAPSHOT_REPO}/${SNAPSHOT_NAME}" \
-u "${ES_USER}:${ES_PASS}" \
-H "Content-Type: application/json" \
-d '{
"indices": ".kibana*",
"ignore_unavailable": true,
"include_global_state": false
}'
log "快照请求已发出,等待完成..."
# 轮询快照状态,直到变成SUCCESS
for i in $(seq 1 30); do
local state
state=$(curl -s -X GET "${ES_URL}/_snapshot/${SNAPSHOT_REPO}/${SNAPSHOT_NAME}" \
-u "${ES_USER}:${ES_PASS}" | jq -r '.snapshots[0].state' 2>/dev/null || echo "")
if [ "$state" = "SUCCESS" ]; then
log "快照完成,状态为 SUCCESS"
return 0
fi
log "快照状态为 ${state},10秒后再检查..."
sleep 10
done
log "快照等待超时,请检查ES日志。"
}
# 主流程
log "===== Kibana配置备份开始 ====="
export_saved_objects
ensure_repo
create_snapshot
log "===== 备份完成,文件位于 ${BACKUP_DIR} ====="
把这段脚本保存成 kibana_backup.sh,先加上执行权限,再手动试跑一次。
# 给脚本加上可执行权限
chmod +x /opt/kibana_backups/kibana_backup.sh
# 手动执行一次
/opt/kibana_backups/kibana_backup.sh
如果一切顺利,就能在 /opt/kibana_backups 里看到导出的 .ndjson 文件,以及在 ES 里创建好的快照。之后想定时跑,只要加一条 crontab 规则,比如每天凌晨 2 点执行:
# 每天凌晨2点执行备份
0 2 * * * /opt/kibana_backups/kibana_backup.sh >> /var/log/kibana_backup_cron.log 2>&1
有了 cnotab,就算你睡得正香,服务器也会默默把备份做好。偶尔想看有没有成功,翻一眼日志就行。
五、恢复流程:把备份拿出来“救火”
备份做得再好,不会恢复等于白做。下面分两种情况说:一种是只导出了 Saved Objects,另一种是做了快照。
5.1 恢复 Saved Objects
如果你手头只有那个 .ndjson 文件,恢复方式也很简单。在 Kibana 界面的 Saved Objects 里点“Import”,选择文件,然后勾选“覆盖已有对象”就能导回去。当然,用 API 也可以,适合那种界面打不开的场景。
# 技术栈:Bash Shell + curl
# 用途:从ndjson文件导入Saved Objects
KIBANA_URL="http://localhost:5601"
KIBANA_USER="elastic"
KIBANA_PASS="changeme"
BACKUP_FILE="kibana_saved_objects.ndjson"
# 导入文件。overwrite=true表示如果已存在同名对象,直接覆盖
curl -s -X POST "${KIBANA_URL}/api/saved_objects/_import" \
-u "${KIBANA_USER}:${KIBANA_PASS}" \
-H "kbn-xsrf: true" \
-H "Content-Type: multipart/form-data" \
-F "file=@${BACKUP_FILE}" \
-F "overwrite=true"
导入之后,刷新一下 Kibana 页面,通常就能看到恢复出来的仪表盘了。不过要注意,如果你在导出之后还创建了新的对象,并且同名,覆盖之后新内容会被旧内容顶掉。所以导入前最好确认一下当前环境是不是真的需要“回到过去”。
5.2 从快照恢复整个 Kibana 索引
要恢复“高级设置”这些普通导出没有的东西,就得用快照。但快照恢复是有风险的,因为它会直接把 .kibana 索引整个替换掉。如果当前索引里还有想要的新数据,千万先备份,或者用“重命名恢复”的方式把旧索引搬到旁边,再手动处理。
下面演示一个比较稳妥的做法:把快照里的 .kibana 索引恢复成一个带新名字的索引,不要直接覆盖。
# 技术栈:Bash Shell + curl
# 用途:从快照恢复.kibana索引到另一个索引名
ES_URL="http://localhost:9200"
ES_USER="elastic"
ES_PASS="changeme"
SNAPSHOT_REPO="kibana_backup_repo"
SNAPSHOT_NAME="kibana_20251015_120000"
# 执行恢复。rename_pattern匹配原来的索引名,rename_replacement改成 new_ 前缀
curl -s -X POST "${ES_URL}/_snapshot/${SNAPSHOT_REPO}/${SNAPSHOT_NAME}/_restore" \
-u "${ES_USER}:${ES_PASS}" \
-H "Content-Type: application/json" \
-d '{
"indices": ".kibana*",
"ignore_unavailable": true,
"include_global_state": false,
"rename_pattern": "(.+)",
"rename_replacement": "restored_$1"
}'
执行完以后,你会得到一堆 restored_.kibana_1 之类的索引。这时 Kibana 并不会直接认这些新名字,你需要手动处理索引别名,或者干脆把原索引删掉,再利用 Elasticsearch 的“重新索引”功能把数据搬回去。这个过程比较繁琐,因此更常见的做法是:先关闭 Kibana,删除当前 .kibana* 索引,然后直接恢复成原索引名。这样最简单,但要注意,一旦出错,当前配置就没了,你必须确保快照是好的。
# 技术栈:Bash Shell + curl
# 用途:删掉当前.kibana索引,再从快照恢复(高风险操作,慎用!)
# 先关闭Kibana应用(在Kibana机器上执行)
# systemctl stop kibana
# 删除当前.kibana索引
curl -s -X DELETE "http://localhost:9200/.kibana*" \
-u "elastic:changeme"
# 从快照恢复到原索引名
curl -s -X POST "http://localhost:9200/_snapshot/kibana_backup_repo/kibana_20251015_120000/_restore" \
-u "elastic:changeme" \
-H "Content-Type: application/json" \
-d '{
"indices": ".kibana*",
"ignore_unavailable": true
}'
# 启动Kibana
# systemctl start kibana
做完这些,Kibana 就会回到快照时的样子。这里强烈建议先在测试环境演练一遍,别等真出事时临时抱佛脚。
六、应用场景、技术优缺点和注意事项
6.1 适合哪些场景?
- 升级前必做:Kibana 和 Elasticsearch 升级都有一定风险,尤其跨大版本时,内部数据结构可能会变。升级前做一次完整快照,万一失败还能倒回去。
- 多团队协作:一堆人都在往 Kibana 里加图表,难免有人手抖删错。有了备份,可以迅速恢复到出事前的时间点。
- 环境迁移:从测试环境搬到生产环境,或者换集群,导出一份 Saved Objects 再导入,省时省力。
6.2 两种备份方式的优缺点
Saved Objects 导出,优点是文件小、人眼可读、跨版本移植性好,用文本编辑器就能看到里面有什么。缺点也明显,它不管“高级设置”和部分内部元数据,而且如果对象之间有复杂的引用关系,导出时漏掉某个引用,恢复完可能有些图表显示不出来。
快照备份,优点是极其完整,连索引的底层结构都原样保存,恢复后和当时一模一样。缺点是重,而且强依赖 Elasticsearch 版本,不能拿到别的集群里乱用。另外,恢复快照的权限要求高,如果连集群管理员都搞不定,那就只能靠 Saved Objects 续命了。
6.3 必须留意的坑
- 版本兼容性:Kibana 升级之后,不要用旧版本的导出文件往新版本里硬导,很可能因为字段格式变化而导入失败。最好是同版本或小版本之间导入。
- 索引名称别写错:快照时用的是
.kibana*通配,但 Elasticsearch 里也可能有.kibana_task_manager等其他索引,恢复时也要一并考虑,否则任务记录丢失。 - 权限要锁死:备份脚本里存了明文密码,这本身就是安全隐患。建议把脚本文件权限改成 700,或者用环境变量注入密码。
- 备份不等于万事大吉:定期演练恢复流程,确认备份文件能被成功导入。否则等到真要用时发现文件损坏,那才叫绝望。
- 快照仓库的位置:别把快照仓库放在 Elasticsearch 的数据目录里,否则磁盘满了,整个集群都可能挂。放独立磁盘,并且定期清理旧快照。
6.4 锦上添花的小工具
除了上面这些,你还可以用 elasticsearch-crone(自带工具)配合对象存储做跨机房备份,或者用 Curator 定期清理过期快照。不过这些属于进阶玩法,先把基础备份做好,已经能避开百分之九十的坑了。
七、总结
Kibana 的配置备份,本质上就是备份它身后的 Elasticsearch 索引。有人喜欢用 Saved Objects 导出,方便灵活;有人喜欢用快照,完整可靠。对我来说,最科学的方法是两条腿走路:日常用脚本把 Saved Objects 导出一份,隔一段时间再做一次快照。这样既有了快速恢复的轻量方案,也有了一旦崩塌就能整体还原的救心丸。关键不在于用哪个,而在于你真正把备份做了,并且验证过恢复是通的。人总有手滑的时候,但合理的备份策略,能让手滑多一次“后悔药”的机会。希望今天这套方法能帮你在意外来临时,不那么慌。
评论
围绕“Kibana配置备份与恢复:规避误操作导致的数据丢失风险的方法”参与讨论