本文作者为中国移动云能力中心大数据团队软件开发工程师冯江涛,本文提供了 ElasticSearch 集群的备份、恢复和进度监控等操作实践,并给出多种备份方案,供大家参考。
背景
ELK、EFK 作为较主流的日志解决方案,在后端产品中应用较为广泛,Elasticsearch 和 Logstash 都使用到了 log4j 组件,随着 log4j 核弹级漏洞爆出,不得不考虑 Elasticsearch 升级的问题,出于数据安全的考虑,生产环境的 ES 集群升级前的备份是很重要的。
简介
无论使用哪种存储数据的软件,定期备份数据都是很重要的。Elasticsearch 副本提供了高可靠性;它们让你可以容忍少量的节点丢失而不会中断服务。但是,副本并不提供对灾难性故障的保护。对这种情况,你需要的是对集群真正的备份——在某些东西确实出问题的时候有一个完整的拷贝。需要注意的是 Elasticsearch 官方并不支持直接拷贝集群 data 目录、备份服务器的方式来备份、升级群集,这样做会导致数据损坏、无法恢复数据。
Elasticsearch 提供的 Snapshot api 就是从正在运行的集群中获取备份用的,可以根据情况选择整个集群,也可以指定 index 和 data stream。Elasticsearch 是以增量的方式获取 Snapshot,节约了时间和空间,小开销意味着可以适当增加备份的频率。每一个 Snapshot 在逻辑上相互独立,所以删除某一个 Snapshot 不会影响到其他的 Snapshot。
从 Snapshot 恢复集群数据可以指定 index 和 data stream,当然也可以通 SLM(https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-lifecycle-management.html)来管理 Snapshot 的生命周期。
备份恢复时的版本兼容性:当前版本支持当前版本及升高一个的版本进行恢复不支持跨版本直接恢复。当然如果你想跨版本恢复,可以尝试版本递增滚动升级来达到目的。反之则不可行,不能递减版本来恢复备份的数据。
配置 Snapshot 仓库
在使用 Snapshot 备份前,必须要注册快照仓库。可以存储在本地或者远程仓库中,远程仓库支持 Amazon S3,HDFS,Microsoft Azure,Google GCS 等,参考仓库插件(https://www.elastic.co/guide/en/elasticsearch/plugins/current/repository.html)的类型。
1.创建仓库
在创建仓库之前需要在所有 Master 和 Data 节点中增加仓库配置项(elasticsearch.yml),下面以共享存储的方式举例。单节点的情况下可以用本地物理存储路径,如果是多节点分布式,需要保证共享存储在所有的 data 节点都进行了正确的 mount 挂载。
path:
repo:
- /apps/data/esbackup
- /apps/data/stream-backup
# Windows系统下注意路径分隔符的不同
- D:\\apps\\data\\esbackup
复制代码
等到滚动重启完所有的节点,就可以创建仓库了。
PUT /_snapshot/my_fs_backup
{
# fs:共享文件系统;s3:亚马逊的S3仓库;azure:微软 Azure 仓库;hdfs:HDFS 仓库;gcs:谷歌的 GCS 仓库。
# "type": "url",url 支持类型:file、ftp、http、https、jar。url 为只读类型,意味着只能从此类型中恢复和检索数据。
# "type": "source",source 类型的前提是 filed 里面设置 _source 为 enabled ,这样可以节约大概50%的备份空间。
"type": "fs",
"settings": {
# location 为配置文件中 path.repo 所配置的子路径,所以也可以写相对路径 "location": "My_fs_backup_location"
"location": "/apps/data/esbackup/My_fs_backup_location",
# 是否启用压缩,默认为 true
"compress": "true"
}
}
# 成功后会返回
{
"acknowledged": true
}
复制代码
创建完成后可以 GET 查询仓库。
GET /_snapshot
# 返回结果
{
"my_fs_backup": {
"type": "fs",
"settings": {
"compress": "true",
"location": "My_fs_backup_location"
}
}
}
复制代码
通过 verify 验证节点仓库是否在所有节点已生效。
POST /_snapshot/my_fs_backup/_verify
# 返回结果
{
"nodes": {
# 节点uuid
"8Z7MmAUeToq6WGCxhVFk8A": {
# 节点名称
"name": "jt-hpzbook"
},
"yRahNXjuR2-w6w1ObVXQjg": {
"name": "jt-hpzbook2"
},
}
}
复制代码
其他接口:
# 查看所有仓库
GET /_snapshot/_all
# 删除仓库
DELETE /_snapshot/my_fs_backup
复制代码
2.可配置参数
更多参数可以参考:Repository API(https://www.elastic.co/guide/en/elasticsearch/reference/current/put-snapshot-repo-api.html)
•location: 仓库地址
•compress: 是否启用压缩,默认为 true
•chunk_size: 是否将文件切块,并指定文件块大小,默认:null (不切分)
•max_restore_bytes_per_sec: Snapshot 从仓库恢复时的限速,默认:无限制•max_snapshot_bytes_per_sec: 节点创建 Snapshot 进入仓库时的限速,默认:40mb/s
•readonly: Snapshot 是否只读,默认:false
3.插件
插件的扩展使得 Snapshot 可以选择更多的方式,无论是 HDFS 集群还是常见的云厂商的云端仓库。
•官方插件:
•S3 Repository(https://www.elastic.co/guide/en/elasticsearch/plugins/current/repository-s3.html): 亚马逊的 S3 仓库
•Azure Repository(https://www.elastic.co/guide/en/elasticsearch/plugins/current/repository-azure.html): 微软 Azure 仓库
•HDFS Repository(https://www.elastic.co/guide/en/elasticsearch/plugins/current/repository-hdfs.html): HDFS 仓库
•Google Cloud Storage Repository(https://www.elastic.co/guide/en/elasticsearch/plugins/current/repository-gcs.html): 谷歌的 GCS 仓库•
社区插件:
•Openstack Swift(https://github.com/BigDataBoutique/elasticsearch-repository-swift)
Snapshot 快照备份
1.创建 Snapshot
一个仓库可以包含多个 Snapshot,一个 Snapshot 在集群中的名字是唯一的。Snapshot 快照备份的内容仅包含截止快照开始时间之前的数据,快照之后的数据需要通过不断的增量 Snapshot 来捕获。通过 PUT 请求创建一个 Snapshot,默认备份集群所有可读索引、流,如果需要部分备份则可以通过传参来指定。
# wait_for_completion 参数表示是否要同步等 Snapshot 创建完成再返回,PUT 请求如果传参为空则默认备份所有可读索引、流
PUT /_snapshot/my_fs_backup/snapshot_1?wait_for_completion=true
{
"indices": "hundredsman,index_1,index_2",
# ignore_unavailable:忽略不可用的索引和流
"ignore_unavailable": true,
# include_global_state:是否保存集群全局状态
"include_global_state": false,
# metadata:元数据,一些注释性的数据。
"metadata": {
"taken_by": "james",
"taken_because": "Hundreds man fighting for book backup."
}
}
# 返回结果
{
"snapshot": {
"snapshot": "snapshot_1",
"uuid": "HQHFSpPoQ1aY4ykm2o-a0Q",
"version_id": 7100099,
"version": "7.10.0",
# Snapshot 备份的索引
"indices": [
"index_1",
"index_2",
"hundredsman"
],
"data_streams": [],
# 是否保存集群全局状态
"include_global_state": true,
# 当前快照状态
"state": "SUCCESS",
"start_time": "2021-09-03T12:46:56.237Z",
"start_time_in_millis": 1630673216237,
"end_time": "2021-09-03T12:46:56.237Z",
"end_time_in_millis": 1630673216237,
"duration_in_millis": 0,
# 备份失败的索引和流
"failures": [],
# 分片状态
"shards": {
"total": 3,
"failed": 0,
"successful": 3
}
}
}
复制代码
2.删除 Snapshot
删除 Snapshot 需要发送 DELETE 请求:
DELETE /_snapshot/my_fs_backup/snapshot_1
# 删除多个可以用逗号分隔或者通配符
DELETE /_snapshot/my_fs_backup/snapshot_2,snapshot_3
DELETE /_snapshot/my_fs_backup/snap*
复制代码
如果 Snapshot 正在创建过程中,Elasticsearch 也会终止任务并删除所有 Snapshot 相关的数据。但要注意不能手动删除仓库里的备份数据,这样会有数据损坏的风险。
Restore 恢复
发送 POST 请求从 Snapshot 恢复数据:
# 不带参数的请求默认恢复所有 Snapshot 中的索引、流
POST /_snapshot/my_fs_backup/snapshot_1/_restore
# 如果需要恢复特定的索引、流,可以在 POST 参数中指定
POST /_snapshot/my_fs_backup/snapshot_1/_restore
{
"indices": "index*",
"ignore_unavailable": true,
# include_global_state默认为 true,是否保存集群全局状态
"include_global_state": false,
# 重命名索引匹配规则,如:index_1
"rename_pattern": "index_(.+)",
# 重命名索引为新的规则,如:re_index_1
"rename_replacement": "re_index_$1",
# 是否包含别名
"include_aliases": false
}
# 正常返回结果
{
"accepted": true
}
# 如果索引已经存在,会提示已经有同名索引存在,需要重命名。
{
"error": {
"root_cause": [
{ # Exception 类型
"type": "snapshot_restore_exception",
# 发生错误原因详情
"reason": "[my_fs_backup:snapshot_1/90A9o4hORUCv732HTQBfRQ] cannot restore index [index_1] because an open index with same name already exists in the cluster. Either close or delete the existing index or restore the index under a different name by providing a rename pattern and replacement name"
}
]
},
"status": 500
}
复制代码
需要注意的是如果没有匹配到 Stream 流的模板,Stream 流是不能滚动创建的。所以如果有快照中包含有 Stream 流数据,要记得提前创建模板。
监控进度
1.监控 Snapshot 状态
通过 GET 请求监控当前 Snapshot 状态,需要注意的是,如果你的 Snapshot 和索引 shard 分片很多、仓库存储的延迟很大,那么 _current 请求可能会耗时很久。
# 查看当前 Snapshot 状态
GET /_snapshot/my_fs_backup/_current
# 指定 Snapshot 查看
GET /_snapshot/my_fs_backup/snapshot_1
GET /_snapshot/my_fs_backup/snapshot_*
# 查看所有仓库(如果建了多个仓库的话)
GET /_snapshot/_all
GET /_snapshot/my_fs_backup,my_hdfs_backup
GET /_snapshot/my*
# 指定查看某一个 Snapshot 的进度详情
GET /_snapshot/my_fs_backup/snapshot_1/_status
# 返回结果较大,以下仅展示部分结果。更多结果说明参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/get-snapshot-status-api.html
{
"snapshots": [
{ # 快照名称
"snapshot": "snapshot_1",
# 仓库名称
"repository": "my_fs_backup",
"uuid": "HQHFSpPoQ1aY4ykm2o-a0Q",
# 快照状态
"state": "SUCCESS",
# 是否包含集群全局状态
"include_global_state": true,
# 索引分片的状态
"shards_stats": {
# 初始化中的个数
"initializing": 0,
# 已启动的个数
"started": 0,
# 最后状态的个数
"finalizing": 0,
# 已完成的个数
"done": 3,
# 失败个数
"failed": 0,
# 总数
"total": 3
},
# 快照备份状态
"stats": {
# 待备份的文件统计
"incremental": {
"file_count": 2,
"size_in_bytes": 500
},
# 已完成的备份文件统计
"processed": {
"file_count": 1,
"size_in_bytes": 124
}
# 文件统计总览
"total": {
"file_count": 3,
"size_in_bytes": 624
},
# 开始时间
"start_time_in_millis": 1630673216237,
"time_in_millis": 0
},
# 包含的索引
"indices": {
# 索引名称
"index_1": {
# 分片备份状态
"shards_stats": {
"initializing": 0,
"started": 0,
"finalizing": 0,
"done": 1,
"failed": 0,
"total": 1
},
"stats": {
"incremental": {
"file_count": 1,
"size_in_bytes": 208
},
"total": {
"file_count": 1,
"size_in_bytes": 208
},
"start_time_in_millis": 1630673216237,
"time_in_millis": 0
},
"shards": {
"0": {
# initializing:初始化检查集群状态是否可以创建快照
# started:数据正在被传输到仓库
# finalizing:数据传输完成,shard 分片正在发送 Snapshot 元数据
# done:Snapshot 创建完成
# failed:遇到错误失败的 shard 分片
"stage": "DONE",
"stats": {
"incremental": {
"file_count": 1,
"size_in_bytes": 208
},
"total": {
"file_count": 1,
"size_in_bytes": 208
},
"start_time_in_millis": 1630673216237,
"time_in_millis": 0
}
}
}
# 省略部分结果...
}
}
}
]
}
复制代码
2.监控 Restore 恢复状态
当 Restore 恢复启动后,因为 Restore 在恢复索引的主分片,所以集群状态会变成 yellow,主分片恢复完成后 Elasticsearch 开始根据副本设置的策略恢复副本数,所有操作完成后集群才会恢复到 green 状态。也可以先把索引的副本数修改为 0,待主分片完成后再修改到目标副本数。Restore 恢复状态可以通过监控集群或者指定索引的 Recovery 状态。
# 查看集群恢复状态,更多请参考集群恢复监控接口:https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-recovery.html
GET /_cat/recovery/
# 查看索引的恢复状态,更多请参考索引恢复监控接口:https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-recovery.html
GET /index_1/_recovery
# 返回结果
{
"restore_lakehouse": {
"shards": [
{
"id": 1,
"type": "SNAPSHOT",
# 恢复的阶段,INIT:初始化;START:开始;INDEX:读取索引元数据和拷贝数据;TRANSLOG:日志回放;FINALIZE:善后整理;DONE:完成;
"stage": "INDEX",
# 是否主分片
"primary": true,
"start_time_in_millis": 1630673216237,
"total_time_in_millis": 1513,
# 恢复来源
"source": {
# 仓库
"repository": "my_fs_backup",
"snapshot": "snapshot_3",
"version": "7.10.0",
"index": "index_1",
"restoreUUID": "fLtPIdOORr-3E7AtEQ3nFw"
},
# 目标集群
"target": {
"id": "8Z7MmAUeToq6WGCxhVFk8A",
# 集群 host
"host": "127.0.0.1",
"transport_address": "127.0.0.1:9300",
"ip": "127.0.0.1",
# 集群名称
"name": "jt-hpzbook"
},
# 索引
"index": {
"size": {
# 占用空间大小
"total_in_bytes": 25729623,
# 可回收空间
"reused_in_bytes": 0,
# 已恢复大小
"recovered_in_bytes": 23397681,
# 已恢复进度百分比
"percent": "90.9%"
},
# 文件统计
"files": {
"total": 50,
"reused": 0,
"recovered": 43,
"percent": "86.0%"
},
"total_time_in_millis": 1488,
"source_throttle_time_in_millis": 0,
"target_throttle_time_in_millis": 0
},
# 回放日志
"translog": {
"recovered": 0,
"total": 0,
"percent": "100.0%",
"total_on_start": 0,
"total_time_in_millis": 0
},
# 验证索引
"verify_index": {
"check_index_time_in_millis": 0,
"total_time_in_millis": 0
}
}
# 其它shard详情省略...
]
}
}
复制代码
其他备份手段
elasticdump
类似 MySQL dump 的功能,对 Elasticsearch 集群的数据进行导入导出。适合非增量索引数据的备份。更多安装使用 elasticdump 请移步:elasticsearch-dump Github(https://github.com/elasticsearch-dump/elasticsearch-dump)。
data 文件备份恢复
前面有提到官方非常不建议此种方式备份数据,data 文件备份的方式较为粗暴且风险极大,适合一些极端场景下使用。根据集群中的 data 节点配置项中"path.data: /path/to/data"的配置路径,将所有的 data 节点下数据文件打包压缩备份。此种备份方式在恢复时较为不便,需要将数据覆盖原集群的 data 目录,或者重新构建一个与原集群相同 data 节点数的新集群,然后将备份的数据放置在"path.data: /path/to/data"的配置路径下,重启新集群后可恢复数据,此种方式恢复集群有点类似将原集群 data 节点重启了一遍。经过验证在相同节点数的情况下集群可正常运行并加载到原来 data 数据拷贝时的索引,但是对于增量数据是无法感知的,所以并不适合生产环境数据的备份。
利用数据同步工具
•Kettle: 免费开源的基于 Java 的企业级 ETL 工具,功能强大简单易用,仅支持写入 Elasticsearch。更多参考:Kettle Gighub(https://github.com/pentaho/pentaho-kettle)
•DataX:是阿里云 DataWorks 数据集成。更多参考:Datax Github(https://github.com/alibaba/DataX)的开源版本,多种异构数据源之间高效的数据同步功能,目前仅支持写入 Elasticsearch。
•Flinkx:是一个基于 Flink 的批流统一的数据同步工具,支持 Elasticsearch 的读取和写入。更多参考:Flinkx Github(https://github.com/DTStack/flinkx)
•SeaTunnel: 支持海量数据实时同步的超高性能分布式数据集成平台。更多参考:SeaTunnel(https://github.com/apache/incubator-seatunnel)
总结
Elasticsearch 自身提供了较完备的备份恢复的工具,可以精确的恢复数据,除非极端场景下否则不推荐使用 data 文件的备份恢复方式,在大数据平台上管理数据则推荐使用数据同步工具,更方便使用调度任务操作。
评论