
刚接手一套Elasticsearch 6.5.4集群的维护工作顺便把之前搭建过程中的坑都翻了出来。这套东西说难不难但部署细节是真的多尤其是6.x这个分水岭版本和7.x、8.x在配置上差别挺大网上的教程又大多过时或抄来抄去新手照着做很容易卡壳。这篇文章就基于我自己实际部署elasticsearch-6.5.4三节点集群的过程把规划、配置、启动、验证到常见错误排查完整走一遍给正在折腾这套版本的同学一份能直接抄作业的参考。写这篇文章的另一个原因是最近好几个朋友都在从单机ES转向集群部署毕竟业务一上来单节点的内存和磁盘完全扛不住。但很多人一上手就踩系统参数、JVM堆内存配置、节点发现失败这些老坑。其实ES本身是自带防御机制的大多数启动失败都是因为环境没准备好或者配置参数理解不到位。这篇文章适合运维、后端开发以及准备从ELK日志平台入门大数据集群的同学读完你至少能搞明白6.5.4集群部署的全链路逻辑而不是照着命令盲敲。1. 部署前的整体规划与核心思路1.1 集群节点角色划分为什么要分master、data、ingest很多人部署ES集群时喜欢每个节点都配置成全角色也就是master、data、ingest全开。单机测试这么搞没问题但生产环境就等着踩坑。ES集群的三个核心角色各有分工理解它们的作用直接决定了你后续集群的稳定性和扩展性。master节点负责集群级别的操作比如创建删除索引、管理节点状态、分配分片等。master节点不参与数据存储和查询请求的处理它的任务是保证集群元数据的一致性。如果master节点同时也承担大量数据读写一旦数据查询压力上来集群管理操作就会跟着变慢严重时还可能引发GC停顿导致集群误判主节点失联。data节点真正干数据存储和查询的活数据分片都落在这些节点上。data节点需要较大的磁盘空间和较好的IO性能如果条件允许建议单独用SSD。data节点的内存配置往往决定了JVM堆内存的分配策略因为查询和索引都需要在内存中操作。写多读多的业务场景还要区分热数据节点和冷数据节点但6.5.4原生支持的分层架构还不像7.x之后那么成熟所以咱们先不展开。ingest节点负责数据预处理比如管道解析、字段过滤、格式转换。大多数场景下ingest节点不需要独立部署直接让data节点兼任即可。如果你的数据源多、管道处理逻辑很重再考虑独立的ingest节点否则就别浪费机器资源。我在部署这套6.5.4集群时规划了三个节点节点角色分配配置建议es-node1master data8核16GSSD 500Ges-node2master data8核16GSSD 500Ges-node3master data8核16GSSD 500G三节点全部配置为master候选节点加data节点是因为这套集群规模不算大单节点承担所有角色足够运行。但请注意生产大集群比如超过10个节点建议把master角色独立出来配置3个专用的master节点data节点不参与master选举这样能有效降低脑裂风险。1.2 JDK版本选择与安装包准备Elasticsearch 6.5.4对JDK版本的要求是JDK 8具体来说需要8u191及以上版本。这里有个小坑网上很多教程让你安装Java 11或Java 12其实ES 6.5.4官方文档明确只支持Java 8。你如果用Java 11启动ES进程虽然也能起来但日志里会刷大量警告而且某些特性不受支持后续出莫名奇妙的兼容性问题时你会崩溃的。JDK安装包推荐使用OpenJDK或Oracle JDK 8均可生产环境我一般用Oracle JDK 8u201。安装完成后记得配置JAVA_HOME环境变量这一步很多朋友会漏掉。ES启动时虽然会检测系统PATH里的java命令但某些情况它会优先读取JAVA_HOME配置如果不一致就会导致版本不匹配。然后是ES安装包官方下载地址是elastic.co注意选择elasticsearch-6.5.4.tar.gz包。这个包解压后大约500M里面自带了JDK但默认是OpenJDK 11所以即使你没有装系统JDKES 6.5.4也能启动。但这里就产生了一个问题如果你需要调整ES的JVM参数自带的OpenJDK 11并不受官方推荐因此我还是建议用系统JDK 8并在启动脚本里显式指定Java路径。1.3 三节点服务器系统参数统一调优ES对系统参数有硬性要求不调整的话服务直接启动报错这也是新手栽得最多的地方。我用的是CentOS 7.5系统在三个节点上都需要执行以下优化1. 文件描述符限制limits.confES为了充分利用文件句柄要求单进程能打开的文件描述符数量最少为65536。默认情况下普通用户这个值通常是1024root用户是65536但ES又不允许root用户直接运行。所以需要修改/etc/security/limits.conf文件在文件末尾添加esuser soft nofile 65536 esuser hard nofile 65536 esuser soft memlock unlimited esuser hard memlock unlimited这里esuser是专门给ES创建的系统用户。修改完后需要重新登录或者重启服务器才生效。验证方法是用esuser登录并执行ulimit -n如果显示65536就说明配置成功。2. 虚拟内存映射数量vm.max_map_countES默认需要至少262144个虚拟内存映射区域默认值65530远不够。解决办法是修改内核参数sysctl -w vm.max_map_count262144 echo vm.max_map_count262144 /etc/sysctl.conf这个参数修改后立即生效不需要重启。很多教程说需要重启其实sysctl命令实时生效后再写进/etc/sysctl.conf保证重启后依然有效这就够了。3. swap禁用与堆内存锁定ES官方强烈建议禁用swap分区。Linux系统的swap机制会把一部分不活跃的内存页面交换到磁盘这会导致ES节点GC时间飙升查询性能断崖式下跌。禁用方式有两种一是直接关闭swap生产环境推荐swapoff -a二是只禁用ES进程使用swap通过修改elasticsearch.yml配置文件设置bootstrap.memory_lock: true同时配合limits.conf里的memlock unlimited配置。我习惯两者都用双保险。还有一个关键参数是vm.swappiness默认值是60也就是系统在内存使用达到60%时就开始考虑swap。建议调成1或者10越低越好sysctl -w vm.swappiness1 echo vm.swappiness1 /etc/sysctl.conf设置完成后重启服务器后这些配置依然存在。2. 核心配置详解与实操要点2.1 elasticsearch.yml核心配置项逐行拆解安装包解压后主要配置文件在config/elasticsearch.yml。这个文件默认全是注释每行配置都需要自己手动打开。我把几个关键的配置项逐项解释一下因为这些参数直接影响集群是否能正常组建。cluster.name: es-prod-cluster node.name: es-node1 path.data: /data/es-data path.logs: /data/es-logs network.host: 0.0.0.0 http.port: 9200 transport.tcp.port: 9300 discovery.zen.ping.unicast.hosts: [192.168.1.101, 192.168.1.102, 192.168.1.103] discovery.zen.minimum_master_nodes: 2 node.master: true node.data: true node.ingest: true bootstrap.memory_lock: true逐个来解释cluster.name集群名称同一个集群内的所有节点必须一致。如果A节点叫es-prod-clusterB节点叫es-test-cluster它们之间永远不会握手。这点看似简单却是集群组建失败出现频率最高的原因之一。node.name节点名称用于在集群中唯一标识一个节点。这个名字不能重复建议直接用主机名或者IP后缀来命名方便日志排查时知道问题出在哪台机器上。path.data和path.logs数据存储路径和日志路径。默认是ES安装目录下的data和logs文件夹。生产环境强烈建议把数据路径和安装路径分开放到独立磁盘或挂载点下这样即使ES版本升级重新部署也不会误删数据。比如我这边统一规划到/data/es-data和/data/es-logs。network.host监听地址默认是127.0.0.1也就是只能本机访问。改成0.0.0.0后ES会监听所有网卡地址这样集群内其他节点才能通过9300端口通信。这里也有人会直接用内网IP效果一样但0.0.0.0更灵活适合服务器IP有变更的场景。http.port和transport.tcp.port9200是HTTP接口供应用层和Kibana访问9300是节点间内部通信端口。两个端口默认值分别是9200和9300一般不需要改但如果一台服务器上跑了多个ES实例就得用不同端口区分开。discovery.zen.ping.unicast.hosts节点发现列表。ES节点启动后会主动连接这个列表里的所有IP和端口去询问“你们谁是master”谁认识其他节点。这里必须配置所有master候选节点的IP一个都不能漏。注意这里用的是9300端口不是9200这个非常容易写错。我见过好几个朋友把unicast.hosts配成了9200端口结果节点发现死活不成功。discovery.zen.minimum_master_nodes防止脑裂的关键配置计算公式是master候选节点数 / 2 1。三个节点就是2五个节点就是3。这个参数的作用是只有当一个节点看到的master候选节点数量达到这个阈值时它才会认为自己有资格成为主节点。如果该值太小集群在网络分区时就可能出现两个master节点各自为政数据写入不一致如果该值设置过大会导致整个集群没有足够的节点来选举master集群就一直选不出来。node.master / node.data / node.ingest三个角色开关。我这边三个节点全部设为true也就是master候选节点同时承担data角色。如果你规划里某台机器只做data节点就把node.master设为false并移除它从unicast.hosts中避免它参与master选举。bootstrap.memory_lock是否锁定进程地址空间到内存防止ES内存被swap。设成true之前需要确认系统的memlock限制是unlimited否则ES启动时会报错“memory locking requested for elasticsearch process but memory is not locked”。2.2 JVM堆内存参数设置不是越大越好ES的JVM堆内存设置是部署过程中的重头戏很多人以为堆内存越大越好结果把机器搞到OOM。在config/jvm.options文件里有两个参数需要修改-Xms16g -Xmx16g这两个值必须完全相等因为ES启动时会一次性申请堆内存如果不相等JVM在运行过程中动态扩展堆时会触发Full GC导致节点响应变慢。生产环境推荐设置为物理内存的一半且单个节点的最大堆内存不要超过31GB。为什么不能超过31GB因为JVM的压缩指针技术在31GB以内的堆内存上可以启用对象指针压缩超过31GB后压缩指针会失效导致内存浪费严重GC性能急剧下降。ES官方博客有篇文章专门讲过这个现象实测一个64GB内存的节点配置31GB堆比配置40GB堆的GC表现更好这是一个反直觉但经过验证的结论。以我用的16GB内存服务器为例JVM堆设置为8GB比较稳妥另外8GB留给操作系统页缓存和ES的文件索引缓存。这里要注意ES有很多内存是无法通过堆内存控制的比如segment内存、索引缓冲区、网络通信缓冲它们用的都是堆外内存。如果你的堆内存设置过高系统总内存很容易被吃满。2.3 系统环境变量与启动脚本调整在启动ES之前先做两件事。第一件创建esuser用户并赋予数据目录权限useradd esuser mkdir -p /data/es-data /data/es-logs chown -R esuser:esuser /data/es-data /data/es-logs chown -R esuser:esuser /usr/local/elasticsearch-6.5.4第二件配置ES的JAVA_HOME。虽然ES自带JDK但我之前说过建议用系统JDK 8。打开bin/elasticsearch脚本文件在文件开头加上export JAVA_HOME/usr/local/java/jdk1.8.0_201 export PATH$JAVA_HOME/bin:$PATH这样ES启动时会使用指定的JDK避免版本不匹配的警告。如果你发现启动还是用了自带的OpenJDK可以在脚本里强制覆盖ES_JAVA_HOME环境变量。还有一个细节ES启动脚本默认会检查ES_JAVA_OPTS环境变量你可以通过这个变量追加JVM参数比如GC日志输出地址、CMS垃圾回收器等。但我建议直接改jvm.options文件改动更直观。3. 完整部署流程与三节点启动验证3.1 单节点启动先把第一台跑起来集群搭建并不是三台服务器同时启动就完事了稳妥的做法是先把第一台节点启动起来确认配置文件无误、日志没有报错再依次启动另外两台。这样如果配置文件有错一次只处理一个问题排查起来不懵。第一台节点启动命令su - esuser cd /usr/local/elasticsearch-6.5.4/bin ./elasticsearch -d -p /data/es-es.pid-d参数表示后台运行-p指定PID文件路径。这里有个小技巧用PID文件是为了后续关闭ES进程时用kill $(cat /data/es.pid)精确找到进程避免用pkill -f elasticsearch误杀其他进程。启动后查看日志tail -f /data/es-logs/es-prod-cluster.log正常情况下你会看到类似下面的输出[INFO ][o.e.n.Node] [es-node1] initialized [INFO ][o.e.n.Node] [es-node1] starting ... [INFO ][o.e.t.TransportService] [es-node1] publish_address {192.168.1.101:9300} [INFO ][o.e.h.AbstractHttpServerTransport] [es-node1] publish_address {192.168.1.101:9200}看到这两行publish_address说明ES已经成功监听9200和9300端口。然后验证一下HTTP接口curl http://192.168.1.101:9200返回结果里有cluster_name、tagline等字段说明单节点服务已经正常。这时候再看集群健康状态curl http://192.168.1.101:9200/_cluster/health?pretty因为只启动了一个节点集群状态会显示yellow主分片已经分配副本分片处于未分配状态这是正常的。等后面两台节点加入集群后状态会自动变成green。3.2 三节点全部加入集群并验证第一台启动成功后依次在第二台和第三台机器上执行同样的操作修改各自的node.name和IP地址即可。记得第二台和第三台的network.host和unicast.hosts配置要一致不能搞混IP。等三台全部启动后第一时间查看集群健康状态curl http://192.168.1.101:9200/_cluster/health?pretty输出大概长这样{ cluster_name : es-prod-cluster, status : green, number_of_nodes : 3, number_of_data_nodes : 3, active_primary_shards : 20, active_shards : 40, relocating_shards : 0, initializing_shards : 0, unassigned_shards : 0 }看到status为greennumber_of_nodes为3说明集群组建成功。再查看节点列表curl http://192.168.1.101:9200/_cat/nodes?v输出会显示三个节点的IP、节点角色mdi分别代表master、data、ingest、CPU负载、内存使用等信息。如果这里只显示一个节点说明另外两台没有成功加入集群接下来就要用我们后面的常见错误排查方法来解决了。3.3 写入测试数据验证分片分布集群组建成功后光看状态还不够我习惯再写入几条测试数据确认数据能在分片间正确分布和检索。这一步也是验证集群功能是否真的正常的唯一可靠方式。curl -X PUT http://192.168.1.101:9200/test-index/_doc/1?pretty -H Content-Type: application/json -d { name: es-cluster, type: test, count: 100 }返回结果的result字段如果是created说明写入成功。然后执行查询curl http://192.168.1.101:9200/test-index/_search?pretty如果查询结果返回了刚才写入的文档说明整个读写链路已经打通。接着看一下分片分布情况curl http://192.168.1.101:9200/_cat/shards/test-index?v正常情况下你会看到5个主分片和5个副本分片分散在三台节点上主分片和副本分片不会落在同一台节点上这是ES的默认行为。4. 常见错误与排查思路实录4.1 启动阶段必踩的几个错误错误1bootstrap checks failed这个错误是ES启动时最常见的拦路虎日志里会列出具体的失败项基本都是文件描述符、虚拟内存、内存锁定这几类。我第一次部署时连续遇到三个检查项失败逐一修复后才启动成功。遇到这个错误时不要慌先看日志里具体是哪一项失败[1] bootstrap checks failed [1]: max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]上面这个是文件描述符数不够修复方式就是按1.3节说的修改limits.conf并重新登录esuser用户。如果日志提示max virtual memory areas就设置vm.max_map_count。如果提示memory locking就检查memlock是否配了unlimited以及bootstrap.memory_lock是否设为true。错误2Unsupported major.minor version 52.0这个报错通常是JVM版本不对ES 6.5.4要求JDK 8但你系统默认的Java是9或更高版本就会在启动时直接报这个错。解决办法就是检查java -version并修改bin/elasticsearch脚本指定JAVA_HOME指向正确的JDK路径。错误3Exception in thread main java.nio.file.AccessDeniedException这个错误几乎都是因为用root用户解压了ES安装包导致安装目录下部分文件权限属于root而ES进程运行在esuser用户下无法读取。解决方式特别简单chown -R esuser:esuser /usr/local/elasticsearch-6.5.4另外注意ES不接受/root目录下安装运行官方文档明确要求ES安装路径的属主必须是esuser而且路径不要包含空格否则脚本执行会怪异报错。4.2 集群状态异常黄、红状态排查集群启动后状态如果是yellow或者red需要立刻排查。yellow表示有副本分片未分配red表示存在主分片未分配或已丢失数据。两个状态的处理方式完全不同。yellow状态排查如果是刚创建完索引或刚启动集群出现yellow状态是正常的ES在等待副本分片分配。但如果长时间不恢复就可能是副本分片无法分配到其他节点原因通常是磁盘空间不足或分片分配策略限制。查看未分配分片的原因curl http://192.168.1.101:9200/_cluster/allocation/explain?pretty输出会告诉你具体的阻塞原因比如节点磁盘水位超过85%导致es不分配副本。解决办法是清理节点磁盘空间或者调整cluster.routing.allocation.disk.watermark.low/high阈值。red状态排查red意味着有主分片缺失同时索引不可写。先用下面的命令找出哪些索引处于redcurl http://192.168.1.101:9200/_cat/indices?vred状态的索引在health列会显示red。然后查看分片分配情况curl http://192.168.1.101:9200/_cat/shards?v红色行显示UNASSIGNED的分片就是缺失的主分片。如果该分片之前所在的节点彻底掉线且数据没有副本恢复难度就大一些。这时建议在明确数据可丢失的前提下通过如下命令强制将未分配的分片分配到其他节点curl -X POST http://192.168.1.101:9200/_cluster/reroute?pretty -H Content-Type: application/json -d { commands: [ { allocate_empty_primary: { index: test-index, shard: 0, node: es-node2, accept_data_loss: true }} ] }这里的accept_data_loss: true一定要非常慎重只有确认该分片数据可以从其他渠道恢复时才使用。能用副本恢复的就不要用这个命令。4.3 节点发现失败与脑裂问题三节点中某一台没有加入集群或者集群中同时出现两个master节点是集群部署中最高级也最难排查的问题。节点发现失败的常见原因有以下几类unicast.hosts配置错误检查IP和端口确认写的是9300端口而不是9200。这是个非常隐蔽且最容易被忽略的坑。网络不通或防火墙拦截检查服务器防火墙规则确保内网IP之间能通过9300端口互相通信。用telnet 192.168.1.101 9300验证一下。cluster.name不一致三台机器的集群名称必须完全相同这是节点间通信的前提条件。节点名称重复如果多个节点的node.name配成了一样的名字ES会认为已存在同名节点并拒绝注册日志会提示duplicate node。脑裂问题更复杂一些。典型的脑裂表现是集群被分成两个部分两个部分各自选出了一个master节点同时处理写入请求数据无法合并。预防脑裂的核心是正确设置discovery.zen.minimum_master_nodes。如果这个值设置错误比如3个节点设置成了3那当其中一台网络抖动时剩下两个节点会因为节点数不足3而无法选举出master整个集群就瘫痪了。所以这里一定要按公式来算3个节点设置成25个节点设置成3保证多数派原则。4.4 内存溢出与GC问题排查集群运行一段时间后如果节点频繁Full GC或日志里出现OutOfMemoryError基本可以确定是JVM堆调得不合理。我见过一种很典型的情况16G内存的机器给ES分了12G堆系统折腾几次之后节点就OOM崩溃了。排查GC问题用jstat命令jstat -gcutil pid 1000 10关注FGC列如果Full GC次数持续快速增长说明堆内存不够或者存在内存泄漏。需要配合jmap生成堆转储文件再用MAT工具分析内存中的对象分布。这部分展开讲又是一篇很长的文章这里只提定位思路。还有个容易被忽视的问题就是ES的fielddata和segment memory都是堆外内存它们不受JVM堆参数控制。当查询请求过大时堆外内存会快速增长甚至超过物理内存导致系统OOM。所以不要把所有可用内存都分给堆留20%给堆外是底线。4.5 常见错误速查表把我在部署和运维过程中遇到的高频错误整理成一张表格方便随手查阅。错误现象根本原因解决方案bootstrap checks failed系统参数未调整设置nofile、max_map_count、mlockallAccessDeniedException安装目录权限不属于esuserchown -R esuser:esuserUnsupported major.minor versionJDK版本不符安装JDK 8并指定JAVA_HOMEmaster not discovered yet节点发现失败检查unicast.hosts IP/端口、cluster.name集群状态yellow副本分片未分配检查磁盘水位、集群重启自动恢复集群状态red主分片丢失排查进程挂掉原因考虑分配空副本节点频繁Full GCJVM堆过大或过小调整-Xms/Xmx为物理内存一半不超过31G磁盘剩余空间总为0数据盘写入权限配置错误检查挂载点、磁盘空间、水的阈值这张表覆盖了日常部署中90%以上的报错。你在排查时先确定自己遇到的问题属于哪个阶段再按对应方案去处理效率高很多。5. 集群部署策略与常见坑位避让5.1 先单机后集群先验证后扩展我踩过最大的坑就是三台机器同时启动然后面对一大堆互相矛盾的报错信息根本分不清问题出在哪台机器、哪个配置上。后来总结出“先单机后集群”的部署策略先在每台机器上单独启动ES确认单机模式下9200端口能正常响应、日志无报错然后再一起组建集群。这个顺序虽然多花了些时间但排查问题的成本远低于直接全部启动后逐个纠错。5.2 每次配置修改后清理旧数据ES的数据目录里如果不小心保留了一份旧集群的数据在新集群启动时很可能因为元数据冲突导致节点加入失败。尤其是不同环境的集群如果复用了相同的数据目录旧数据的cluster UUID与新集群不一致ES会直接拒绝启动。我的习惯是每次部署新环境时删除数据目录下的所有文件保证从干净的状态开始避免出现各种奇奇怪怪的兼容性错误。5.3 升级与迁移时的注意点如果你后续要升级ES版本6.5.4有一个特别坑的地方就是与7.x版本之间的滚动升级是不平滑的需要将6.x升级到6.8.x再迁移到7.x中间不能跨大版本升级。而且升级前必须逐条检查breaking changes文档中的配置变更比如7.x把discovery.zen.*配置替换成了discovery.seed_hosts和cluster.initial_master_nodes。如果你在6.5.4上用了很多自定义的Mapping参数升级前更要仔细核对。另外6.5.4版本的索引和7.x版本在索引类型上有根本性的差异6.x默认支持一个索引多种type7.x开始彻底移除了type概念。如果业务里存在用多个type的情况升级前就需要做数据重构。这些重依赖关系建议提前评估不要等到升级窗口期才开始研究。5.4 以日志为纲别放过任何一个WARN很多人在排查ES问题时只看ERROR级别的日志其实WARN级别的日志同样重要。比如节点CPU过高之前日志里可能出现多次GC耗时过长的WARN磁盘水位告警也有WARN预警。养成每天查看ES日志的习惯用ElastAlert这类工具把关键WARN级别日志触发告警能有效避免集群状态恶化到不可控的地步。6. 实操中的几点心得与内存调优细节部署这套6.5.4集群前后折腾了小两周前前后后踩了不少坑。这里挑几个我印象最深的心得给后来的同学做个参考。第一JVM堆内存和系统内存比例的分配一定要理性。16G内存的机器一开始我分了10G堆结果每天凌晨定时任务批量写入时节点就会报GC overhead limit exceeded整个集群查询超时。后来把堆降到8G把系统内存留给文件缓存和堆外内存写性能反而提升了。别迷信堆内存越大越好给系统留足余量ES的段缓存才能发挥作用。第二unicast.hosts里一定要把所有master候选节点都列进去一个都不能少。我遇到过只配了另外两个节点中的一台结果那台机器临时维护集群无法选举出活跃主节点整个集群只读。后来把三个节点的地址全部填进去才彻底解决这个问题。集群容错的关键就是节点之间全部互知每个节点都能联系到其他节点。第三Kibana和ES集群的连接参数也要提前规划。Kibana连接ES时如果你在Kibana配置里填了ES集群的某个单点IP一旦那个节点挂了Kibana就无法展示数据。正确做法是配置Kibana的elasticsearch.hosts为多个节点地址让它具备故障转移能力。这个细节很容易被静默忽略因为平时只有一个节点负责接收Kibana的请求表面上一切正常等挂了一个节点才发现无从下手。最后再分享一个我常用的备份技巧。ES 6.5.4官方推荐的快照备份方式是把备份库注册到共享文件系统配置path.repo参数指向共享存储目录然后定期创建快照。如果预算不够也可以直接对数据目录做rsync增量备份恢复时把数据目录拷回去再启动操作上也能接受。但无论如何备份一定要定期验证可恢复性不然等到真需要恢复数据时才叫天天不应。