作者:刘晓国
在本教程中,我们介绍了一些与 Elasticsearch 中的分片管理相关的常见问题,其解决方案以及一些最佳实践。 在某些用例中,我们结合了特殊的技巧来完成任务。
将 Shard 从一个节点移动到另一个节点
当处理任何大小的集群时,这是最常见的用例之一。 一个典型的场景是,如果在一个节点上共存了太多分片,它们将全部用于查询或索引。
这种情况表示节点/群集健康的潜在风险。 因此,将分片从一个节点移动到另一个节点是一个好习惯。 Elasticsearch 可能不会自动处理这种情况,这意味着我们需要手动进行干预。 如何做到这一点?
Elasticsearch 提供了一个集群级 API,该 API 允许将碎片从一个节点移动到另一个节点。 让我们在下面查看使用此 API 的示例:
重要的是要注意,在处理任何重新路由命令之后,Elasticsearch 将正常执行重新平衡(尊重诸如 cluster.routing.rebalance.enable 之类的设置的值),以便保持平衡状态。例如,如果请求的分配包括将分片从节点1移动到节点2,则这可能导致分片从节点2移动回到节点1来保持平衡。
可以使用 cluster.routing.allocation.enable 设置将集群设置为禁用分配。如果禁用了分配,则将执行的唯一分配是使用 reroute 命令指定的显式分配,以及由于重新平衡而导致的后续分配。
通过使用 ?dry_run URI 查询参数,或通过在请求正文中传递 “dry_run”: true,可以在 “dry run” 模式下运行重新路由命令。这将计算将命令应用于当前群集状态的结果,并在应用命令(和重新平衡)后返回结果群集状态,但实际上不会执行所请求的更改。
我们可以使用 reroute API 来实现把一个 shard 从一个节点移动到另外一个节点。下面是一个例子:
POST /_cluster/reroute { "commands": [ { "move": { "index": "test", "shard": 0, "from_node": "node1", "to_node": "node2" } }, { "allocate_replica": { "index": "test", "shard": 1, "node": "node3" } } ] }
在上面,我们强制把索引 test 的 shard 0 从node1 移到 node2。我们同时也强制分配索引 test 的 shard 1到node3中。
停用节点
另一个用例是从活动集群中停用节点。 这种情况下的主要挑战之一是在不导致群集停机或重启的情况下停用节点。 幸运的是,Elasticsearch 提供了一个选项,可以在不丢失数据或不会造成停机的情况下,优雅地删除/停用节点。 让我们看看如何实现它:
PUT _cluster/settings { "transient": { "cluster.routing.allocation.exclude._ip": "IP of the node" } }
上面的 API 使集群停止分配任何东西到指定节点并排除它。 同时,来自该节点的数据将被移植到非排除节点。 数据传输将在后台进行,完成后将导致从群集中完全删除该节点。
停用某个节点时,其他节点中可用的磁盘空间应大于要传输的数据大小。 否则,群集状态可能会变为红色或黄色,这可能会导致停机。
拥有其他选项来标识要停用的节点通常会很有帮助。 在上面的示例中,我们用节点的 “ip” 标识了该节点。 我们还可以使用集群中唯一的 “node ID” 和 “node name” 进行相同的操作。
按节点ID排除
PUT _cluster/settings { "transient": { "cluster.routing.allocation.exclude._id": "unique id of the node" } }
按名称排除节点
PUT _cluster/settings { "transient": { "cluster.routing.allocation.exclude._name": "name of the node" } }
我们如何查看节点的停用是否结束? 为此,我们有两个规定:
方法一
我们使用如下的方法:
GET _cluster/health?pretty
显示的结果是:
{ "cluster_name" : "elasticsearch", "status" : "yellow", "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 26, "active_shards" : 26, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 19, "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" : 57.77777777777777 }
在上面 relocating_shards 显示的数值为0,表明目前没有 shard 被重新转移。
方法二
使用以下 API 检查的专有的节点的状态:
GET _cat/nodes?v
通过上面的 API 我们得到 node 的名字:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 127.0.0.1 42 50 2 1.94 dilm * liuxg-2.local
然后使用如下的 API:
GET _nodes/liuxg-2.local/stats/indices
在上面的 liuxg-2.local 为我们的 node 的名字。显示的结果为:
"nodes" : { "Zs0Uy-9mTDOifm5Ef8U6FA" : { "timestamp" : 1581585326681, "name" : "liuxg-2.local", "transport_address" : "127.0.0.1:9300", "host" : "127.0.0.1", "ip" : "127.0.0.1:9300", "roles" : [ "ingest", "master", "data", "ml" ], "attributes" : { "ml.machine_memory" : "34359738368", "xpack.installed" : "true", "ml.max_open_jobs" : "20" }, "indices" : { "docs" : { "count" : 0, "deleted" : 8 }, ...
如果上述的 indices.docs.count 的值为 0,就表示转移已经完成。
重命名索引
另一个用例是重命名索引。 可以根据使用情况以多种方式完成此操作。
Aliasing
如果我们希望在不丢失任何数据的情况下重命名索引,则最常用的方法是别名。
例如,我们想将索引 “testindex” 重命名为 “testindex-1”。 我们可以为索引 “testindex” 提供别名 “testindex-1”,以便所有引用 “testindex-1” 的请求现在都将路由到 “testindex”。 可以按照以下步骤完成:
POST _aliases { "actions": [ { "add": { "index": "testindex", "alias": "testindex-1" } } ] }
这种方法使我们可以在停机时间为零的情况下重命名索引。
Reindex API
有时,别名并不是重命名的最佳选择。 在这种情况下,我们剩下称为重新索引的选项。 它将所有文档从目标索引重新索引到目标索引。 为了有效地做到这一点,需要检查两件事:
机器上是否还有足够的空间。
目标索引是否存在正确的映射。
如果满足以上两个条件,我们可以使用如下所示的 reindex API:
POST _reindex { "source": { "index": "testindex" }, "dest": { "index": "testindex-1" } }
有用链接:
1) In depth guide to running Elasticsearch in production: https://facinating.tech/2020/02/22/in-depth-guide-to-running-elasticsearch-in-production/