Elasticsearch 快照备份到 NAS(NFS)实战:SLM 自动化 + 365 天保留策略
ES 的数据安全只有一道保险:snapshot 快照。但第一次配备份的人几乎都会卡在同一个地方------仓库挂上了、一备份就报
Permission Denied,根因是 NFS 权限走数字 UID,而各节点的 ES 启动用户 UID 不一致。本文给出一套完整落地方案:统一 UID/GID → NAS 导出 → 挂载 → 注册仓库 → SLM 策略每天凌晨 2 点自动备份、保留 365 天,附恢复演练命令。全程可在 Kibana Dev Tools 复现。
📚 前置阅读:备份的粒度从索引规划开始。《Elasticsearch 日志索引设计:按小时切分 + Rollover 兜底》讲了怎么把日志索引切整齐------索引边界清楚了,备份和过期删除才有干净的抓手。

一、方案总览
| 项目 | 内容 |
|---|---|
| 目标 | 日志索引(示例 logs-app-*)每日自动快照,备份到 NAS,保留 365 天 |
| 技术栈 | Elasticsearch Snapshot and Restore + SLM(Snapshot Lifecycle Management) + NFS v4 |
| 核心要求 | 所有节点的 ES 启动用户 UID/GID 必须严格一致 |
为什么 UID 是第一位的:NFS 文件系统做权限校验时认的是数字 UID/GID ,不是用户名。ES 集群所有节点都要往同一个 NFS 目录写快照,只要有一台机器的 elastic 用户 UID 和别人不同,那台机器写出来的文件其他节点读不了,快照直接报错。专用 NAS(群晖/威联通)同理,要在权限设置里映射到相同 UID。
二、第一步:统一 UID/GID(关键前置)
先在所有 ES 节点检查当前用户:
bash
cat /etc/passwd | grep '^elastic'
elastic:x:1004:1004::/home/elastic:/bin/bash
如果各节点输出不一致(比如有 1004:1004 也有 1005:1005),必须统一。假设规划目标为 1005:1005(选一个未被占用的 ID):
bash
# 1. 修改组 GID
groupmod -g 1005 elastic
# 2. 修改用户 UID
usermod -u 1005 elastic
改完必须修正文件所有权------UID 变了,原目录下所有文件的属主数字对不上,ES 起不来是常事:
bash
# 数据/日志/配置目录,按实际路径替换
chown -R elastic:elastic /data/elasticsearch
# 挂载点如果已存在也要修
chown -R elastic:elastic /mnt/es_snapshots
此时不用急着重启 ES ,等后面
path.repo改完统一滚动重启一次即可。
三、NAS 端与挂载配置
3.1 NAS 存储端
- 创建共享目录:
/volume1/es_backup - 配置 NFS 导出:允许 ES 集群全部节点 IP 访问
- 权限对齐 :NAS 是 Linux 就把目录属主设为
1005:1005;专用 NAS 在权限设置里把读写权限映射给 UID 1005
3.2 所有 ES 节点挂载
bash
mkdir -p /mnt/es_snapshots
/etc/fstab 追加(参数已做 NFS 备份场景优化):
text
# <NAS_IP>:<DIR> <挂载点> nfs <选项> 0 0
192.168.1.100:/volume1/es_backup /mnt/es_snapshots nfs defaults,hard,intr,noatime,nfsvers=4,rsize=1048576,wsize=1048576,timeo=600 0 0
参数说明:nfsvers=4 用 v4 协议;rsize/wsize 拉到 1MB 提高吞吐;hard 保证写入不静默丢(备份场景宁挂勿错);timeo=600 网络抖动容忍。
执行挂载并授权:
bash
mount -a
df -h | grep es_snapshots # 验证挂载
chown -R elastic:elastic /mnt/es_snapshots
chmod 750 /mnt/es_snapshots
四、ES 配置与注册仓库
4.1 所有节点修改 elasticsearch.yml
yaml
path.repo: ["/mnt/es_snapshots"]
然后滚动重启 ES 集群(按你的部署方式:systemd 或脚本),并确认进程属主正确:
bash
ps -ef | grep elasticsearch # 输出应显示 elastic 用户启动
4.2 注册快照仓库
bash
curl -X PUT --user <账号>:<密码> "http://<ES_IP>:9200/_snapshot/nas_repo" -H 'Content-Type: application/json' -d'
{
"type": "fs",
"settings": {
"location": "/mnt/es_snapshots",
"compress": true
}
}
'
必做连通性验证------它会逐个节点检查读写,任何一台 UID 不对都会在这里现形:
bash
curl -X POST --user <账号>:<密码> "http://<ES_IP>:9200/_snapshot/nas_repo/_verify?pretty"
返回包含各节点信息且无 error 即通过。
五、SLM 策略:每天凌晨 2 点自动备份
bash
curl -X PUT --user <账号>:<密码> "http://<ES_IP>:9200/_slm/policy/daily_backup_policy" -H 'Content-Type: application/json' -d'
{
"schedule": "0 0 2 * * ?",
"name": "<app-snap-{now/d}>",
"repository": "nas_repo",
"config": {
"indices": "logs-app-*",
"ignore_unavailable": true,
"include_global_state": false
},
"retention": {
"expire_after": "365d",
"min_count": 5,
"max_count": 400
}
}
'
字段逐个说:
| 字段 | 值 | 含义 |
|---|---|---|
schedule |
0 0 2 * * ? |
每天 02:00 触发(Quartz 表达式) |
name |
<app-snap-{now/d}> |
快照命名模板,{now/d} 按天取日期,尖括号必带 |
indices |
logs-app-* |
备份范围,按实际索引前缀替换 |
include_global_state |
false | 不备份集群全局状态------不加这个,恢复时容易和现有集群状态冲突,是快照失败的常见原因 |
retention.expire_after |
365d |
保留 365 天后自动清理 |
min_count / max_count |
5 / 400 | 至少留 5 份兜底,最多 400 份防 NAS 爆满 |
SLM 策略创建后不需要额外动作,到点自动执行;执行历史可以在 Kibana 的 Stack Management → Snapshot and Restore 里看。
六、测试与验证
6.1 手动触发一次快照
数据量小可以同步等结果:
bash
curl -X PUT --user <账号>:<密码> "http://<ES_IP>:9200/_snapshot/nas_repo/manual-test?wait_for_completion=true&pretty" -H 'Content-Type: application/json' -d'
{
"indices": "logs-app-*",
"ignore_unavailable": true,
"include_global_state": false
}
'
数据量大改用异步(wait_for_completion=false),立即返回、后台执行:
bash
# 查看执行中的快照
curl -X GET --user <账号>:<密码> "http://<ES_IP>:9200/_snapshot/nas_repo/_current?pretty"
6.2 检查 NAS 侧文件权限
bash
ls -ln /mnt/es_snapshots/indices/
输出属主是 1005 1005(数字而不是用户名)就说明 UID/GID 链路全部打通。
七、恢复演练:备份的意义在于恢复
没做过恢复演练的备份等于没有备份。 恢复建议一律用新索引名,避免覆盖现有数据:
bash
curl -X POST "http://<ES_IP>:9200/_snapshot/nas_repo/app-snap-2026.10.27/_restore?pretty" -H 'Content-Type: application/json' -d'
{
"indices": "logs-app-2026.01.26",
"rename_pattern": "logs-app-(.+)",
"rename_replacement": "restored-logs-app-$1",
"ignore_unavailable": true,
"include_global_state": false
}
'
rename_pattern + rename_replacement 用正则把恢复出来的索引重命名(原 logs-app-2026.01.26 → restored-logs-app-2026.01.26),验证无误后再决定是否替换原索引。
建议把恢复演练做成季度例行动作,并在文档里记录当时的快照名和恢复耗时。
八、故障排查三条
| 现象 | 根因 | 处理 |
|---|---|---|
日志报 Permission Denied |
节点间 UID/GID 不一致,或挂载点属主不对 | 重新核对 /etc/passwd、ls -ln 挂载点、NAS 侧 UID 映射 |
| 快照创建失败 | NAS 空间满 | 检查空间使用率,核对 retention 的 max_count |
| 挂载超时 / 仓库验证失败 | NFS 协议或防火墙问题 | 确认 nfsvers=4 生效、放通 2049 端口 |
总结
这套方案的三个要点:UID/GID 一致性是 NFS 备份的命门 (90% 的失败在这里);include_global_state: false 是快照策略的标配(否则恢复时容易踩全局状态冲突);备份必须配恢复演练(rename 恢复到新索引名验证)。SLM 接管调度和过期清理后,整套备份就只剩一件事:定期看一眼执行历史。
你们的 ES 备份是方案?快照到 NAS、HDFS 还是 S3?有没有真的用过备份救火?评论区聊聊。
方案在生产环境验证;文中索引名、IP、账号均为示例,请按实际环境替换。