docker中部署ES集群的详细操作步骤(6个节点)

ElasticSearch集群

1.集群原理
ElasticSearch是天生支持集群的,他不需要依赖其他的服务发现和注册的组件,如zookeeper这些,因为他内置了一个名字叫ZenDiscovery的模块,是ElasticSearch自己实现的一套用于节点发现和选主等功能的组件,所以ElasticSearch做起集群来非常简单,不需要太多额外的配置和安装额外的第三方组件。

1、单节点
A.一个运行中的ElasticSearch实例称为一个节点,而集群是由一个或者多个拥有相同的cluster.name配置的节点组成,他们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。
B.当一个节点被选举成为主节点时,他将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加他也不会成为瓶颈。任何节点都可以成为主节点。我们的示例集群就只有一个节点,所以他同时也成为了主节点。
C.作为用户,我们可以将请求发送到集群中的任何节点,包括主节点。每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。无论我们将请求发送到哪个节点,他都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回给客户端。ElasticSearch对这一切的管理都是透明的。

2、集群健康
ElasticSearch的集群监控信息中包含了许多的统计数据,其中最为重要的一项就是集群健康,他在status字段中展示为green、yellow或者red。
GET /_cluster/health
status字段指示着当前集群在总体上是否工作正常。他的三种颜色含义如下:
green:所有的主分片和副本分片都正常运行。
yellow:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red:有主分片没能正常运行。

3、分片
A.一个分片是一个底层的工作单元,他仅保存了全部数据中的一部分。我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互。分片就认为是一个数据区
B.一个分片可以是主分片或者副本分片。索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
C.在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。
D.我们在包含一个空节点的集群内创建名为block的索引。索引在默认情况下会被分配5个主分片,但是为了演示目的,我们将分配3个主分片和一份副本(每个主分片拥有一个副本分片):
PUT /block{
“settings”:{
“number_of_shards”:3,
“number_of_replicas”:1
}
}
拥有一个索引的单节点集群
此时集群的健康状况为yellow,则表示全部主分片都正常运行(集群可以正常服务所有请求),但是副本分片没有全部处在正常状态。实际上,所有3个副本分片都是unassigned–他们都没有被分配到任何节点。在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。
当前我们的集群是正常运行的,但是在硬件故障时有丢失数据的风险。

4、新增节点
当你在同一台机器上启动了第二个节点时,只要它和第一个节点有同样的cluster.name配置,它就会自动发现集群并加入到其中。但是在不同机器上启动节点的时候,为了加入到同一集群,你需要配置一个可连接到的单播主机列表。而且最好使用单播代替组播。

拥有2个节点的集群-所有主分片和副本分片都已被分配
此时,cluster-health现在展示的状态为green,这表示所有6个分片(包括3个主分片和3个副本分片)都在正常运行。我们的集群现在不仅仅时正常运行的,并且还处于始终可用的状态。

5、水平扩容-启动第三个节点
拥有三个节点的集群-为了分散负载而对分片进行重新分配

node1和node2上各有一个分片被迁移到了新的node3节点,现在每个节点上都拥有2个分片,而不是之前的3个。这表示每个节点的硬件资源(CPU,RAM,I/O)将被更少的分片所共享,每个分片的性能将会得到提升。

在运行的集群上是可以动态调整副本分片数目的,我们可以按需伸缩集群。我们把副本数从默认的1增加到2
PUT /block/_settings{
“number_of_replicas”:2
}
block索引现在拥有9个分片:3个主分片和6个副本分片。这意味着我们可以将集群扩容到9个节点,每个节点上一个分片。相比原来3个节点时,集群搜索性能可以提升3倍。

6、应对故障
关闭一个节点后的集群
A.我们关闭的节点是一个主节点。而集群必须拥有一个主节点来保证正常工作,所以发生的第一个事情就是选举一个新的主节点:node2.
B.在我们关闭了node1的同时也失去了主分片1和2,并且在缺失主分片的时候索引也不能正常工作。如果此时来检查集群的状况,我们看到的状态将会为red:不是所有主分片都在正常工作。
C.幸运的是,在其他节点上存在着这两个主分片的完整副本,所以新的主节点立即将这些分片在node2和node3上对应的副本分片提升为主分片,此时集群的状态将会为yellow,这个提示主分片的过程是瞬间发生的,如果按下一个开关一般。
D.为什么我们集群状态时yellow而不是green呢?虽然我们拥有所有的三个主分片,但是同时设置了每个主分片需要对应2份副本分片,而此时只存在一份副本分片。所以集群不能以green的状态,不过我们不必过于担心,如果我们同样关闭了node2,我们的程序依然可以保持在不丢任何数据的情况下运行,因为node3为每一个分片都保留着一份副本。
E.如果我们重新启动node1,集群可以将缺失的副本分片再次进行分配。如果node1依然拥有着之前的分配,它将尝试去重用它们,同时仅从主分片复制发生了修改的数据文件。

