一、初始集群
前言
现在,讲述一个真实的故事!
一天小黑在完成项目任务,美滋滋的开始准备和对象约会的时候。突然接到命令,公司谈了个大项目,预计一天后,将会有海量的搜索请求访问小黑写的接口。小黑慌了啊!该怎么办?该怎么办?是删库还是跑路?手里目前就运行着一台es实例、但幸好有三台备用的服务器。你自己该如何搞?赶紧拉个QQ群,把之前的朋友资源都用上!鼓捣一圈,发现没人能救得了自己!这个苦逼群主就管理自己!后来就准备死磕es了。谢天谢地,可爱的elasticsearch集群还真能救得了!
在elasticsearch中,一个节点(node)就是一个elasticsearch实例,而一个集群(cluster)由一个或多个节点组成,它们具有相同的cluster.name
,并且协同工作,分享数据和负载。当加入新的节点或者删除一个节点时,集群就会感知到并平衡数据。
当Elasticsearch用于构建高可用和可扩展的系统时(解决小黑的燃眉之急)。扩展的方式可以是:
- 购买更好的服务器(纵向扩展(vertical scale or scaling up))
- 购买更多的服务器(横向扩展(horizontal scale or scaling out))
Elasticsearch虽然能从更强大的硬件中获得更好的性能,但是纵向扩展有它的局限性。真正的扩展应该是横向的,它通过增加节点来均摊负载和增加可靠性。
对于大多数数据库而言,横向扩展意味着你的程序将做非常大的改动才能利用这些新添加的设备。对比来说,Elasticsearch天生就是分布式的:它知道如何管理节点来提供高扩展和高可用。这意味着你的程序不需要关心这些。
这正好合了小黑的意,正好有三台备用服务器。只需要这个水平扩展就可以继续美滋滋的去约会了。集群看起来难,做起来——试试看!
向集群中加入节点
小黑首先在本地环境搭建集群,那么只需要三步就行了:
- 在本地单独的目录中,再复制一份elasticsearch文件
- 分别启动bin目录中的启动文件
- 貌似没有第三步了.....
然后,在浏览器地址栏输入:
http://127.0.0.1:9200/_cluster/health?pretty
返回的结果中:
cluster_name "elasticsearch"
status "green"
timed_out false
number_of_nodes 2
number_of_data_nodes 2
active_primary_shards 0
active_shards 0
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
通过number_of_nodes
可以看到,目前集群中已经有了两个节点了。小黑一看,这完事了啊!走走走,去约会!
发现节点
小黑在约会的路上突然对一个问题很好奇,这两个es实例是如何发现相互发现,并且自动的加入集群的?谁是群主?不由自主的思考入迷......
es使用两种不同的方式来发现对方:
- 广播
- 单播
也可以同时使用两者,但默认的广播,单播需要已知节点列表来完成。
广播
当es实例启动的时候,它发送了广播的ping请求到地址224.2.2.4:54328
。而其他的es实例使用同样的集群名称响应了这个请求。
一般这个默认的集群名称就是上面的cluster_name
对应的elasticsearch
。通常而言,广播是个很好地方式。想象一下,广播发现就像你大吼一声:别说话了,再说话我就发红包了!然后所有听见的纷纷响应你。
但是,广播也有不好之处,过程不可控。
单播
当节点的ip(想象一下我们的ip地址是不是一直在变)不经常变化的时候,或者es只连接特定的节点。单播发现是个很理想的模式。使用单播时,我们告诉es集群其他节点的ip及(可选的)端口及端口范围。我们在elasticsearch.yml
配置文件中设置:
discovery.zen.ping.unicast.hosts: ["10.0.0.1", "10.0.0.3:9300", "10.0.0.6[9300-9400]"]
大家就像交换微信名片一样,相互传传就加群了.....
一般的,我们没必要关闭单播发现,如果你需要广播发现的话,配置文件中的列表保持空白即可。
选取主节点
无论是广播发现还是到单播发现,一旦集群中的节点发生变化,它们就会协商谁将成为主节点,elasticsearch认为所有节点都有资格成为主节点。如果集群中只有一个节点,那么该节点首先会等一段时间,如果还是没有发现其他节点,就会任命自己为主节点。
对于节点数较少的集群,我们可以设置主节点的最小数量,虽然这么设置看上去集群可以拥有多个主节点。实际上这么设置是告诉集群有多少个节点有资格成为主节点。怎么设置呢?修改配置文件中的:
discovery.zen.minimum_master_nodes: 3
一般的规则是集群节点数除以2(向下取整)再加一。比如3个节点集群要设置为2。这么着是为了防止脑裂(split brain)问题。
什么是脑裂
脑裂这个词描述的是这样的一个场景:(通常是在重负荷或网络存在问题时)elasticsearch集群中一个或者多个节点失去和主节点的通信,然后小老弟们(各节点)就开始选举新的主节点,继续处理请求。这个时候,可能有两个不同的集群在相互运行着,这就是脑裂一词的由来,因为单一集群被分成了两部分。为了防止这种情况的发生,我们就需要设置集群节点的总数,规则就是节点总数除以2再加一(半数以上)。这样,当一个或者多个节点失去通信,小老弟们就无法选举出新的主节点来形成新的集群。因为这些小老弟们无法满足设置的规则数量。
我们通过下图来说明如何防止脑裂。比如现在,有这样一个5个节点的集群,并且都有资格成为主节点:
为了防止脑裂,我们对该集群设置参数:
discovery.zen.minimum_master_nodes: 3 # 3=5/2+1
之前原集群的主节点是node1
,由于网络和负荷等原因,原集群被分为了两个switch
:node1 、2
和node3、4、5
。因为minimum_master_nodes
参数是3,所以node3、4、5
可以组成集群,并且选举出了主节点node3
。而node1、2
节点因为不满足minimum_master_nodes
条件而无法选举,只能一直寻求加入集群(还记得单播列表吗?),要么网络和负荷恢复正常后加入node3、4、5
组成的集群中,要么就是一直处于寻找集群状态,这样就防止了集群的脑裂问题。
除了设置minimum_master_nodes
参数,有时候还需要设置node_master
参数,比如有两个节点的集群,如果出现脑裂问题,那么它们自己都无法选举,因为都不符合半数以上。这时我们可以指定node_master
,让其中一个节点有资格成为主节点,另外一个节点只能做存储用。当然这是特殊情况。
那么,主节点是如何知道某个小老弟(节点)还活着呢?这就要说到错误识别了。
错误识别
其实错误识别,就是当主节点被确定后,建立起内部的ping机制来确保每个节点在集群中保持活跃和健康,这就是错误识别。
主节点ping集群中的其他节点,而且每个节点也会ping主节点来确认主节点还活着,如果没有响应,则宣布该节点失联。想象一下,老大要时不常的看看(循环)小弟们是否还活着,而小老弟们也要时不常的看看老大还在不在,不在了就赶紧再选举一个出来!
但是,怎么看?多久没联系算是失联?这些细节都是可以设置的,不是一拍脑门子,就说某个小老弟挂了!在配置文件中,可以设置:
discovery.zen.fd.ping_interval: 1
discovery.zen.fd.ping_timeout: 30
discovery_zen.fd.ping_retries: 3
每个节点每隔discovery.zen.fd.ping_interval
的时间(默认1秒)发送一个ping请求,等待discovery.zen.fd.ping_timeout
的时间(默认30秒),并尝试最多discovery.zen.fd.ping_retries
次(默认3次),无果的话,宣布节点失联,并且在需要的时候进行新的分片和主节点选举。
根据开发环境,适当修改这些值。
小黑觉得研究的差不多了就美滋滋的要继续约会,但是恍然大悟——我是条单身狗,哪来的女朋友,就看着右手,陷入了沉思........
欢迎斧正,that's all see also:[elasticsearch的master选举机制](https://www.cnblogs.com/zziawanblog/p/6577383.html)
二、本地环境搭建集群
前言#
我们搭建一个有4个节点的单播集群。
- 系统环境:windows 10
- elasticsearch版本: elasticsearch6.5.4
- kibana版本:kibana6.5.4
配置各节点#
需要说明的是,查看集群还可以用到elasticsearch head
插件,但是该插件依赖nodejs
,我嫌麻烦就没装。采用kibana一样。而且,由于elasticsearch比较大,所以,根据系统环境适当的减少节点也是合理的,没必要非要搞4个。最好放到固态盘下演示效果最好!
为了便于管理,我在C盘的根目录下创建一个es_cluster
目录,然后将elasticsearch压缩包和kibana压缩包解压到该目录内,并且重命名为如下结构:
补充一点,这里仅是搭建集群,并没有安装ik分词插件,如果要安装的话,直接安装在各es目录内的plugins
目录下即可。具体安装方法参见ik分词器的安装
配置单播发现#
现在,我们为这个集群增加一些单播配置,打开各节点内的configelasticsearch.yml
文件。每个节点的配置如下(原配置文件都被注释了,可以理解为空,我写好各节点的配置,直接粘贴进去,没有动注释的,出现问题了好恢复):
- elasticsearch1节点,,集群名称是my_es1,集群端口是9300;节点名称是node1,监听本地9200端口,可以有权限成为主节点和读写磁盘(不写就是默认的)。
cluster.name: my_es1
node.name: node1
network.host: 127.0.0.1
http.port: 9200
transport.tcp.port: 9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
- elasticsearch2节点,集群名称是my_es1,集群端口是9302;节点名称是node2,监听本地9202端口,可以有权限成为主节点和读写磁盘。
cluster.name: my_es1
node.name: node2
network.host: 127.0.0.1
http.port: 9202
transport.tcp.port: 9302
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
- elasticsearch3节点,集群名称是my_es1,集群端口是9303;节点名称是node3,监听本地9203端口,可以有权限成为主节点和读写磁盘。
cluster.name: my_es1
node.name: node3
network.host: 127.0.0.1
http.port: 9203
transport.tcp.port: 9303
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
- elasticsearch4节点,集群名称是my_es1,集群端口是9304;节点名称是node4,监听本地9204端口,仅能读写磁盘而不能被选举为主节点。
cluster.name: my_es1
node.name: node4
network.host: 127.0.0.1
http.port: 9204
transport.tcp.port: 9304
node.master: false
node.data: true
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
由上例的配置可以看到,各节点有一个共同的名字my_es1,但由于是本地环境,所以各节点的名字不能一致,我们分别启动它们,它们通过单播列表相互介绍,发现彼此,然后组成一个my_es1集群。谁是老大则是要看谁先启动了!
kibana测试#
我们打开kibana,通过左侧菜单栏的Monitoring
来监控各集群的健康状况。
由于我先启动的是node2节点,所以node2就是主节点。
问题#
- 出现kibana连不上的问题,可能原因是我是测试,频繁改一些配置导致的,解决办法,就是删除各节点下的data目录。放心它会再创建回来的。
- 防止脑裂配置上,es实例就无法启动,查看日志发现是
[2019-03-21T09:33:30,142][INFO ][o.e.x.m.j.p.NativeController] [XkfReKb] Native controller process has stopped - no new native processes can be started
,我怀疑是由于是本地环境引起,等下次在不同的主机上测试再来解决这个问题。
discovery.zen.fd.ping_interval: 1
discovery.zen.fd.ping_timeout: 20
discovery.zen.fd.ping_retries: 3
discouery.zen.minimum_master_nodes: 3
欢迎斧正,that's all
三、elasticsearch集群那点事儿
前言#
接下来的演示,在本地需要一个新的集群,没有搭建好呢?来点击
空集群#
现在,万事俱备,只欠东风。我们继续来探讨集群的内部细节。
当我们打开一个单独的节点node1,此时它还没有数据和索引。那么这个集群就是个空集群。
是的,一个集群下辖一个主节点,空白白的........
集群健康#
我们通过kibana的监控来查看集群状态:
或者这么查询:
GET cluster/health # 在kibana的Dev Tools中查询
http://127.0.0.1:9200/_cluster/health?pretty # 浏览器中输入
返回结果如下:
{
"cluster_name" : "my_es1",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 3,
"active_shards" : 3,
"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
}
在返回的结果中,我们拿到了集群名称、状态信息、是否超时等信息。我们要对这个status保持关注。
status字段提供一个综合的指标来表示该集群的服务状况,它有三种不同的颜色代表不同的含义:
颜色 | 描述 |
---|---|
green | 所有主要分片和复制分片都可用 |
yellow | 所有主要分片可用,但不是所有复制分片都可用 |
red | 不是所有的主要分片都可用 |
我们将数据添加到elasticsearch中的索引中——一个存储关联数据的地方。实际上索引只是用来指向一个或者多个分片的逻辑命名空间。
一个分片是一个最小级别的工作单元。它只是保存了索引中所有数据的一部分。我们可以把分片想象成容器,文档就存储在分片中,然后分片被分配到你的集群节点上。当我们扩容或缩小集群时,elasticsearch会自动在节点间迁移分片,以保持集群的平衡。
分片又可以分为主分片(primary shard)和复制分片(replica shard)。我们索引的每个文档都是一个单独的主分片,主分片的数量决定了索引最多能存储多少数据。
而复制分片只是主分片的一个副本,它用来防止硬件故障导致的数据丢失,同时可以提供读请求。
当索引创建完成后,主分片的数量级就固定了,但是复制分片的数量可以随时调整。
添加索引#
了解了集群中的索引和分片。我们就来为空集群创建一个索引(在kibana中):
PUT /blogs
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
上例中,我们为blogs节点分配了3个分片(默认是5个)和1个复制分片(每个主分片默认都有一个复制分片)。
现在三个分片都被分配到节点1中了。我们来查看一下集群的健康状态:
{
"cluster_name" : "my_es1",
"status" : "yellow",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 6,
"active_shards" : 6,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 3,
"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" : 66.66666666666666
}
此时的集群健康状态是yellow,表示所有的主分片都正常,集群可以正常的处理请求。只是复制分片还没有全部可用,此时复制分片处于unassigned
状态,它们还没分配给节点。因为没有必要在同一个节点上保存相同的数据副本。只要这个节点挂了,那么数据副本也都丢了。
在kibana中看更直观一些:
现在集群工作正常,比如我们为blogs索引添加一条数据:
PUT /blogs/doc/1
{
"title": "es集群"
}
GET /blogs/_search
{
"query": {
"match_all": {}
}
}
添加和查询都没问题。现在一切看起来非常的完美。但虽然集群的功能完备,但是存在因硬件故障导致数据丢失的风险。
添加更多的节点#
我们为这个集群添加一个新的节点,来承担数据丢失的风险。启动我们的集群中的node2节点。启动后,我们就可以在kibana中来检测这两个节点。
当第二个节点加入到集群后,三个复制分片也已经被分配,这意味着,当在集群中有任意一个节点挂掉依然可以保证数据的完整性。
其实,当第二个节点再加入到集群中时,文档的索引将首先被存储在主分片中,然后并发复制到对应的复制节点上。这可以确保我们的数据在主节点和复制节点上都可以被检索。
再来看集群的健康状态:
{
"cluster_name" : "my_es1",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 2,
"number_of_data_nodes" : 2,
"active_primary_shards" : 6,
"active_shards" : 12,
"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
}
此时的集群状态是green,这就表明6个分片都可用了。
目前为止,我们的集群不仅是功能完备,而且是高可用的了。
继续扩展#
随着应用需求的增长,我们需要启动更多的节点,现在让我们启动node3和node4。
越来越多的节点意味着分片将获得更多的硬件资源,包括CPU、RAM、I/O。
我们可以使用命令来查看当前集群的信息:
GET _cluster/state/master_node,nodes?pretty # 返回所有节点
GET _cluster/state/master_node,node?pretty # 返回当前主节点的信息
GET _nodes # 返回所有节点列表
当老大(主节点)挂了怎么办#
有一天,我们的主节点挂掉了,比如小黑不小心踢掉了其中一个主机的电源插头,恰好,这个主机的es实例是主节点node1。相当于小黑亲手干掉了集群中的老大!此时会发生什么?
首先,我们由于是通过kibana来监控集群的,而kibana在启动的时候默认寻找的是9200端口,也就是老大node1,但这家伙刚被我们干掉,所以,kibana先挂掉了。现在我们先把kibana的配置文件把端口临时改为(提醒自己一会演示完别忘了改回来!)9202端口(你集群的其他的示例端口)。然后重启kibana服务。再访问监控:
可以看到,老大node1提示离线状态,而老大则换成了node4。这其中到底发生了什么呢?
我们杀掉了老大主节点。但是一个集群必须有一个主节点才能使其功能正常,所以,集群做的第一件事就是在小弟们(各节点)中选举出一个新的老大(主节点)。
当原来的老大被干掉后,它节点内的主分片也都没了,但还好,其他的节点还存着副本。所以新官上任三把火的头一把火就是把分布在别的节点上复制分片升级为主分片,这个过程是非常快的。当然,它身为新的主节点还要做些别的事情。它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,这意味着在流量增长的时候,该主节点不会成为集群的瓶颈。
但不幸的的是,原主节点在被小黑杀掉扔下悬崖后,幸运地挂在了树上而逃过一死(故障解决,重启node1),它就回来了。你就会在kibana中发现:
虽然胡汉三又回来了,但是只能做个小弟了。主节点依然被node4牢牢把握。
成为主节点的资格#
之前当主节点node1被干掉后,为什么node4被选举出来,这其中是否存在着py
交易暂且不提。但能说的是,集群下的各节点都有资格被选举为主节点。所以node4上位后,就在思考,要把这资格收回来,怎么做呢?其实在配置文件中可以体现:
node.master: false # 该节点是否可以被选举为主节点,默认为true
node.data: true # 该节点是否有存储权限,默认为true
我们通过node.master来设置哪个节点有资格称为主节点。
停用节点#
很多时候,我们需要维护的时候,就需要关闭某个节点,那怎么做呢?
PUT /_cluster/settings
{
"transient": {
"cluster.routing.allocation.exclude._ip": "192.168.1.1"
}
}
一旦执行上述命令,elasticsearch将该节点上的全部分片转移到其他的节点上。并且这个设置是暂时的,集群重启后就不再有效。
see also:[Elasticsearch权威指南(中文版)](https://es.xiaoleilu.com/020_Distributed_Cluster/05_Empty_cluster.html) 欢迎斧正,that's all