这是继上一篇文章 “Elasticsearch:Searchable snapshot - 可搜索的快照” 的第二篇关于 searchable snapshot 文章。在上一篇文章中,我提到使用 search snapshot 的两个应用场景:
- 通过 mount snapshot API 来实现
- 通过 ILM 自动完成。 当可搜索快照操作达到冷或冻结阶段时,它将自动将常规索引转换为可搜索快照索引
第一种方式,我已经在上述文章中已经展示了。 在今天的文章中,我将展示如何在索引生命周期中使用 searchable snapshot。在索引生命周期管理中,我们可以在 cold 阶段自动地引入 searchable snapshot,从而达到节省成本的目的。
安装
如果你有 Elastic cloud 的账号或者其它云部署,你可以省去这一步。在今天的展示中,我们将使用一个本地部署的 Elasticsearch 集群来进行展示。我们可以参阅之前的文章 “Elastic:Data tiers 介绍及索引生命周期管理 - 7.10 之后版本” 来安装我们的 Elasticsearch。简单地说,我们参阅文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch” 下载 Elasticsearch,并解压缩到一个目录中。然后,我们在用户的 home 目录中创建一个如下的目录:
mkdir -p shared_folder/my_repo/
然后我们修改 Elasticsearch 的配置文件 config/elasticsearch.yml:
config/elasticsearch.yml
path.repo: <Your home>/shared_folder/my_repo
你需要把你的电脑上的路径替换上面的 path.repo。针对我的情况:
path.repo: /Users/liuxg/shared_folder/my_repo
我们把上面的这句话添加到 elasticsearch.yml 文件中去。然后,打开一个 terminal,并在 Elasticsearch 的安装根目录中打入如下的命令:
./bin/elasticsearch -E node.name=node1 -E node.roles=data_hot,data_content,master,ingest -Enode.max_local_storage_nodes=2
上面的命令创建一个叫做 data_hot 的名字叫做 node1 的节点。我们在另外一个 terminal 中,在同一个安装的 Elasticsearch 安装根目录中打入如下的命令:
./bin/elasticsearch -E node.name=node2 -E node.roles=data_cold,data_content,master,ingest -Enode.max_local_storage_nodes=2
在上面,它运行了一个叫做 node2 的数据层为 data_cold 的节点。这样我们的 Elasticsearch 集群就有两个节点:一个是 data_hot,而另外一个是 data_cold。
接下来,我们按照文章 “Kibana:如何在 Linux,MacOS 及 Windows上安装 Elastic 栈中的 Kibana” 按照好 Kibana。
我们可以在 Kibana 中打入如下的命令:
GET _cat/nodes?v
上面命令显示的结果为:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1 58 100 9 2.10 hims * node1
127.0.0.1 17 100 9 2.10 cims - node2
上面的 hims 的意思是:data_hot,master,ingest,而 cims 中 c 指的是 data_cold。显然我们的两个节点的 Elasticsearch 集群已经起来了。
开启试用许可
当我们使用 searchable snapshot 时,它是一个订阅的功能。我们需要在 Stack Management 中启动试用许可:
注册 snapshot 仓库
我们运行如下的命令来创建一个叫做 my_local_repo 的 snapshot 仓库。这个在 ILM 的 policy 创建中将被使用:
PUT _snapshot/my_local_repo
{
"type": "fs",
"settings": {
"location": "<Your home>/shared_folder/my_repo"
}
}
在上面,你需要依据你自己电脑上的路径修改上面的 location 参数。
创建 ILM 策略
我们需要创建一个 ILM 策略:
在上面,我们定义了 rollover 的条件:
- 当文档的大小超过 50G
- 当文档的数量超过 5 个文档
- 当文档在 hot phase 的时间超过 30 天
当其中的任何一个条件满足时,那么 rollover 将会发生。接下来,我们来定义 cold phase:
在上面,我们定义 searchable snapshot 仓库为 my_local_repo。点击 Save policy。我们可以在 Kibana 中使用如下的命令来进行查看:
GET _ilm/policy/searchable-snapshot-demo
上面的命令返回的结果为:
{
"searchable-snapshot-demo" : {
"version" : 2,
"modified_date" : "2021-04-27T05:22:29.253Z",
"policy" : {
"phases" : {
"cold" : {
"min_age" : "0d",
"actions" : {
"searchable_snapshot" : {
"snapshot_repository" : "my_local_repo",
"force_merge_index" : true
},
"set_priority" : {
"priority" : 0
}
}
},
"hot" : {
"min_age" : "0ms",
"actions" : {
"rollover" : {
"max_size" : "50gb",
"max_age" : "30d",
"max_docs" : 5
},
"set_priority" : {
"priority" : 100
}
}
}
}
}
}
}
从上面我们可以看出来,在 searchable_snapshot 中,它定义了 "force_merge_index" : true。这个对于提升 searchable snapshot 的速度非常有用。在上面,我们实现了 Hot phase => Cold phase 的迁移,如果条件满足的话。为了说明问题的方便,我们省去了 Warm phase。
因为 ILM 是在每隔一个时间间隔来进行检查的,为了下面的展示方便,我把检查的时间定义为每隔10秒:
PUT _cluster/settings
{
"transient": {
"indices.lifecycle.poll_interval": "10s"
}
}
这样我们就完成了 ILM 策略的定义。
定义 index template
我们在 Kibana 的 console 中输入如下的命令:
# Create tje template to apply the policy to every new backing index of the data stream
PUT _index_template/template_demo
{
"index_patterns": ["demo-*"],
"data_stream": {},
"priority": 200,
"template": {
"settings": {
"number_of_shards": 1,
"auto_expand_replicas": "0-1",
"index.lifecycle.name": "searchable-snapshot-demo",
"index.routing.allocation.include._tier_preference": "data_hot"
}
}
}
在上面,我们定义了一个叫做 template_demo 的 index template。也就是当任何索引的名字符合 demo-*,那么这个 index template 里的设置将会自动起作用。在这里,我们定义了一个 data_stream。在 template 里,我们定义了 index.lifecycle.name。它使用我们刚才定义的 ILM policy searchable-snapshot-demo。当我们的数据被导入时,将会在 data_hot 的节点进行处理。
创建一个 data stream
创建一个 data stream 是非常简单的:
# Create a data stream
PUT _data_stream/demo-ds
运行上面的命令。由于我们在上面已经创建了以 demo-* 为 index_pattern 的 index template,所以上面的创建是成功的。否则如果我们用如下的命令:
PUT _data_stream/demo
它将会是失败的。错误代码告诉你没有相对应的 index template。
# Check the shards allocation
GET _cat/shards/demo-ds?v
上面的命令显示:
index shard prirep state docs store ip node
.ds-demo-ds-2021.04.27-000001 0 p STARTED 0 208b 127.0.0.1 node1
从上面我们可以看出来,新生成的索引在 node1 上,也就是我们的 data_hot 节点。我们使用如下的命令来检查索引:
# Verify data stream indexes
GET _data_stream/demo-ds
{
"data_streams" : [
{
"name" : "demo-ds",
"timestamp_field" : {
"name" : "@timestamp"
},
"indices" : [
{
"index_name" : ".ds-demo-ds-2021.04.27-000001",
"index_uuid" : "6XomEKlRTq22hB1nyau78w"
}
],
"generation" : 1,
"status" : "GREEN",
"template" : "template_demo",
"ilm_policy" : "searchable-snapshot-demo",
"hidden" : false
}
]
}
导入数据
接下来,我们可以开始导入我们的数据了:
PUT _ingest/pipeline/add-timestamp
{
"processors": [
{
"set": {
"field": "@timestamp",
"value": "{{_ingest.timestamp}}"
}
}
]
}
POST demo-ds/_doc?pipeline=add-timestamp
{
"user": {
"id": "liuxg"
},
"message": "This is so cool!"
}
在上面,我们使用一个 pipeline 来添加一个 timestamp。我们执行上面的 POST 命令 7 七次,这样我们就创建了7个文档,从而满足大于5个文档的条件。rollover 将发生。我们可以看到如下的结果:
{
"_index" : ".ds-demo-ds-2021.04.27-000001",
"_type" : "_doc",
"_id" : "LGnBEXkBugydd_Q0zaSO",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 6,
"_primary_term" : 1
}
上面说明数据都是写入到 .ds-demo-ds-2021.04.27-000001 中去的。我们可以使用如下的命令来查看当前的 ILM 状态:
GET demo-ds/_ilm/explain
{
"indices" : {
".ds-demo-ds-2021.04.27-000002" : {
"index" : ".ds-demo-ds-2021.04.27-000002",
"managed" : true,
"policy" : "searchable-snapshot-demo",
"lifecycle_date_millis" : 1619501034528,
"age" : "6.18s",
"phase" : "hot",
"phase_time_millis" : 1619501036441,
"action" : "rollover",
"action_time_millis" : 1619501037520,
"step" : "check-rollover-ready",
"step_time_millis" : 1619501037520,
"phase_execution" : {
"policy" : "searchable-snapshot-demo",
"phase_definition" : {
"min_age" : "0ms",
"actions" : {
"rollover" : {
"max_size" : "50gb",
"max_age" : "30d",
"max_docs" : 5
},
"set_priority" : {
"priority" : 100
}
}
},
"version" : 2,
"modified_date_in_millis" : 1619500949253
}
},
".ds-demo-ds-2021.04.27-000001" : {
"index" : ".ds-demo-ds-2021.04.27-000001",
"managed" : true,
"policy" : "searchable-snapshot-demo",
"lifecycle_date_millis" : 1619501034494,
"age" : "6.21s",
"phase" : "cold",
"phase_time_millis" : 1619501037791,
"action" : "searchable_snapshot",
"action_time_millis" : 1619501038333,
"step" : "wait-for-shard-history-leases",
"step_time_millis" : 1619501038333,
"phase_execution" : {
"policy" : "searchable-snapshot-demo",
"phase_definition" : {
"min_age" : "0d",
"actions" : {
"searchable_snapshot" : {
"snapshot_repository" : "my_local_repo",
"force_merge_index" : true
},
"set_priority" : {
"priority" : 0
}
}
},
"version" : 2,
"modified_date_in_millis" : 1619500949253
}
}
}
}
从上面,我们可以看到两个 phase:hot 及 cold。我们可以通过如下的命令来查看所有的索引:
GET _cat/indices
上面的命令显示:
green open .ds-demo-ds-2021.04.27-000002 yrYjvXelQmy1rMtnW7LJuw 1 0 0 0 208b 208b
green open .kibana_task_manager_7.12.0_001 wsyiF-q3T728L5iOV6YyRA 1 1 9 2915 956.2kb 554.5kb
green open restored-.ds-demo-ds-2021.04.27-000001 hTo6C5jkSC-2vcq4yZ05eg 1 0 7 0 5.9kb 5.9kb
green open .apm-custom-link pTXXdpKOSnybUgPAPwYzjQ 1 1 0 0 416b 208b
green open .apm-agent-configuration hVHPkXnMRjaXUV4lRqJHCQ 1 1 0 0 416b 208b
green open kibana_sample_data_logs VikGPyZ2Rg22Z-9Jk6Blcw 1 1 14074 0 19.6mb 9.8mb
green open .kibana_7.12.0_001 cVFoWm1zRMu-2jwYANAALQ 1 1 164 266 8.8mb 4.4mb
green open .kibana-event-log-7.12.0-000001 gHuh-Kf4QA-uVTXOLwCFHg 1 1 8 0 86.9kb 43.4kb
green open twitter1 vcmlfo9KRkeS2VJEZ1TGgg 1 1 6 0 24.8kb 12.4kb
green open .tasks vTmHk4ClTaqIcdPtTchs3w 1 1 12 0 50.5kb 25.2kb
在上面,我们可以看到 .ds-demo-ds-2021.04.27-000002 及 restored-.ds-demo-ds-2021.04.27-000001 两个索引。我们可以分别查看它们当中的文档:
GET .ds-demo-ds-2021.04.27-000002/_count
{
"count" : 0,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
上面显示的结果为0。当我们重新写入一个文档时,那么它将写入到 .ds-demo-ds-2021.04.27-000002 索引中。
GET restored-.ds-demo-ds-2021.04.27-000001/_count
它显示:
{
"count" : 7,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
也就是是说所有的文档都可以通过访问 restored-.ds-demo-ds-2021.04.27-000001 来获得。这个索引就是通过 searchable snapshot 来实现的。
GET /_cat/shards/.ds-demo-ds-2021.04.27-000002/?v&h=index,shard,prirep,state,docs,store,node
上面的命令显示:
index shard prirep state docs store node
.ds-demo-ds-2021.04.27-000002 0 p STARTED 0 208b node1
而我们使用如下的命令:
GET /_cat/shards/restored-.ds-demo-ds-2021.04.27-000001/?v&h=index,shard,prirep,state,docs,store,node
它显示:
index shard prirep state docs store node
restored-.ds-demo-ds-2021.04.27-000001 0 p STARTED 7 5.9kb node2
上面的命令显示有7个文档。
GET /_cat/recovery/restored-.ds-demo-ds-2021.04.27-000001/?v&h=index,time,type,stage,files_percent,bytes_recovered,bytes_percent
上面的命令显示:
index time type stage files_percent bytes_recovered bytes_percent
restored-.ds-demo-ds-2021.04.27-000001 693ms snapshot done 100.0% 4751 100.0%
它显示类型是 snapshot,并且是100%数据可以使用的。
我们可以通过如下的命令来查询所有的文档:
GET demo-ds/_search
上面的命令将显示7个文档:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 2,
"successful" : 2,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 7,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "restored-.ds-demo-ds-2021.04.27-000001",
"_type" : "_doc",
"_id" : "U2nIEXkBugydd_Q0i6RB",
"_score" : 1.0,
"_source" : {
"@timestamp" : "2021-04-27T05:23:46.113665Z",
"message" : "This is so cool!",
"user" : {
"id" : "liuxg"
}
}
},
{
"_index" : "restored-.ds-demo-ds-2021.04.27-000001",
"_type" : "_doc",
"_id" : "WGnIEXkBugydd_Q0m6QX",
"_score" : 1.0,
"_source" : {
"@timestamp" : "2021-04-27T05:23:50.166958Z",
"message" : "This is so cool!",
"user" : {
"id" : "liuxg"
}
}
},
...
虽然我们文档的存储是在 snapshot 里,但是我们还是可以对它进行搜索,感觉它就像我们正常使用的一个索引一样。通过 searchable snapshot 的引入,我们可以把存储的成本几乎降下一半。