7、问题与解决

1、主节点
主节点负责创建索引、删除索引、分配分片、追踪集群中的节点状态等工作。ElasticSearch中的主节点的工作量相对较轻,用户的请求可以发往集群中任何一个节点,由该节点负责分发和返回结果,而不需要经过主节点转发。而主节点是由候选主节点通过ZenDiscovery机制选举出来的,所以要想成为主节点,首先要先成为候选主节点。

2、候选主节点
在ElasticSearch集群初始化或者主节点宕机的情况下,由候选主节点中选举其中一个作为主节点。指定候选主节点的配置为:node.master:true。
当主节点负载压力过大,或者集群环境中的网络问题,导致其他节点与主节点通讯的时候,主节点没来及响应,这样的话,某些节点就认为主节点宕机,重新选择新的主节点,这样的话整个集群的工作就有问题了,比如我们集群中有10个节点,其中7个候选主节点,1个候选主节点成为了主节点,这种情况是正常的情况。但是如果现在出现了我们上面所说的主节点响应不及时,导致其他某些节点认为主节点宕机而重选主节点,那就有问题了,这剩下的6个候选主节点可能有3个候选主节点去重选主节点,最后集群中就出现了两个主节点的情况,这种情况官方成为“脑裂现象”。
集群中不同的节点对于master的选择出现了分歧,出现了多个master竞争,导致主分片和副本的识别也发生了分歧,把一些分歧中的分片标识为了坏片。

3、数据节点
数据节点负责数据的存储和相关具体操作,比如CRUD、搜索、聚合。所以,数据节点对机器配置要求比较高,首先需要有足够的磁盘空间来存储数据,其次数据操作对系统CPU、Memory和IO的性能消耗都很大。通常随着集群的扩大,需要增加更多的数据节点来提高可用性。指定数据节点的配置:node.data:true。
ElasticSearch是允许一个节点既做候选主节点也做数据节点的,但是数据节点的负载较重,所以需要考虑将二者分离开,设置专用的候选主节点和数据节点,避免因数据节点负责重导致主节点不响应。

4、客户端节点
客户端节点就是既不做候选主节点也不做数据节点的节点,只负责请求的分发、汇总等等,但是这样的工作,其实任何一个节点都可以完成,因为在ElasticSearch中一个集群内的节点都可以执行任何请求,其会负责将请求转发给对应的节点进行处理。所以单独增加这样的节点更多是为了负载均衡。指定该节点的配置为:
node.master:false
node.data:false

5、脑裂问题可能的成因

1.网络问题:集群间的网络延迟导致一些节点访问不到master,认为master挂掉了从而选举出新的master,并对master上的分片和副本标红,分配新的主分片。
2.节点负载:主节点的角色既为master又为data,访问量较大时可能会导致ES停止响应造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
3.内存回收:data节点上的ES进程占用的内存较大,引发JVM的大规模内存回收,造成ES进程失去响应。

脑裂问题解决方案:
角色分离:即master节点与data节点分离,限制角色;数据节点时需要承担存储和搜索的工作的,压力会很大。所以如果该节点同时作为候选主节点和数据节点,那么一旦选上它作为主节点了,这时主节点的工作压力将会非常大,出现脑裂现象的概率就增加了。

减少误判:配置主节点的响应时间,在默认情况下,主节点3秒没有响应,其他节点就认为主节点宕机了,那我们可以把该时间设置得长一点,该配置是:
discovery.zen.ping_timeout:5

选举触发:discovery.zen.minimum_master_nodes:1(默认是1),该属性定义的是为了形成一个集群,有主节点资格并互相连接的节点的最小数目。
一个有10节点的集群,且每个节点都有成为主节点的资格,
discovery.zen.minimum_master_nodes参数设置为6。
正常情况下,10个节点,互相连接,大于6,就可以形成一个集群。
若某个时刻,其中有3个节点断开连接。剩下7个节点,大于6,继续运行之前的集群。而断开的3个节点,小于6,不能形成一个集群。
该参数就是为了防止脑裂的产生
建议设置为(候选主节点数/2)+1。

8、集群结构
以3台物理机为例。在这3台物理机上,搭建了6个ES的节点,3个data节点,3个master节点(每台物理机分别起了一个data和一个master),3个master节点,目的是达到(n/2)+1等于2的要求,这样挂掉1台master后(不考虑data),n等于2,满足参数,其他两个节点都认为master挂掉之后开始重新选举,

master节点上
node.master = true
node.data = true
discovery.zen.minimum_master_nodes =2

docker中部署ES集群的详细操作步骤(6个节点)

