【elasticsearch】docker下elasticsearch集群和分片

2022-10-25 15:40:01 浏览数 (1)

1、集群安装

cd /mydata/ 创建目录和文件

  • /mydata/elasticsearch/conf/elasticsearch.yml
  • /mydata/elasticsearch2/conf/elasticsearch.yml
  • /mydata/elasticsearch3/conf/elasticsearch.yml

es1配置文件

代码语言:javascript复制
# 开启跨域,为了让es-head可以访问
http.cors.enabled: true
http.cors.allow-origin: "*"

# 集群的名称
cluster.name: elasticsearch
# 节点的名称
node.name: es1
# 指定该节点是否有资格被选举成为master节点,默认是true,es是默认集群中的第一台机器为master,如果这台机挂了就会重新选举master
node.master: true
# 允许该节点存储数据(默认开启)
node.data: true
# 允许任何ip访问
network.host: 0.0.0.0
# 通过这个ip列表进行节点发现,我这里配置的是各个容器的ip
discovery.zen.ping.unicast.hosts: ["192.168.159.128:9300","192.168.159.128:9301","192.168.159.128:9302"]
#如果没有这种设置,遭受网络故障的集群就有可能将集群分成两个独立的集群 – 导致脑裂 - 这将导致数据丢失
discovery.zen.minimum_master_nodes: 2

es2-es3配置文件 -----------------------es2-----------------------

代码语言:javascript复制
http.cors.enabled: true
http.cors.allow-origin: "*"
cluster.name: elasticsearch
node.name: es2

network.host: 0.0.0.0
node.master: true
node.data: true

discovery.zen.ping.unicast.hosts: ["192.168.159.128:9300","192.168.159.128:9301","192.168.159.128:9302"]
discovery.zen.minimum_master_nodes: 2

-----------------------es3-----------------------

代码语言:javascript复制
http.cors.enabled: true
http.cors.allow-origin: "*"
cluster.name: elasticsearch
node.name: es3

network.host: 0.0.0.0
node.master: true
node.data: true

discovery.zen.ping.unicast.hosts: ["192.168.159.128:9300","192.168.159.128:9301","192.168.159.128:9302"]
discovery.zen.minimum_master_nodes: 2

集群化实例启动 实例一:

代码语言:javascript复制
docker run --name es1 -p 9200:9200 -p 9300:9300 
-e ES_JAVA_OPTS="-Xms256m -Xmx256m" 
-v /mydata/elasticsearch/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml 
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data -d elasticsearch:5.6.11

实例二:

代码语言:javascript复制
docker run --name es2 -p 9201:9200 -p 9301:9300 
-e ES_JAVA_OPTS="-Xms256m -Xmx256m" 
-v /mydata/elasticsearch2/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml 
-v /mydata/elasticsearch2/data:/usr/share/elasticsearch/data -d elasticsearch:5.6.11

实例三:

代码语言:javascript复制
docker run --name es3 -p 9202:9200 -p 9302:9300 
-e ES_JAVA_OPTS="-Xms256m -Xmx256m" 
-v /mydata/elasticsearch3/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml 
-v /mydata/elasticsearch3/data:/usr/share/elasticsearch/data -d elasticsearch:5.6.11

需要修改linux的进程数限制

代码语言:javascript复制
vi /etc/sysctl.conf
vm.max_map_count=655360
sysctl -p

代码集群测试: application.properties

代码语言:javascript复制
spring.elasticsearch.jest.uris[0]=http://192.168.217.130:9200
spring.elasticsearch.jest.uris[1]=http://192.168.217.130:9202
spring.elasticsearch.jest.uris[2]=http://192.168.217.130:9201

test类:

代码语言:javascript复制
import io.searchbox.client.JestClient;
import io.searchbox.core.DocumentResult;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.junit4.SpringRunner;

import javax.xml.ws.soap.Addressing;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@RunWith(SpringRunner.class)
@SpringBootTest
public class EsDemoApplicationTests {

    @Autowired
    JestClient jestClient;

