title: elasticsearch介绍及集群部署 #标题tags: elasticsearch #标签
date: 2020-05-24
categories: elastic stack # 分类
历时12天,logstash的学习之路可以告一段落了,基本可以满足日常工需求,今天开始啃elasticsearch。
elasticsearch简介
elasticsearch(下面简称为es)是一个分布式文档存储,实时的分布式搜索分析引擎,内部使用lucene做索引和搜索。当你的es是多节点部署时(es集群),存储的文档会分布再整个集群中,并且可以从任何节点立即访问。
存储文档时,将在1s内几乎实时地对其进行索引和完全搜索。es使用称为倒排索引的数据结构,该结构支持非常快速的全文本搜索,反向索引列出了出现在任何文档中的每个唯一单词,并标识了每个单词出现的所有文档。
何为倒排索引?
索引是一种数据结构,它依据你的数据建造,最终会让搜索变得非常迅速(可以将索引当作我们书籍中的目录)。
例如,如果我们想通过标签来搜索博客文章,倒排序看上去,就会是下面的样子:
在上面的示例中,如果搜索含有elections标签的博文,那么相对于查找原始数据而言,查找倒排序索引后的数据会更快捷。在搜索引擎的应用场景下,这种数据结构带来的速度提升是非常必要的。
索引可以认为是文档的优化集合,每个文档都是字段的集合,这些字段是包含数据的键值对。默认情况下,es对每个字段中的所有数据建立索引,并且每个索引字段都具有专用的优化数据结构。
es还具有无模式的能力,这意味着可以为文档建立索引,而无需明确指定如何处理文档中可能出现的每个不同字段。启用动态映射后,es自动检测并将新字段添加到索引。这种默认行为使索引和浏览数据变得很容易-只需开始建立索引文档,es就会检测布尔值,浮点数和整数值,日期和字符串并将其映射到适当的es数据类型。
es的可伸缩和弹性
集群工作原理
es旨在始终可用,并可以根据您的需求进行扩展。它是通过自然分布来实现的。我们可以将es节点添加到集群以增加容量,es会自动在所有可用节点之间分配数据和查询负载。无需大修我们的程序,es知道如何平衡多节点集群以提供扩展性和高可用性。理论上讲,es节点越多越好。
那么es是怎么实现这个可扩展性的呢?在幕后,es索引实际上只是一个或多个物理分片的逻辑分组,其中每个分片实际上是一个独立的索引。通过在多个分片之间的索引中分配文档,并在多个节点之间分配这些分片,es可以确保冗余,这样既可以防止硬件故障,又可以在将节点添加到集群中使提高查询能力。随着集群的增长或收缩,es会自动迁移碎片以重新平衡集群。
分片是什么
分片有两种类型,主书库和副本数据库。索引中的每个文档都属于一个主分片。副本分片是主分片的副本。副本可提供数据的冗余副本,以防止硬件故障并增加处理读取请求(如搜索或检索文档)的能力。
创建索引时,索引中的主碎片的数量是固定的,但是副本碎片的数量可以随时修改,而不会中断索引或查询操作。
如何合理的设置分片大小和数量
在分片大小和为索引配置的主分片数量方面,存在许多性能方面的考虑和权衡取舍,分片越多,维护这些索引的开销就越大。分片越大,当es需要重新平衡集群时,分片移动所需的时间就越长。
查询很多小的分片会使每个分片的处理速度更快,但是更多的查询意味着更多的开销,因此查询较少数量的大分片可能会更快(系统开销也会少一些)。
那么,如何来合理的设置分片呢?我们可以用以下几点来作为参考:
- 最好将平均分片大小保持在几GB到几十GB之间,对于具有基于时间的数据,通常会看到20GB到40GB范围内的分片。
- 尽量避免庞大的分片问题。节点可以容纳的分片数量与可用堆空间成比例,一般情况下,每GB堆空间中的分片数量应少于20。
es的跨集群复制
出于性能原因,群集内的节点必须位于同一局域网内。跨不同数据中心中的节点在群集中平衡碎片的时间太长了。但是高可用性架构要求避免将所有鸡蛋都放在一个篮子里。如果一个位置发生重大故障,则另一个位置的服务器能够无缝的接管,这就要用到跨集群复制(CCR)。
CCR提供了一种方法,可以自动将索引从主群集同步到可以用作热备份的辅助远程群集。如果主群集发生故障,则辅助群集可以接管。还可以使用CCR创建辅助群集,以接近地理位置的方式向用户提供读取请求。
跨集群复制是主动-被动的。主群集上的索引是活动的领导者索引,并处理所有写请求。复制到辅助群集的索引是只读跟随者。
更通俗的理解es
为了理解es中数据是如何组织的,可以从以下两个角度来观察:
- 逻辑设计:用于索引和搜索到最小单位是文档,可以将其认为是关系性数据库里的一行。文档以类型来分组,索引是更大的容器,如果这样说比较抽象,那么我们可以套在sql数据库上,你可以这样来理解:es中的文档 == 数据库中的行;es中的type == 数据库中的表;es中的索引 === 数据库中的库。
- 物理设计:es将每个索引划分为分片,每个分片可以在集群中的不同服务器间迁移。
示意图如下:
es集群中的查询流程
查询操作可以在es中的主分片或副本分片上进行。
上图就是一个完整的查询过程,大概流程如下:
- client向集群发送查询请求,集群再随机选择一个节点作为协调节点(NODE1),负责处理这次查询。
- Node 1使用文档的routing id来计算要查询的文档再哪个分片上(再上面的图中,落在了分片0上),分片0的副本存在所有的节点上,这种情况下,协调节点可以把请求转发到任意节点,在上图中将请求转发到了node 2 上。
- node 2 执行查找,并将查找结果返回给协调节点node 1,node 1 再将文档返回给client。
部署es集群
环境准备
系统 | 主机名 | 服务 | IP |
---|---|---|---|
Cent OS 7.7 | es1 | es | 192.168.20.3 |
Cent OS 7.7 | es2 | es | 192.168.20.4 |
Cent OS 7.7 | es3 | es+kibana | 192.168.20.5 |
注:所有主机节点均为4G内存,4个CPU。
注:以下操作在es1主机执行。
下载并解压
[root@es3 local]# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.6.2-linux-x86_64.tar.gz
[root@es1 local]# tar zxf elasticsearch-7.6.2-linux-x86_64.tar.gz
[root@es1 local]# mv elasticsearch-7.6.2 /usr/local/es
[root@es1 local]# rm -rf elasticsearch-7.6.2-linux-x86_64.tar.gz
在上线生产环境之前,必须考虑以下设置:
- 禁用交换风区
- 增加文件描述符
- 确保有足够的虚拟内存
- 确保足够的线程
- JVM DNS缓存设置
优化es
配置es的临时目录
默认情况下,Elasticsearch使用启动脚本在系统tmp目录下立即创建的私有临时目录。但我们最好为他配置专有的临时目录。
[root@es1 bin]# vim /usr/local/es/bin/elasticsearch-env
ES_TMPDIR=/data/es/tmp # 在第二行指定即可
配置jvm致命错误日志
[root@es1 bin]# vim /usr/local/es/config/jvm.options
-XX:ErrorFile=/data/es/logs/hs_err_pid%p.log
配置虚拟内存(官方推荐)
es默认使用mmapfs目录来存储索引。mmap计数的默认操作系统限制可能太低,这可能导致内存异常。
[root@es1 config]# tail -1 /etc/sysctl.conf # 写入以下配置
vm.max_map_count=262144
vm.swappiness = 0
[root@es1 config]# sysctl -p # 刷新配置
vm.max_map_count = 262144
vm.swappiness = 0
修改其默认使用内存大小
[root@es1 es]# pwd
/usr/local/es
[root@es1 es]# vim config/jvm.options
# elasticsearch默认内存使用为1G,可以更改如下配置,修改其默认使用内存
-Xms700m
-Xmx700m
注:生产环境中建议将Xms和Xmx两个值设置为一致(最多不要超过物理内存的50%),我们线上的环境是设置为8g。
禁用交换分区
大多数操作系统尝试使用尽可能多的内存作为文件系统缓存,并急切地交换出未使用的应用程序内存。这可能导致部分JVM堆或其可执行页面被交换到磁盘,交换对性能和节点稳定性非常不利,应该不惜一切代价避免交换。它可能导致垃圾收集持续几分钟而不是几毫秒,并且可能导致节点响应缓慢,甚至与集群断开连接。在弹性分布式系统中,让操作系统杀死节点更有效。
$ swapoff -a #临时禁用
$ grep swap /etc/fstab # 注释掉交换分区挂载项
#/dev/mapper/centos-swap swap swap defaults 0 0
$ free -h | grep -i swap # 确认交换分区已关闭
Swap: 0B 0B 0B
修改其打开文件数的大小
如果服务器文件数上线和线程上线较低,就会产生如下异常:
1. max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]每个进程最大同时打开文件数太小
2. max number of threads [3818] for user [es] is too low, increase to at least [4096]最大线程个数太低
可以进行以下修改,以便修改可打开文件数的大小
$ mv /etc/security/limits.conf{,.bak}
$ cat >> /etc/security/limits.conf << EOF
* - nofile 65535
* - memlock unlimited
* - stack 655360
* - nproc unlimited
EOF
修改es配置文件
关于日志的优化配置可以参考官方文档,一般默认即可,无需改动(官方文档修改的日志配置文件为/usr/local/es/config/log4j2.properties
)。
[root@es1 ~]# egrep -v '^$|^#' /usr/local/es/config/elasticsearch.yml
cluster.name: my-es
node.name: es1
node.master: true
node.data: false
path.data: /data/es
path.logs: /data/es/logs
bootstrap.memory_lock: true
network.host: 192.168.20.2 # 这里请指定具体IP,而不是0.0.0.0
# 如果不指定具体IP,最后有大概率无法形成集群
http.port: 9200 # 绑定传入http请求的端口
transport.port: 9331 # 绑定用于群集间通信的端口
discovery.seed_hosts:
- es1:9331
- es2:9331
- es3:9331
cluster.initial_master_nodes: # 定义初始集群时的节点,仅在第一次启动es时需要配置此项
- es1 # 此名字必须和node.name一致。
# 开启跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"
配置文件中的其他选项解释
配置数据存放路径
path.data设置可以被设置为多条路径,在这种情况下,所有的路径将被用于存储数据(虽然属于单个碎片文件将全部存储相同的数据路径上):
path:
data:
- /data/es/elasticsearch_1
- /data/es/elasticsearch_2
- /data/es/elasticsearch_3
附加:docker stack/docker-compose启动es集群
以下yml文件来源于官方文档。
version: '2.2'
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
container_name: es01
environment:
- node.name=es01
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data01:/usr/share/elasticsearch/data
ports:
- 9200:9200
networks:
- elastic
es02:
image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
container_name: es02
environment:
- node.name=es02
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data02:/usr/share/elasticsearch/data
networks:
- elastic
es03:
image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
container_name: es03
environment:
- node.name=es03
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es02
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data03:/usr/share/elasticsearch/data
networks:
- elastic
volumes:
data01:
driver: local
data02:
driver: local
data03:
driver: local
networks:
elastic:
driver: bridge
es的启动与停止
第一种启停方式
$ /usr/local/es/bin/elasticsearch & # 启动es
$ jps | grep Elasticsearch # 停止es
# 或者
$ ps -ef | grep elasticsearch | xargs kill -SIGTERM
第二种启停方式
$ ./bin/elasticsearch -p /tmp/elasticsearch.pid -d
$ ps -ef | grep elasticsearch | grep -v grep # 查看到该程序的pid,也可以cat /tmp/elasticsearch.pid来查看pid号
$ kill -SIGTERM 15516
停止时的致命错误
在Elasticsearch虚拟机的生命周期内,可能会出现某些致命错误,使虚拟机处于可疑状态。此类致命错误包括内存不足错误,虚拟机内部错误以及严重的I/O错误。
当Elasticsearch检测到虚拟机遇到此类致命错误时,Elasticsearch将尝试记录该错误,然后停止该虚拟机。当Elasticsearch启动此类关闭时,它不会如上所述进行有序关闭。Elasticsearch流程还将返回一个特殊的状态代码,以指示错误的性质。状态码如下:
错误信息 | 退出状态码 |
---|---|
JVM内部错误 | 128 |
内存不足错误 | 127 |
堆栈溢出错误 | 126 |
未知的虚拟机错 | 125 |
严重的I/O错误 | 124 |
未知的致命错误 | 1 |
全集群重新启动和滚动重启操作
参考: 官方文档。
启动es1节点
$ useradd es # 创建es用户
# 更改相应的目录属主
$ chown -R es.es /usr/local/es/
$ mkdir -p /data/es/{logs,tmp}
$ chown -R es.es /data/es/
# 使用es用户启动es程序,-p是指定pid文件名,-d是后台启动
$ su -s /bin/bash -c "/usr/local/es/bin/elasticsearch -p /data/es/es.pid -d" es
$ ss -lnpt | egrep "9200|9331" # 确定端口在监听
LISTEN 0 128 :::9200 :::* users:(("java",pid=58589,fd=248))
LISTEN 0 128 :::9331 :::* users:(("java",pid=58589,fd=222))
# 当群集启动成功后,一定要删除配置的初始群集引导
$ vim /usr/local/es/config/elasticsearch.yml # 修改配置文件,删除初始群集引导
cluster.initial_master_nodes:
- es1
将修改后的相应目录发送到其他节点
# 在复制前,先在es2和es3上执行下面的指令创建es用户
[root@es2 ~]# useradd es
# 在es1上将es目录发送到其他节点
[root@es1 ~]# for i in es{2..3};do rsync -avz /usr/local/es root@${i}:/usr/local/;done
[root@es1 ~]# for i in es{2..3};do rsync -avz /etc/security/limits.conf root@${i}:/etc/security/;done
[root@es1 ~]# for i in es{2..3};do rsync -avz /etc/sysctl.conf root@${i}:/etc/;done
配置es2主机
# 确定某些限制已生效,若没有生效,请重新登录
[root@es2 ~]$ ulimit -Hu
4096
[root@es2 ~]$ ulimit -Hn
65536
[root@es2 ~]# sysctl -p # 刷新配置
vm.max_map_count = 262144
vm.swappiness = 0
[root@es2 ~]# mkdir -p /data/es/{logs,tmp}
[root@es2 ~]# chown -R es.es /data/es
[root@es2 ~]# chown -R es.es /usr/local/es
# 修改es配置文件
[root@es2 ~]# egrep -v '^$|^#' /usr/local/es/config/elasticsearch.yml
cluster.name: my-es
node.name: es2 # 这里改下名字
node.master: true
node.data: true # 这里改为true
path.data: /data/es
path.logs: /data/es/logs
bootstrap.memory_lock: true
network.host: 0.0.0.0
http.port: 9200 # 绑定传入http请求的端口
transport.port: 9331 # 绑定用于群集间通信的端口
discovery.seed_hosts:
- es1:9331
- es2:9331
- es3:9331 # 注意,删除了初始master引导配置
http.cors.enabled: true
http.cors.allow-origin: "*"
[root@es2 ~]$ su -s /bin/bash -c "/usr/local/es/bin/elasticsearch -p /data/es/es.pid -d" es # 使用es用户后台启动es程序
[root@es2 ~]$ ss -lnpt | egrep "9200|9331"
LISTEN 0 128 :::9200 :::* users:(("java",pid=65224,fd=266))
LISTEN 0 128 :::9331 :::* users:(("java",pid=65224,fd=222))
注:es3主机上,除了需要稍微改动下配置文件外,其他操作与es2完全一致,这里就不写了。参考下面更改后配置文件自行改动并启动下es。
# es3主机的配置文件如下
[root@es3 ~]# egrep -v '^$|^#' /usr/local/es/config/elasticsearch.yml
cluster.name: my-es
node.name: es3 # 改下node name
node.master: true
node.data: true
path.data: /data/es
path.logs: /data/es/logs
bootstrap.memory_lock: true
network.host: 0.0.0.0
http.port: 9200 # 绑定传入http请求的端口
transport.port: 9331 # 绑定用于群集间通信的端口
discovery.seed_hosts:
- es1:9331
- es2:9331
- es3:9331
http.cors.enabled: true
http.cors.allow-origin: "*"
当三台主机启动后,可以访问每个节点的9200端口,通过比较各个节点的uuid,来判断是否加入到同一个集群中,如下(如果uuid为“na”则表示群集有问题,虽然都是一样的,但绝对不正常,正常的话都是很长一串字母):
集群状态正常的话,是下面这样的:
三个节点的uuid一致,则表示在同一个集群中。
安装ElasticSearch Head
此应用用于管理es集群,任意一个节点安装即可,非必须安装,也可以使用chrome提供的插件:ElasticSearch Head。
[root@es3 ~]# git clone git://github.com/mobz/elasticsearch-head.git
[root@es3 ~]# cd elasticsearch-head/
[root@es1 elasticsearch-head]# yum -y install npm openssl
[root@es3 elasticsearch-head]# npm install
[root@es3 elasticsearch-head]# npm run start &
由于上述安装方式比较费时间,我这里就使用谷歌的ElasticSearch Head插件了,访问界面如下:
安装kibana
由于kibana可以用来根据es集群的数据进行查询、绘制图表等。所以这里也将kibana部署上,后续操作,可能会在kibana上进行。
# 下载并解压kibana
[root@es1 ~]# wget https://artifacts.elastic.co/downloads/kibana/kibana-7.6.2-linux-x86_64.tar.gz
[root@es1 ~]# tar zxf kibana-7.6.2-linux-x86_64_.tar.gz
[root@es1 ~]# mv kibana-7.6.2-linux-x86_64 /usr/local/kibana
[root@es1 ~]# cd /usr/local/kibana/config/
[root@es1 config]# egrep -v '$^|^#' kibana.yml # 修改后的配置文件如下
server.port: 5601 # 监听端口
server.host: "0.0.0.0" # 监听地址
# 指定es集群,必须在同一个集群内
elasticsearch.hosts: ["http://es1:9200","http://es2:9200","http://es3:9200"]
kibana.index: ".kibana" # 增加kibana索引,用于存储kibana保存的仪表盘、图饼等信息。
elasticsearch.requestTimeout: 90000 # 连接es的超时时间
i18n.locale: "zh-CN" # 配置kibana为中文
启动kibana
[root@es1 ~]# useradd kibana
[root@es1 ~]# chown -R kibana.kibana /usr/local/kibana/
# 需要给这个文件666的权限,我尝试过给777权限,第一次可以启动,但再启动就会权限拒绝
[root@es1 ~]# chmod 666 /usr/local/kibana/.i18nrc.json
[root@es1 ~]# su -s /bin/bash -c "/usr/local/kibana/bin/kibana &" kibana
输出以下信息,则表示启动成功:
访问kibana主机5601端口
若选择了示例数据,则这里可以选择将这些仪表盘添加到kibana中,如下:
下面的索引中包含了kibana的默认索引