腾讯云Elasticsearch Service产品提供了一种全托管的云端服务,客户可以在腾讯云控制台一键创建集群、轻松管理集群,以及高度灵活的弹性变配模式。因此,使用腾讯云ES,您可以快速部署、按需扩展您的集群,简化复杂运维工作,快速构建日志分析、异常监控、网站搜索、企业搜索、BI分析等各类业务。同时腾讯云ES团队也提供了高度专业的平台管控、内核开发及集群运维能力。本文将从集群运维角度出发,分享我们日常工作中帮客户运维集群时常用到的一些命令,以及各命令适用的场景。
集群相关命令
在介绍ES集群常用命令之前,我们先回顾下ES集群的分布式基本原理,从下图1中可以看出,ES集群是由多个节点组成,节点中会选举出一个Master节点,用于管理、调度集群;每个节点上存储部分索引数据,索引数据由分片来承载,即集群中有多个索引,每个索引中又会有多个分片,分片中有一到多个segment,segment中存储具体的doc数据。
如下图1所示,当我们通过Client对集群执行写入操作时,请求首先到达的节点1被称为协调节点,协调节点1根据请求信息计算出主分片所在节点3,并将请求转发至该节点3,节点3在主分片写入成功后,再同步将请求转发至副本分片所在的节点1和2,待副本分片所在节点都写入成功后,节点3返回请求给协调节点1,协调节点1再返回写入成功信息给Client。
以上,我们简单回顾了下ES集群的分布式基本原理及读写模型。下面我们详细介绍下ES集群相关的常用命令。
1、查看集群健康状态
代码语言:javascript复制GET _cluster/health
命令Response:
代码语言:javascript复制{
"cluster_name" : "es-wr-test-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 6,
"number_of_data_nodes" : 3,
"active_primary_shards" : 300,
"active_shards" : 561,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
我们知道集群共有三种健康状态,分别是red、yellow和green。通过该命令,我们不仅可以了解集群名称cluster_name
,集群的健康状态status,还可以了解到集群当前有多少节点number_of_nodes,多少个数据节点number_of_data_nodes,有多少个主分片active_primary_shards,以及正在搬迁中的分片relocating_shards、初始化中的分片initializing_shards,还未正常分配的分片unassigned_shards等信息。如果集群是非green状态,我们还可以通过active_shards_percent_as_number来查看集群恢复的进度。
另外从number_of_pending_tasks参数也能够看出当前集群的任务堆积情况,如果number_of_pending_tasks数量较大,则表明Master在处理task时有点力不从心,承载的压力较大了。
图2是我们腾讯云ES客户的一个日志集群健康信息,从该图中我们就很清晰得看到集群当前的状态为yellow,还有690个副本分片未正常分配,且有126个task任务堆积,当前的分片恢复进展为97.8%等。
从该API的官方文档中我们可以看到有一个level参数,该参数是一个可选参数,提供了三种枚举值,分别是cluster、indices和shards。默认就是cluster,如果我们需要查看索引或者分片维度的健康信息,则可以使用如下API。
代码语言:javascript复制GET /_cluster/health?level=indices
GET /_cluster/health?level=shards
同时还支持查看某一个具体的索引的健康信息:
代码语言:javascript复制GET /_cluster/health/wr_index_1?level=indices
GET /_cluster/health/wr_index_1?level=shards
2、查看任务堆积详情
代码语言:javascript复制GET /_cat/pending_tasks
返回Response:
代码语言:javascript复制insertOrder timeInQueue priority source
1685 855ms HIGH update-mapping [foo][t]
1686 843ms HIGH update-mapping [foo][t]
1693 753ms HIGH refresh-mapping [foo][[t]]
1688 816ms HIGH update-mapping [foo][t]
1689 802ms HIGH update-mapping [foo][t]
1690 787ms HIGH update-mapping [foo][t]
1691 773ms HIGH update-mapping [foo][t]
在上面的API中我们可以通过GET _cluster/health
来查看集群当前是否有堆积。如果有堆积也只能看到堆积的量,不能看到堆积了哪些任务,而GET/_cat/pending_tasks
API则可以直接查看具体是哪些任务在执行。从该API返回的信息中我们可以看出有一个priority的字段,用于标记该task的优先级。翻看源码,我们可以看到Master处理的task主要有六种优先级。其优先度从高到低如下所示:
IMMEDIATE > URGENT > HIGH > NORMAL > LOW > LANGUID.
通常创建索引的优先级是URGENT,更新Mapping的优先级是HIGH,如果数据在高压力写入时频繁更新mapping,则会导致pending_tasks堆积的比较严重,对Master造成较大压力。
另外与GET/_cat/pending_tasks
还有一个相似的API。即:
GET /_cluster/pending_tasks
返回Response:
代码语言:javascript复制{
"tasks": [
{
"insert_order": 101,
"priority": "URGENT",
"source": "create-index [foo_9], cause [api]",
"executing" : true,
"time_in_queue_millis": 86,
"time_in_queue": "86ms"
},
{
"insert_order": 46,
"priority": "HIGH",
"source": "shard-started ([foo_2][1], node[tMTocMvQQgGCkj7QDHl3OA], [P], s[INITIALIZING]), reason [after recovery from shard_store]",
"executing" : false,
"time_in_queue_millis": 842,
"time_in_queue": "842ms"
},
{
"insert_order": 45,
"priority": "HIGH",
"source": "shard-started ([foo_2][0], node[tMTocMvQQgGCkj7QDHl3OA], [P], s[INITIALIZING]), reason [after recovery from shard_store]",
"executing" : false,
"time_in_queue_millis": 858,
"time_in_queue": "858ms"
}
]
}
从该API中可以更加直观的看到该task是否在执行executing,以及在队列中等待的时间time_in_queue_millis等详细信息。
3、查看集群元数据状态信息
代码语言:javascript复制GET /_cluster/state/<metrics>/<target>
通过该API可以获取到集群维度非常丰富的元数据相关信息,例如集群中所有节点的名称、ip、tcp/http端口号、节点属性信息。还可以获取到配置的索引模版信息、索引分片路由信息、快照信息等等。由于该API返回的元数据信息较大,因此不单独列出返回Response。
如果只想查看metadata信息,则可以在API后面加上/metadata的path:
代码语言:javascript复制GET /_cluster/state/metadata
同理,如果只想查看routing_table信息,则也可以直接在API后面加上/routing_table的path:
代码语言:javascript复制GET /_cluster/state/routing_table
通过在routing_table path后面加上具体的索引名称GET /_cluster/state/routing_table/wr_index_1
,我们可以非常清晰的看到该索引下每个分片的详细路由信息,如图3所示。
返回Response:
代码语言:javascript复制{
"cluster_name" : "es-wr-test-cluster",
"cluster_uuid" : "2LLChSPGRgqZr1cz3b8cXw",
"routing_table" : {
"indices" : {
"wr_index_1" : {
"shards" : {
"2" : [
{
"state" : "STARTED",
"primary" : true,
"node" : "hz6BqoupSuOUuWykrX5c2g",
"relocating_node" : null,
"shard" : 2,
"index" : "wr_index_1",
"allocation_id" : {
"id" : "I57fOXAwRvWVv4BYchBW8g"
}
}
],
"1" : [
{
"state" : "STARTED",
"primary" : true,
"node" : "CaQnhaYpQw6vbabGwKaPTw",
"relocating_node" : null,
"shard" : 1,
"index" : "wr_index_1",
"allocation_id" : {
"id" : "XqFJWb8fRia00WE8U75jTg"
}
}
],
"0" : [
{
"state" : "STARTED",
"primary" : true,
"node" : "yvtpqeypTke6aFxxwYSjjA",
"relocating_node" : null,
"shard" : 0,
"index" : "wr_index_1",
"allocation_id" : {
"id" : "wPRDAv4cTkKQsEtXL8vLHQ"
}
}
]
}
}
}
}
}
另外,我们也可以直接通过routing_nodes来查看该集群的节点路由信息,如图4所示:
代码语言:javascript复制GET /_cluster/state/routing_nodes
部分返回Response:
从节点的路由元数据信息中,我们可以看到每个节点上分配的索引名称以及具体分片信息,分配状态等详情。
4、查看集群指标统计信息
代码语言:javascript复制GET /_cluster/stats
该API展示了集群维度统计的相关指标信息。例如索引分片数量、存储大小、内存使用率、磁盘使用率等信息,以及集群节点数量、节点角色、属性、jvm版本、内存使用率、cpu使用率等监控信息。该API的返回体数据也非常多,此处只列出部分信息。如图4所示:
5、查看集群分片分配详情
代码语言:javascript复制GET _cluster/allocation/explain
该API主要用来查看集群中非green分片未正常分配的具体原因,对于诊断集群健康状态异常具有非常大的帮助。例如在我们的测试集群中,有三个数据节点,测试索引wr_index_1有三个主分片,现在我们人为将副本设置为3,制造出分片无法分配的情况,如图5所示。
然后我们执行GET _cluster/allocation/explain
API,查看分配失败的具体原因,返回如下
返回Response:
代码语言:javascript复制{
"index" : "wr_index_1",
"shard" : 1,
"primary" : false,
"current_state" : "unassigned",
"unassigned_info" : {
"reason" : "REPLICA_ADDED",
"at" : "2021-12-05T04:27:48.067Z",
"last_allocation_status" : "no_attempt"
},
"can_allocate" : "no",
"allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes",
"node_allocation_decisions" : [
{
"node_id" : "CaQnhaYpQw6vbabGwKaPTw",
"node_name" : "16268031430xx00145532",
"transport_address" : "xx.0.96.xx:9300",
"node_attributes" : {
"ml.machine_memory" : "50299387904",
"rack" : "cvm_4_200003",
"xpack.installed" : "true",
"set" : "200003",
"transform.node" : "true",
"ip" : "xx.20.57.70",
"temperature" : "hot",
"ml.max_open_jobs" : "20",
"region" : "4"
},
"node_decision" : "no",
"deciders" : [
{
"decider" : "same_shard",
"decision" : "NO",
"explanation" : "a copy of this shard is already allocated to this node [[wr_index_1][1], node[CaQnhaYpQw6vbabGwKaPTw], [P], s[STARTED], a[id=XqFJWb8fRia00WE8U75jTg]]"
}
]
},
{
"node_id" : "hz6BqoupSuOUuWykrX5c2g",
"node_name" : "16268031xx43000145432",
"transport_address" : "xx.0.96.9:9300",
"node_attributes" : {
"ml.machine_memory" : "50299387904",
"rack" : "cvm_4_200003",
"xpack.installed" : "true",
"set" : "200003",
"transform.node" : "true",
"ip" : "xx.20.56.176",
"temperature" : "hot",
"ml.max_open_jobs" : "20",
"region" : "4"
},
"node_decision" : "no",
"deciders" : [
{
"decider" : "same_shard",
"decision" : "NO",
"explanation" : "a copy of this shard is already allocated to this node [[wr_index_1][1], node[hz6BqoupSuOUuWykrX5c2g], [R], s[STARTED], a[id=Z5wU3KNHR6-e4Re0PJ4-yQ]]"
}
]
},
{
"node_id" : "yvtpqeypTke6aFxxwYSjjA",
"node_name" : "162680314xx3000145332",
"transport_address" : "xx.0.96.20:9300",
"node_attributes" : {
"ml.machine_memory" : "50299125760",
"rack" : "cvm_4_200003",
"xpack.installed" : "true",
"set" : "200003",
"transform.node" : "true",
"ip" : "xx.20.53.190",
"temperature" : "hot",
"ml.max_open_jobs" : "20",
"region" : "4"
},
"node_decision" : "no",
"deciders" : [
{
"decider" : "same_shard",
"decision" : "NO",
"explanation" : "a copy of this shard is already allocated to this node [[wr_index_1][1], node[yvtpqeypTke6aFxxwYSjjA], [R], s[STARTED], a[id=LiNqe7G6SwK7q8JZsYphvw]]"
}
]
}
]
}
从返回的信息中我们可以很清晰得看到索引的状态current_state为:unassigned,分片未正常分配的原因reason是:“REPLICA_ADDED”。然后每个节点给出的无法分配原因是该索引的副本分片已经在本节点上存在了,无法将两个相同的分片分配在同一个节点上。
代码语言:javascript复制"explanation" : "a copy of this shard is already allocated to this node [[wr_index_1][1], node[yvtpqeypTke6aFxxwYSjjA], [R], s[STARTED], a[id=LiNqe7G6SwK7q8JZsYphvw]]"
我们也可以直接在请求体中指定某一个具体的索引以及分片号,来查看该分片未正常分配的原因:
代码语言:javascript复制GET _cluster/allocation/explain
{
"index": "wr_index_1",
"shard": 0,
"primary": true
}
如果集群本身就是green的状态,即所有索引分片都已经正常分配了,再执行该API则会返回400错误:
返回Response:
代码语言:javascript复制{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "unable to find any unassigned shards to explain [ClusterAllocationExplainRequest[useAnyUnassignedShard=true,includeYesDecisions?=false]"
}
],
"type" : "illegal_argument_exception",
"reason" : "unable to find any unassigned shards to explain [ClusterAllocationExplainRequest[useAnyUnassignedShard=true,includeYesDecisions?=false]"
},
"status" : 400
}
6、更改分片分配
代码语言:javascript复制POST /_cluster/reroute
该命令允许我们手动改变集群中某些分片的分配策略。例如我们可以通过该API来将部分分片从指定节点上移动到其他节点,以平衡节点之间分片的数量和磁盘的使用率。
POST/_cluster/reroute
API的请求体commands参数中,定义了支持执行的几种属性,分别是move、cancel、allocate_replica、allocate_stale_primary和allocate_empty_primary。下面分别介绍:
1)move,移动分片
move属性可以将一个已经正常分配的分片,从一个节点移动到另外一个节点上。move属性的请求体中接收index名称和要移动的分片号,以及该分片源节点名称和目标节点名称。例如,下面的命令就是向将测试索引wr_index_1的0号分片从节点1626803143110145332中迁移到节点1626803143110145432上。
代码语言:javascript复制POST /_cluster/reroute
{
"commands": [
{
"move": {
"index": "wr_index_1",
"shard": 0,
"from_node": "1626803143110145332",
"to_node": "1626803143110145432"
}
}
]
}
2)cancel,取消搬迁或恢复
该属性可以可以分片的恢复或者分配。接收index名称、要取消分片的分片号以及需要取消分配的节点的名称。通常该属性只能取消副本分片的分配或者恢复。如果需要取消主分片的分配,则需要在请求中包含allow_primary标志。
代码语言:javascript复制POST /_cluster/reroute
{
"commands": [
{
"cancel": {
"index": "wr_index_1",
"shard": 0,
"node": "1626803143110145332"
}
}
]
}
其中node的值不是分片源节点名称,而是目标节点名称。
3)allocate_stale_primary,分配陈旧主分片
该属性的作用是当主分片由于version较旧原因未能正常分配的时候。可以通过allocate_stale_primary属性来使处于stale的主分片正常分配,但是有可能会丢失部分数据,因此需要将accept_data_loss参数设置为true。
代码语言:javascript复制cannot allocate because all found copies of the shard are either stale or corrupt
具体API如下:
代码语言:javascript复制POST /_cluster/reroute
{
"commands" : [
{
"allocate_stale_primary" : {
"index" : "wr_index_1",
"shard" : 0,
"node" : "1596598365000222032",
"accept_data_loss": true
}
}
]
}
在执行该命令的时候,需要通过GET _shard_stores?pretty
查找stale主分片所在的节点,查找到相关的节点后,然后再进行allocate_stale_primary,也就是说必须是保存了stale分片的节点,而不是其他节点。否则会导致完全丢失数据。
需要注意的是,如果该名称执行成功后,保存了更新副本分片的节点重新加入了集群后,那么该更新副本分片上的数据可能会被删除或者被覆盖。
4)allocate_empty_primary,分配空主分片
allocate_empty_primary属性的作用是可以分配一个空数据的主分片到一个节点上。我们在平常的集群运维工作中,经常会遇到有些索引由于没有设置副本,又遇到文件损坏,导致主分片分配不了的情况。那么针对这种情况,如果客户能够接收丢失一个分片数据,则可以通过如下API来分片一个空数据的主分片,以让集群恢复green。该属性接收的参数信息如下:
代码语言:javascript复制POST _cluster/reroute
{
"commands": [
{
"allocate_empty_primary": {
"index": "wr_index_1",
"shard": 0,
"node": "1582165889001471732",
"accept_data_loss": true
}
}
]
}
7、查看和设置集群settings信息
代码语言:javascript复制GET /_cluster/settings
当我们需要知道集群设置了哪些settings信息时,可以通过GET /_cluster/settings
API进行查看,例如我们先设置集群的并发度和迁移速度后,然后执行查看API,则可以看到返回的Response如下:
{
"persistent" : { },
"transient" : {
"cluster" : {
"routing" : {
"allocation" : {
"node_concurrent_recoveries" : "8"
}
}
},
"indices" : {
"recovery" : {
"max_bytes_per_sec" : "80mb"
}
}
}
}
通过该API,我们能够很清晰得看到手动设置了哪些集群属性信息。
而如果需要修改某些settings属性时,则可以统一使用PUT /_cluster/settings
API。下面我们介绍几种常见的属性设置API:
1)设置集群分片搬迁并发度和速度
代码语言:javascript复制PUT /_cluster/settings
{
"persistent" : {
"cluster.routing.allocation.node_concurrent_recoveries": "8",
"cluster.routing.allocation.cluster_concurrent_rebalance": "8",
"indices.recovery.max_bytes_per_sec": "80mb"
},
"transient" : {
"cluster.routing.allocation.node_concurrent_recoveries": "8",
"cluster.routing.allocation.cluster_concurrent_rebalance": "8",
"indices.recovery.max_bytes_per_sec": "80mb"
}
}
2)设置集群磁盘使用率水位线
代码语言:javascript复制PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.disk.watermark.high":"95%",
"cluster.routing.allocation.disk.watermark.low":"93%"
},
"transient": {
"cluster.routing.allocation.disk.watermark.high":"95%",
"cluster.routing.allocation.disk.watermark.low":"93%"
}
}
3)将某个节点上的数据驱逐
代码语言:javascript复制PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.exclude._name": "161070127670100270532"
},
"transient": {
"cluster.routing.allocation.exclude._name": "161070127670100270532"
}
}
4)设置集群最小选主节点数
代码语言:javascript复制PUT _cluster/settings
{
"persistent": {
"discovery.zen.minimum_master_nodes": 2
},
"transient": {
"discovery.zen.minimum_master_nodes": 2
}
}
5)将集群设置为只读
代码语言:javascript复制PUT _cluster/settings
{
"transient": {
"cluster.blocks.read_only_allow_delete": true
}
}
6)开启/关闭xpack集群监控采集
代码语言:javascript复制PUT _cluster/settings
{
"persistent": {
"xpack.monitoring.collection.enabled": true,
"xpack.monitoring.collection.interval": "10s" // 设置采集周期
}
}
7)开启/关闭集群通配符模式
代码语言:javascript复制PUT _cluster/settings
{
"transient": {
"action.destructive_requires_name": "false"
}
}
8)设置集群中每个节点的总分片数
代码语言:javascript复制PUT _cluster/settings
{
"transient": {
"cluster.max_shards_per_node": 10000
}
}
9)设置集群mapping更新超时时间
代码语言:javascript复制PUT _cluster/settings
{
"transient": {
"indices.mapping.dynamic_timeout": "20s"
}
}
10)开启/关闭集群自动创建索引
代码语言:javascript复制PUT _cluster/settings
{
"transient": {
"action.auto_create_index": true
}
}
8、查看集群任务详情
tasks API可以获取到集群各节点上当前正在执行的任务信息。
代码语言:javascript复制GET /_tasks
GET /_tasks/<task_id>
例如我们查看某一个节点上正在执行的任务信息,使用GET _tasks?nodes=1626803143000145732
API,返回Response如下:
{
"nodes" : {
"AexOqq25T7SRf6tzMYzO1Q" : {
"name" : "1626803143000145732",
"transport_address" : "xx.0.96.49:9300",
"host" : "xx.0.96.49",
"ip" : "xx.0.96.49:9300",
"roles" : [
"master",
"ml",
"remote_cluster_client"
],
"attributes" : {
"ml.machine_memory" : "16478932992",
"rack" : "cvm_4_200003",
"xpack.installed" : "true",
"set" : "200003",
"transform.node" : "false",
"ip" : "xx.20.58.203",
"temperature" : "hot",
"ml.max_open_jobs" : "20",
"region" : "4"
},
"tasks" : {
"AexOqq25T7SRf6tzMYzO1Q:75293702" : {
"node" : "AexOqq25T7SRf6tzMYzO1Q",
"id" : 75293702,
"type" : "transport",
"action" : "cluster:monitor/tasks/lists[n]",
"start_time_in_millis" : 1638692226488,
"running_time_in_nanos" : 88582,
"cancellable" : false,
"parent_task_id" : "hz6BqoupSuOUuWykrX5c2g:2877050563",
"headers" : { }
}
}
}
}
}
从tasks参数中可以看到有一个task正在执行,即taskId为:AexOqq25T7SRf6tzMYzO1Q:75293702。
如果我们要查看该taskId的详细情况,则使用API:GET/_tasks/AexOqq25T7SRf6tzMYzO1Q:75293702
。
查看该API的官方文档,可以看到有一个detailed参数,该参数默认设置为false。如果我们显式指定为true,则可以获取到当前正在执行的task的更为详细的信息。
代码语言:javascript复制GET _tasks?actions=*search&detailed=true
取消任务:如果我们希望能够手动终止某些任务执行的话,则可以使用_tasks/cancel
来设置,例如我们想要取消节点1和节点2上reindex任务的执行,则使用如下API:
POST _tasks/_cancel?nodes=nodeId1,nodeId2&actions=*reindex
我们还可以通过_cat/tasks来获取和_tasks相同的信息。
代码语言:javascript复制GET _cat/tasks?v=true
返回Response如图6所示:
集群运维常用命令总结
本文我们从集群运维角度出发,向大家介绍了我们在日常的集群运维工作中常用到的集群维度相关命令。下面以表格的形式总结如下:
命令 | API命令说明 |
---|---|
GET /_cluster/health | 查看集群健康状态等信息 |
GET /_cat/pending_tasks | 查看集群任务堆积情况 |
GET /_cluster/state/<metrics>/<target> | 查看集群详细的元数据信息 |
GET /_cluster/stats | 查看集群各维度的统计信息,包括节点CPU、JVM等使用率情况 |
GET _cluster/allocation/explain | 查看集群分片分配详情 |
POST /_cluster/reroute | 更改分片分配策略 |
GET /_cluster/settings | 查看集群层面settings设置信息 |
GET /_tasks | 查看集群中正在执行的task信息 |