安装Docker

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum makecache fast
sudo yum -y install docker-ce
sudo service docker start
docker version

创建ES配置与目录

前置准备:CentOS7调整系统参数:

在默认CentOS7系统安装ES7 后执行会报:

max_map_count: 定义了一个进程能拥有的最多的内存区域,默认为 65536 内存异常处理

vim /etc/sysctl.conf

在最后面追加以下内容

vm.max_map_count=655360

使配置生效

sysctl -p

在每一台虚拟机均执行下面的命令,因为数据文件不允许存储在Docker容器内,因此我们要在宿主机创建这些目录挂载到Docker容器。

# 创建主节点数据存放目录
mkdir -p /root/elasticsearch/data
# 创建主节点配置存放目录
mkdir -p /root/elasticsearch/config
# 创建主节点日志存放目录
mkdir -p /root/elasticsearch/logs
# 创建主节点插件存放目录
mkdir -p /root/elasticsearch/plugins

Node1节点配置文件

cd /root/elasticsearch/config
vi elasticsearch.yml

增加如下内容:

# 设置集群名称,集群内所有节点的名称必须一致。
cluster.name: es-cluster

# 设置节点名称,集群内节点名称必须唯一。
node.name: node1

# 表示该节点会不会作为主节点,true 表示会,false 表示不会
node.master: true

# 当前节点是否用于存储数据,是:true、否:false
node.data: true

# 监听地址,用于访问该 Elasticsearch 实例。
network.host: 0.0.0.0

# Elasticsearch 对外提供的 HTTP 端口,默认为 9200。
http.port: 9200

# TCP 的默认监听端口,默认为 9300。
transport.tcp.port: 9300

# 设置这个参数来保证集群中的节点可以知道其它 N 个有 master 资格的节点。默认为 1,对于大的集群来说,可以设置大一点的值(2-4)。
discovery.zen.minimum_master_nodes: 3

# ES7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点。
discovery.seed_hosts: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# ES7.x 之后新增的配置,探测节点是否可用的超时时间。
discovery.zen.fd.ping_timeout: 1m

# ES7.x 之后新增的配置,探测节点是否可用的重试次数。
discovery.zen.fd.ping_retries: 5

# ES7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master。
cluster.initial_master_nodes: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# 是否支持跨域,是:true,在使用 head 插件时需要此配置。
http.cors.enabled: true

# 跨域请求时允许的来源,"*" 表示支持所有域名。
http.cors.allow-origin: "*"

# 索引数据存放的位置
#path.data: /usr/share/elasticsearch/data

# 日志文件存放的位置
#path.logs: /usr/share/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false
#bootstrap.memory_lock: true

Node2节点与Node1配置文件唯一的区别是:更改node.name=node2

# 设置集群名称,集群内所有节点的名称必须一致。
cluster.name: es-cluster

# 设置节点名称,集群内节点名称必须唯一。
node.name: node2

# 表示该节点会不会作为主节点,true 表示会,false 表示不会
node.master: true

# 当前节点是否用于存储数据,是:true、否:false
node.data: true

# 监听地址,用于访问该 Elasticsearch 实例。
network.host: 0.0.0.0

# Elasticsearch 对外提供的 HTTP 端口,默认为 9200。
http.port: 9200

# TCP 的默认监听端口,默认为 9300。
transport.tcp.port: 9300

# 设置这个参数来保证集群中的节点可以知道其它 N 个有 master 资格的节点。默认为 1,对于大的集群来说,可以设置大一点的值(2-4)。
discovery.zen.minimum_master_nodes: 3

# ES7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点。
discovery.seed_hosts: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# ES7.x 之后新增的配置,探测节点是否可用的超时时间。
discovery.zen.fd.ping_timeout: 1m

# ES7.x 之后新增的配置,探测节点是否可用的重试次数。
discovery.zen.fd.ping_retries: 5