    @Test
    public void contextLoads() {
        System.out.println(jestClient);
    }

    @Test
    public void index() throws IOException {

        //Update  Delete Search Index
        //1、Action action = new Crud.Builder(携带的数据).其他信息.build();
        //2、DocumentResult execute = jestClient.execute(action);
        //3、获取结果DocumentResult数据即可

        User user = new User();
        user.setEmail("122333");user.setUserName("dsadasda");


        Index index = new Index.Builder(user)
                .index("user")
                .type("info")
                .build();

        DocumentResult execute = jestClient.execute(index);

        System.out.println("执行?" execute.getId() "==>" execute.isSucceeded());
    }


    @Test
    public void query() throws IOException {

        //{"query":"{"match_all":"{}"}"}
        String queryJson ="";


        Search search = new Search.Builder(queryJson).addIndex("user").build();

        SearchResult execute =
                jestClient.execute(search);

        System.out.println("总记录" execute.getTotal() "==>最大得分:" execute.getMaxScore());
        System.out.println("查到的数据");
        List<SearchResult.Hit<User, Void>> hits = execute.getHits(User.class);
        hits.forEach((hit)->{
                 User u = hit.source;
            System.out.println(u.getEmail() "==>" u.getUserName());
        });

    }




}

class User{
    private String userName;
    private String email;

    public void setEmail(String email) {
        this.email = email;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public String getUserName() {
        return userName;
    }
}

pom文件

代码语言:javascript复制
<dependencies>
        <!--<dependency>-->
            <!--<groupId>org.springframework.boot</groupId>-->
            <!--<artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
        <!--</dependency>-->

        <!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
        <dependency>
            <groupId>io.searchbox</groupId>
            <artifactId>jest</artifactId>
            <version>5.3.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>5.6.11</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2、集群、分片原理

集群的目的是为了,高可用。

1)、单节点

一个运行中的 Elasticsearch 实例称为一个 节点,而集群是由一个或者多个拥有相同 cluster.name 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。

当一个节点被选举成为 主 节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。 任何节点都可以成为主节点。我们的示例集群就只有一个节点,所以它同时也成为了主节点。

作为用户,我们可以将请求发送到 集群中的任何节点 ,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。 无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。 Elasticsearch 对这一切的管理都是透明的。

2)、集群健康
代码语言:javascript复制
GET /_cluster/health

status 字段指示着当前集群在总体上是否工作正常。它的三种颜色含义如下:

green 所有的主分片和副本分片都正常运行。 yellow 所有的主分片都正常运行,但不是所有的副本分片都正常运行。 red 有主分片没能正常运行。

如下图:拥有一个索引的单节点集群

3)、添加故障转移

当集群中只有一个节点在运行时,意味着会有一个单点故障问题——没有冗余。 幸运的是,我们只需再启动一个节点即可防止数据丢失。

启动第二个节点

为了测试第二个节点启动后的情况,你可以在同一个目录内,完全依照启动第一个节点的方式来启动一个新节点(参考安装并运行 Elasticsearch)。多个节点可以共享同一个目录。

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

当第二个节点加入到集群后,3个 副本分片 将会分配到这个节点上——每个主分片对应一个副本分片。 这意味着当集群内任何一个节点出现问题时,我们的数据都完好无损。

所有新近被索引的文档都将会保存在主分片上,然后被并行的复制到对应的副本分片上。这就保证了我们既可以从主分片又可以从副本分片上获得文档。

cluster-health 现在展示的状态为 green ,这表示所有6个分片(包括3个主分片和3个副本分片)都在正常运行。

4)、水平扩容

读操作——搜索和返回数据——可以同时被主分片 或 副本分片所处理,所以当你拥有越多的副本分片时,也将拥有越高的吞吐量。 在运行中的集群上是可以动态调整副本分片数目的 ,我们可以按需伸缩集群。让我们把副本数从默认的 1 增加到 2 :

代码语言:javascript复制
PUT /blogs/_settings
{
   "number_of_replicas" : 2
}

5)、应对故障