2.集群搭建
所有之前先运行:sysctl -w vm.max_map_count=262144
如果只是测试,可以临时修改。永久修改使用下面的语句
#防止JVM报错
echo vm.max_map_count=262144>> /etc/sysctl.conf sysctl -p

{"type": "deprecation", "timestamp": "2022-01-26T07:58:54,866Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "es-master-3", "message": "[discovery.zen.ping.unicast.hosts] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." }
{"type": "deprecation", "timestamp": "2022-01-26T07:58:54,882Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "es-master-3", "message": "[discovery.zen.minimum_master_nodes] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." }
{"type": "server", "timestamp": "2022-01-26T07:59:05,217Z", "level": "INFO", "component": "o.e.x.s.a.s.FileRolesStore", "cluster.name": "my-es", "node.name": "es-master-3", "message": "parsed [0] roles from file [/usr/share/elasticsearch/config/roles.yml]" }
{"type": "server", "timestamp": "2022-01-26T07:59:08,561Z", "level": "INFO", "component": "o.e.x.m.p.l.CppLogMessageHandler", "cluster.name": "my-es", "node.name": "es-master-3", "message": "[controller/87] [Main.cc@110] controller (64 bit): Version 7.6.2 (Build e06ef9d86d5332) Copyright (c) 2020 Elasticsearch BV" }
{"type": "server", "timestamp": "2022-01-26T07:59:13,427Z", "level": "DEBUG", "component": "o.e.a.ActionModule", "cluster.name": "my-es", "node.name": "es-master-3", "message": "Using REST wrapper from plugin org.elasticsearch.xpack.security.Security" }
{"type": "server", "timestamp": "2022-01-26T07:59:15,164Z", "level": "INFO", "component": "o.e.d.DiscoveryModule", "cluster.name": "my-es", "node.name": "es-master-3", "message": "using discovery type [zen] and seed hosts providers [settings]" }
{"type": "server", "timestamp": "2022-01-26T07:59:22,616Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "initialized" }
{"type": "server", "timestamp": "2022-01-26T07:59:22,617Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "starting ..." }
{"type": "server", "timestamp": "2022-01-26T07:59:24,821Z", "level": "INFO", "component": "o.e.t.TransportService", "cluster.name": "my-es", "node.name": "es-master-3", "message": "publish_address {172.18.12.23:9303}, bound_addresses {[::]:9303}" }
{"type": "server", "timestamp": "2022-01-26T07:59:26,236Z", "level": "INFO", "component": "o.e.b.BootstrapChecks", "cluster.name": "my-es", "node.name": "es-master-3", "message": "bound or publishing to a non-loopback address, enforcing bootstrap checks" }
ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
ERROR: Elasticsearch did not exit normally - check the logs at /usr/share/elasticsearch/logs/my-es.log
{"type": "server", "timestamp": "2022-01-26T07:59:26,277Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "stopping ..." }
{"type": "server", "timestamp": "2022-01-26T07:59:26,327Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "stopped" }
{"type": "server", "timestamp": "2022-01-26T07:59:26,328Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "closing ..." }
{"type": "server", "timestamp": "2022-01-26T07:59:26,435Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "es-master-3", "message": "closed" }
{"type": "server", "timestamp": "2022-01-26T07:59:26,438Z", "level": "INFO", "component": "o.e.x.m.p.NativeController", "cluster.name": "my-es", "node.name": "es-master-3", "message": "Native controller process has stopped - no new native processes can be started" }

1.准备docker网络
docker创建容器时默认采用bridge网络,自行分配ip,不允许自己指定。
在实际部署中,我们需要指定容器ip,不允许其自行分配ip,尤其时搭建集群时,固定ip是必须的。
我们可以创建自己的bridge网络:mynet,创建容器的时候指定网络为mynet并指定ip即可。
查看网络模式 docker network ls;

创建一个新的bridge网络
docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 mynet
查看网络信息

docker network inspect mynet

以后使用 --network=mynet --ip 172.18.12.x 指定ip

2.创建3个master

for port in $(seq 1 3); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat <<EOF> /mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es
node.name: es-master-${port}
node.master: true 
node.data: false 
network.host: 0.0.0.0
http.host: 0.0.0.0 
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s
discovery.zen.ping.unicast.hosts: ["172.18.12.21:9301","172.18.12.22:9302","172.18.12.23:9303"]
discovery.zen.minimum_master_nodes: 2
cluster.initial_master_nodes: ["172.18.12.21"]
EOF
docker run --name elasticsearch-node-${port} \
 -p 920${port}:920${port} -p 930${port}:930${port} \
 --network=mynet --ip 172.18.12.2${port} \
 -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \
 -v /mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
 -v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \
 --privileged=true \
 -d elasticsearch:7.6.2
done

cluster.name: my-es #集群的名称,同一个集群该值必须设置相同
node.name: es-master-${port} #该节点的名字
node.master: true #该节点是否有机会成为master节点
node.data: false #该节点可以存储数据
http.host: 0.0.0.0 #所有http都可以访问
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时ping连接的超时时间
discovery.send_hosts: [“172.18.12.21:9301”,“172.18.12.22:9302”,“172.18.12.23:9303”] #设置集群中的master节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置
cluster.initial_master_nodes: [“172.18.12.21”]#新集群初始时的候选主节点,es7的新增配置

注意:
1.上面的创建shell脚本一定要注意后面是否带空格,如果带空格运行shell脚本会失败,比如EOF后面,当复制粘贴到linux上面的时候,后面不能带空格,不然会创建节点失败。
2.需要注意在elasticsearch.yml配置文件中,每个属性配置的时候前面要多一个空格,比如上面的cluster.name:后面就是加了一个空格,然后再是集群的名称。
3.对于centos7以上的版本,需要在运行docker的时候加上–privileged=true,不然运行shell脚本有可能会报错。

3.创建3个Data node节点

for port in $(seq 4 6); \
do \
mkdir -p /mydata/elasticsearch/node-${port}/config
mkdir -p /mydata/elasticsearch/node-${port}/data
chmod -R 777 /mydata/elasticsearch/node-${port}
cat <<EOF> /mydata/elasticsearch/node-${port}/config/elasticsearch.yml
cluster.name: my-es
node.name: es-node-${port}
node.master: false 
node.data: true 
network.host: 0.0.0.0
http.host: 0.0.0.0 
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s
discovery.zen.ping.unicast.hosts: ["172.18.12.21:9301","172.18.12.22:9302","172.18.12.23:9303"]
discovery.zen.minimum_master_nodes: 2
cluster.initial_master_nodes: ["172.18.12.21"]
EOF
docker run --name elasticsearch-node-${port} \
 -p 920${port}:920${port} -p 930${port}:930${port} \
 --network=mynet --ip 172.18.12.2${port} \
 -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \
 -v /mydata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
 -v /mydata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \
 --privileged=true \
 -d elasticsearch:7.6.2
done

运行之后,通过docker ps检查每个节点是否正常启动,节点信息如下:

[root@centos3 etc]# docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                                                                NAMES
780bc49a4a44        elasticsearch:7.6.2   "/usr/local/bin/do..."   7 minutes ago       Up 7 minutes        9200/tcp, 0.0.0.0:9206->9206/tcp, 9300/tcp, 0.0.0.0:9306->9306/tcp   elasticsearch-node-6
d05b846789be        elasticsearch:7.6.2   "/usr/local/bin/do..."   7 minutes ago       Up 7 minutes        9200/tcp, 0.0.0.0:9205->9205/tcp, 9300/tcp, 0.0.0.0:9305->9305/tcp   elasticsearch-node-5
852b40cd39a8        elasticsearch:7.6.2   "/usr/local/bin/do..."   8 minutes ago       Up 7 minutes        9200/tcp, 0.0.0.0:9204->9204/tcp, 9300/tcp, 0.0.0.0:9304->9304/tcp   elasticsearch-node-4
de72572353ee        elasticsearch:7.6.2   "/usr/local/bin/do..."   14 minutes ago      Up 14 minutes       9200/tcp, 0.0.0.0:9203->9203/tcp, 9300/tcp, 0.0.0.0:9303->9303/tcp   elasticsearch-node-3
61271dd17d42        elasticsearch:7.6.2   "/usr/local/bin/do..."   14 minutes ago      Up 14 minutes       9200/tcp, 0.0.0.0:9202->9202/tcp, 9300/tcp, 0.0.0.0:9302->9302/tcp   elasticsearch-node-2
eb898f25b615        elasticsearch:7.6.2   "/usr/local/bin/do..."   14 minutes ago      Up 14 minutes       9200/tcp, 0.0.0.0:9201->9201/tcp, 9300/tcp, 0.0.0.0:9301->9301/tcp   elasticsearch-node-1
[root@centos3 etc]# 

在网页中输入http://192.168.0.106:9206/_cat/nodes,查看集群状态,截图如下:
docker中部署ES集群的详细操作步骤(6个节点)如果我们手动的停掉一个节点,则该节点将自动停止,会从其他的备份主节点中选举一个作为主节点信息,例如:如果将es-master-1停掉,则会从es-master-2和es-master-3中选举一个作为主节点,当es-master-1恢复的时候,es-master-1就作为备份主节点参与到集群中来。

调试es集群的命令:

批量停止es集群容器:
docker stop $(docker ps -a | grep elasticsearch-node-* | awk ‘{ print $1}’)

批量删除es集群容器:
docker rm $(docker ps -a | grep elasticsearch-node-* | awk ‘{ print $1}’)

上一篇:SkyWalking简要介绍


下一篇:使用 ES Module 的正确姿势