# ES7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master。
cluster.initial_master_nodes: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# 是否支持跨域,是:true,在使用 head 插件时需要此配置。
http.cors.enabled: true

# 跨域请求时允许的来源,"*" 表示支持所有域名。
http.cors.allow-origin: "*"

# 索引数据存放的位置
#path.data: /usr/share/elasticsearch/data

# 日志文件存放的位置
#path.logs: /usr/share/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false
#bootstrap.memory_lock: true

Node3节点与Node1配置文件唯一的区别是:更改node.name=node3

# 设置集群名称,集群内所有节点的名称必须一致。
cluster.name: es-cluster

# 设置节点名称,集群内节点名称必须唯一。
node.name: node3

# 表示该节点会不会作为主节点,true 表示会,false 表示不会
node.master: true

# 当前节点是否用于存储数据,是:true、否:false
node.data: true

# 监听地址,用于访问该 Elasticsearch 实例。
network.host: 0.0.0.0

# Elasticsearch 对外提供的 HTTP 端口,默认为 9200。
http.port: 9200

# TCP 的默认监听端口,默认为 9300。
transport.tcp.port: 9300

# 设置这个参数来保证集群中的节点可以知道其它 N 个有 master 资格的节点。默认为 1,对于大的集群来说,可以设置大一点的值(2-4)。
discovery.zen.minimum_master_nodes: 3

# ES7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点。
discovery.seed_hosts: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# ES7.x 之后新增的配置,探测节点是否可用的超时时间。
discovery.zen.fd.ping_timeout: 1m

# ES7.x 之后新增的配置,探测节点是否可用的重试次数。
discovery.zen.fd.ping_retries: 5

# ES7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master。
cluster.initial_master_nodes: ["192.168.31.250:9300", "192.168.31.251:9300","192.168.31.252:9300"]

# 是否支持跨域,是:true,在使用 head 插件时需要此配置。
http.cors.enabled: true

# 跨域请求时允许的来源,"*" 表示支持所有域名。
http.cors.allow-origin: "*"

# 索引数据存放的位置
#path.data: /usr/share/elasticsearch/data

# 日志文件存放的位置
#path.logs: /usr/share/elasticsearch/logs

# 需求锁住物理内存,是:true、否:false
#bootstrap.memory_lock: true

启动容器

在3个虚拟机分别执行下面的docker run命令创建容器,每一个启动项之间只有--name容器名不同,其他完全相同

node1

docker run -d --network=host --privileged=true \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-e TAKE_FILE_OWNERSHIP=true --name es-node1 \
-v /root/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /root/elasticsearch/data:/usr/share/elasticsearch/data \
-v /root/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
elasticsearch:7.16.2
  • docker run:在 Docker 中启动一个容器。
  • -d:表示容器运行在后台。
  • --network=host:表示使用主机网络模式,容器和宿主机共享网络。
  • --privileged=true:表示给容器授权,以便于容器内的 Elasticsearch 进程可以锁定内存。
  • -e ES_JAVA_OPTS="-Xms512m -Xmx512m":设置 Elasticsearch 进程的堆内存大小。
  • -e TAKE_FILE_OWNERSHIP=true:将从宿主机挂载的目录的所有权交给 Elasticsearch 用户。
  • --name es-node1:指定容器名称为 es-node1。
  • -v /root/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:将宿主机上的 Elasticsearch 配置文件挂载到容器内的配置文件目录。
  • -v /root/elasticsearch/data:/usr/share/elasticsearch/data:将宿主机上的 Elasticsearch 数据目录挂载到容器内的数据目录。
  • -v /root/elasticsearch/logs:/usr/share/elasticsearch/logs:将宿主机上的 Elasticsearch 日志目录挂载到容器内的日志目录。
  • -v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins:将宿主机上的 Elasticsearch 插件目录挂载到容器内的插件目录。
  • elasticsearch:7.16.2:使用 Elasticsearch 7.16.2 版本的 Docker 镜像启动容器。