我们关闭的节点是一个主节点。而集群必须拥有一个主节点来保证正常工作,所以发生的第一件事情就是选举一个新的主节点: Node 2 。

在我们关闭 Node 1 的同时也失去了主分片 1 和 2 ,并且在缺失主分片的时候索引也不能正常工作。 如果此时来检查集群的状况,我们看到的状态将会为 red :不是所有主分片都在正常工作。

幸运的是,在其它节点上存在着这两个主分片的完整副本, 所以新的主节点立即将这些分片在 Node 2 和 Node 3 上对应的副本分片提升为主分片, 此时集群的状态将会为 yellow(不是green是因为我们之前设置主分片存在两个副本,而现在只剩一个了) 。 这个提升主分片的过程是瞬间发生的,如同按下一个开关一般。

3、科普:脑裂问题

CAP:分区容器

  • 脑裂问题,就是同一个集群中的不同节点,对于集群的状态,有了不一样的理解。
  • 由于并发访问量的提高,导致了我们两个节点的集群(分片数默认为5,副本为1,没有固定的master,都是集群中的节点又做data又做master)状态变成了red,出现了大量的坏片,并且坏掉的都是主分片及其副本。分析发现,是ES集群出现了脑裂问题(俗称精神分裂),即集群中不同的节点对于master的选择出现了分歧,出现了多个master竞争,导致主分片和副本的识别也发生了分歧,对一些分歧中的分片标识为了坏片。
  • “脑裂”问题可能的成因
  1. 网络问题:集群间的网络延迟导致一些节点访问不到master,认为master挂掉了从而选举出新的master,并对master上的分片和副本标红,分配新的主分片
  2. 节点负载:主节点的角色既为master又为data,访问量较大时可能会导致ES停止响应造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
  3. 内存回收:data节点上的ES进程占用的内存较大,引发JVM的大规模内存回收,造成ES进程失去响应。

主不能工作了就得选。

  1. 脑裂问题解决方案:
  2. 减少误判:discovery.zen.ping_timeout节点状态的响应时间,默认为3s,可以适当调大,如果master在该响应时间的范围内没有做出响应应答,判断该节点已经挂掉了。调大参数(如6s,discovery.zen.ping_timeout:6),可适当减少误判。
  3. 选举触发 discovery.zen.minimum_master_nodes:1。 discovery.zen.minimum_master_nodes(默认是1):这个参数控制的是,一个节点需要看到的具有master节点资格的最小数量,然后才能在集群中做操作。官方的推荐值是(N/2) 1,其中N是具有master资格的节点的数量(我们的情况是3,因此这个参数设置为2,但对于只有2个节点的情况,设置为2就有些问题了,一个节点DOWN掉后,你肯定连不上2台服务器了,这点需要注意)。

增大该参数,当该值为2时,我们可以设置master的数量为3,这样,挂掉一台,其他两台都认为主节点挂掉了,才进行主节点选举。

  1. 角色分离:即master节点与data节点分离,限制角色

主节点配置为: node.master: true ##作为master节点 node.data: false ##不作为存储数据节点

从节点配置为: node.master: false node.data: true

  1. 实际解决办法

最终考虑到资源有限,解决方案如下:

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

master节点上

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

方案分析

  1. 角色分离后,当集群中某一台节点的master进程意外挂掉了,或者因负载过高停止响应,终止掉的master进程很大程度上不会影响到同一台机器上的data进程,即减小了数据丢失的可能性。
  2. discovery.zen.minimum_master_nodes设置成了2(3/2 1)当集群中两台机器都挂了或者并没有挂掉而是处于高负载的假死状态时,仅剩一台备选master节点,小于2无法触发选举行为,集群无法使用,不会造成分片混乱的情况。 而图一,两台节点假死,仅剩一台节点,选举自己为master,当真正的master苏醒后,出现了多个master,并且造成查询不同机器,查到了结果不同的情况。

以上的解决方法只能是减缓这种现象的发生,并没有从根本上杜绝,但是毕竟是有帮助的

https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html

0 人点赞