node2

docker run -d --network=host --privileged=true \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-e TAKE_FILE_OWNERSHIP=true --name es-node2 \
-v /root/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /root/elasticsearch/data:/usr/share/elasticsearch/data \
-v /root/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
elasticsearch:7.16.2

node3

docker run -d --network=host --privileged=true \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-e TAKE_FILE_OWNERSHIP=true --name es-node3 \
-v /root/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /root/elasticsearch/data:/usr/share/elasticsearch/data \
-v /root/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
elasticsearch:7.16.2

验证集群

TIPS:因为前面配置最少拥有3个候选Master才能选主,因此必须3个节点都启动后才能看

到效果。你也可以改为1。

在任意一个虚拟机内访问:

curl http://192.168.31.250:9200/_cat/nodes?pretty

查看集群状态,出现下面提示代表集群启动成功, 其中 * 代表是Master

192.168.31.252 27 73  1 0.03 0.03 0.06 cdfhilmrstw * node3
192.168.31.251 26 76 17 0.56 0.29 0.16 cdfhilmrstw - node2
192.168.31.250 51 75  1 0.00 0.01 0.05 cdfhilmrstw - node1

部署Kibana

# 创建主节点数据存放目录
mkdir -p /root/kibana/config
vi /root/kibana/config/kibana.yml
# ** THIS IS AN AUTO-GENERATED FILE **

# Default Kibana configuration for docker target
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://192.168.31.250:9200", "http://192.168.31.251:9200", "http://192.168.31.252:9200" ]
monitoring.ui.container.elasticsearch.enabled: true

使用下面命令启动kibana,并挂在kibana.yml配置文件

docker run --network=host -v /root/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml --name kibana -d kibana:7.16.2

启动后点击Stack Monitoring进入Kibana集群监控,过程中选择传统模式

进入后选择Or,set up with elg monitoring,使用es自带的监控

在这个页面,就可以查看集群的信息:

集群的分片

分片(shard):因为ES是个分布式的搜索引擎,所以索引通常都会分解成不同部分,而这些分布在不同节点的数据就是分片。ES自动管理和组织分片,并在必要的时候对分片数据进行再平衡分配,所以用户基本上不用担心分片的处理细节。

副本(replica):ES默认为一个索引创建1个主分片,并分别为其创建一个副本分片,也就是说每个索引都有1个主分片成本,而每个主分片都相应的有一个copy,当主分片所在节点宕机后,副分片就会升级为主分片。

Elastic search7.x之后,如果不指定索引分片,默认会创建1个主分片和1个副分片。

准备创建Job索引

由于搭建了Kibana,就没有必要使用postman了,可以直接通过Kibana的Dev Tools工具进行操作。

PUT /job
{
  "settings": {
    "index": {
      // 主分片数和副本分片数,设置为三个主分片,一个副本分片
      "number_of_shards": "3",
      "number_of_replicas": "1"
    }
  },
  "mappings": {
    "properties": {
      "jid": {
        "type": "long"
      },
      "title": {
        "type": "text"
      },
      "salary": {
        "type": "integer_range"
      },
      "city": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}

索引分片的故障转移

  • 当主分片节点挂了以后,集群其他节点上的副本分片会升级为主分片,同时在剩余的有效节点尽量保证主分片与副本是1:1或者1:N的
  • 分片分配到哪个节点是由ES自动管理的,如果某个节点挂了,那分片又会重新分配到别的节点上。
  • 在单机中,节点没有副分片,因为只有一个节点没必要生成副本分片,一个节点挂点,副分片也会挂掉,完全是单故障,没有存在的意义。
  • 在集群中,同个分片它的主分片不会和它的副分片在同一个节点上,因为主分片和副分片在同个节点,节点挂了,副分片和主分机一样是挂了。
  • 可以手动移动分片,比如把某个分片移动从节点1移动到节点2
  • 创建索引时指定的主分片数以后是无法修改的,开始时做好规划。所以主分片数的数量要根据项目决定,如果真的要增加主分片只能重建索引了,副分片数以后是可以修改的。

进入Kibana,选择Nodes

选择indices后再选中job索引

从图中可以看出,(蓝色)primary为主分片,(绿色)Replica为副分片,目前有三个主分片,每个主分片有一个副分片,并且每个主分片和自己的副分片都是分别位于不同的节点。

验证分片故障转移

进入node1服务器,停掉node1节点,观察node2节点的0号副分片是否升级为了主分片

docker stop es-node1

查看node2中的0号节点升级为了主分片

但是此时流程还没结束,等待一会再刷新,发现node2节点多了一个2号副分片,node3节点多了一个0号副分片。那是因为node1节点宕机后,2号节点的副分片也没有了,但是我们设置的是每个主分片必须有一个副分片,并且主分片不能与副分片在同一台服务器中,所以2号节点的副本分片就落在了node2节点中,响应的,node1节点宕机,0号主分片也没有了,那么node2节点的0号副分片升级为主分片,但是此时0号分片没有副节点,所以0号节点的副分片就只能落在node3节点上了。

假如node1节点恢复了

进入node1服务器,重启es-node1容器

docker start es-node1

重启后查看分片的状态,发现node1中有两个主分片,node3中有两个副分片,此时在处理查询请求时node3节点是不会被使用的,如果node1节点的服务器性能较差,也会影响我们整体查询的性能,这时候就需要使用分片转移

将node1节点的2号主分片转移到node3节点(无法转移1号主分片,因为1号副本分片在node3节点)

POST /_cluster/reroute
{
  "commands": [
    {
      "move": {
        "index": "job",
        "shard": 2,
        "from_node": "node1",
        "to_node": "node3"
      }
    }
  ]
}

2号主分片已经转移到了node3节点,同时Elasticsearch 集群会自动触发分片的重新分配过程,将0号副分片转移到了node1节点,以保证集群中所有节点上的分片数量和负载的平衡

重新设置副本分片

es中,主分片一旦被确认了,就无法被修改了,但是副分片还可以进行修改。如果我们要进一步提高数据的可靠性,可以使用_settings重新设置副本分片数

// 设置job索引每个主分片的副本分片数量为 2
PUT /job/_settings
{
  "number_of_replicas": 2
}

3个主分片+每个主分片3x2个副分片,就是9个分片

ES分布式底层原理

文档写入过程

  1. 客户端发送任何一个请求到任意一个节点,这个节点就成为协调节点(coordinate node)
  2. 协调节点对document(可以手动设置doc id,也可以由系统分配)进行哈希路由,将请求转发给对应的node。
文档编号HASH值(HASH值 % 主分片数量)
80088172010
90089321741
93389368912

主分片确定后,

  1. node上的primary shard处理请求,然后将数据同步到replica node
  2. 协调节点如果发现primary shard所在的node和所有的replica shard所对应的node都搞定之后,就会将请求返回给客户端

文档提取过程

  1. 客户端发送任何一个请求到任意一个node,这个节点就成为协调节点
  2. 协调节点对文档id进行哈希路由,此时会使用round-robin随机轮询算法,在主分片以及所有的副本分片中随机选择一个,让读请求负载均衡
  3. 数据提取完毕,返回document给协调节点
  4. 协调节点再将数据返回给客户端

文档搜索过程

  1. 客户端发送一个请求给协调节点(coordinate node)
  2. 协调节点将搜索的请求转发给所有shard对应的primary shard或replica shard
  3. query phase:每一个shard将自己搜索的结果(其实也就是一些唯一标识),返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最后的结果
  4. fetch phase :接着由协调节点,根据唯一标识去各个节点进行拉取数据,最总返回给客户端
最后修改:2023 年 06 月 12 日
如果觉得我的文章对你有用,请随意赞赏