CentOS 7 部署 Elasticsearch 6.4.3 安装配置与系统调优实战
1. 为什么 ES 6.4.3 在 CentOS 7 上装起来这么别扭Elasticsearch 6.4.3 这个版本放在今天来看不算新但它在很多存量项目里依然是主力——日志检索、商品搜索、监控数据聚合不少系统还在跑这个版本。问题在于CentOS 7 的系统环境和 ES 6.4.3 的默认预期之间有一道不小的鸿沟很多人第一次装的时候会连续撞上五六个报错从 JDK 版本到内存映射从文件句柄到线程数限制一个接一个。我自己第一次在 CentOS 7 上部署 ES 6.4.3 的时候光是让它正常启动就花了大半天。后来复盘发现真正卡住我的不是 ES 本身有多复杂而是 CentOS 7 的默认系统参数和 ES 的运行要求之间存在系统性错配。ES 是一个吃资源很凶的 Java 应用它对 JVM 堆内存、虚拟内存映射区域数量、文件描述符上限、线程数都有硬性要求而 CentOS 7 作为一款通用服务器系统默认值是按普通服务来配的根本扛不住 ES 的胃口。这篇文章面向的是需要在 CentOS 7 上部署 ES 6.4.3 的运维和开发人员不管你之前有没有装过 ES只要跟着把系统层面的坑先填平后面基本就是一路顺畅。我会把安装过程拆成几个阶段每个阶段先讲清楚为什么这里会出问题再给具体操作最后补上我实际踩过的坑和验证方法。核心关键词就三个CentOS 7、Elasticsearch 6.4.3、安装配置。需要提前说明的是ES 6.4.3 官方推荐搭配 JDK 8虽然它也能在 JDK 11 上跑但 6.x 系列对 JDK 11 的支持并不完整很多内部依赖还是按 JDK 8 的 API 写的。所以下面所有操作都基于 JDK 8 环境这一点后面还会展开讲。2. 装之前先把 JDK 这件事定死2.1 为什么 ES 6.4.3 必须用 JDK 8ES 6.4.3 内置了 JDK 的捆绑包吗没有。从 ES 7.0 开始官方才在发行包里自带 JDK6.x 系列必须依赖系统上已经装好的 Java 环境。而 6.4.3 这个版本对 JDK 版本相当挑剔JDK 8 是官方明确支持的JDK 9 和 JDK 10 基本没戏JDK 11 虽然能启动但会遇到一些模块化相关的警告甚至异常。我实测过在 JDK 11 上跑 ES 6.4.3启动阶段就会报一堆Illegal reflective access的警告虽然不一定直接崩但运行一段时间后可能出现莫名其妙的连接断开。所以最稳妥的做法就是老老实实装 JDK 8。CentOS 7 自带的 yum 源里通常有java-1.8.0-openjdk直接装就行yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel装完之后验证一下java -version输出里应该能看到1.8.0_xxx的字样。这里有个细节要注意如果你系统里同时存在多个 Java 版本java -version显示的未必是 ES 实际用的那个。ES 启动时会读JAVA_HOME环境变量所以务必确认JAVA_HOME指向 JDK 8 的安装路径echo $JAVA_HOME如果为空或者指向别的版本在/etc/profile里加上export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk export PATH$JAVA_HOME/bin:$PATH然后source /etc/profile让它生效。2.2 用 OpenJDK 还是 Oracle JDK这个问题在社区里一直有争论。我的经验是ES 6.4.3 用 OpenJDK 8 完全没问题不需要非得去折腾 Oracle JDK。OpenJDK 8 在 CentOS 7 上通过 yum 安装省去了手动配置的麻烦而且后续安全更新也能跟着系统走。Oracle JDK 虽然在某些 GC 表现上略有差异但对于 ES 这种场景差异小到可以忽略。唯一需要注意的是有些老项目可能依赖 Oracle JDK 里的一些专有类但 ES 本身不依赖这些。所以除非你的环境有特殊要求直接用 OpenJDK 8 就行省事。提示装完 JDK 后先别急着下载 ES先把 Java 环境确认三遍——java -version、javac -version、echo $JAVA_HOME三个输出都指向 JDK 8 再往下走。我见过太多人卡在 ES 启动报 could not find java 上最后发现是 JAVA_HOME 没配。3. 系统参数不调ES 起来也会自己挂掉3.1 文件描述符和线程数最容易被忽略的两个限制ES 在运行时会打开大量的文件句柄尤其是当索引分片多、并发查询高的时候。CentOS 7 默认的单进程文件描述符上限是 1024这个数字对 ES 来说远远不够。同样进程可创建的线程数默认也偏低ES 的线程池会创建大量线程来处理搜索、写入、合并等操作。这两个限制不调的话ES 可能在启动时直接报错也可能运行一段时间后突然崩溃日志里出现Too many open files或者unable to create new native thread。调整方法分两步。第一步改/etc/security/limits.conf在文件末尾加上* soft nofile 65536 * hard nofile 65536 * soft nproc 4096 * hard nproc 4096这里用*表示对所有用户生效你也可以指定只对运行 ES 的用户生效。nofile是文件描述符数量nproc是进程数。65536 和 4096 这两个值是 ES 官方文档里推荐的起点实际生产环境可以根据负载再往上调。第二步CentOS 7 用了 systemdlimits.conf对通过 systemd 启动的服务不一定生效。如果你打算用 systemd 管理 ES还需要在 service 文件里显式声明LimitNOFILE65536 LimitNPROC4096改完之后重新登录或者重启系统用ulimit -n和ulimit -u验证是否生效。3.2 vm.max_map_countES 最著名的那个坑vm.max_map_count这个参数是 ES 安装过程中出现频率最高的报错来源。ES 使用 mmap 的方式读取索引文件每个分片需要映射大量内存区域。Linux 默认的vm.max_map_count是 65530而 ES 官方建议至少调到 262144。不调的话ES 启动时会直接抛错max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]修改方式sysctl -w vm.max_map_count262144但这只是临时生效重启就没了。永久生效要写进/etc/sysctl.confvm.max_map_count262144然后sysctl -p重新加载。验证sysctl vm.max_map_count输出应该是vm.max_map_count 262144。3.3 关闭 swap不是必须但强烈建议ES 官方建议关闭 swap原因是当内存不足时操作系统把 ES 的堆内存换出到磁盘会导致 GC 时间暴涨严重时集群会失去响应。CentOS 7 默认是开启 swap 的。临时关闭swapoff -a永久关闭需要编辑/etc/fstab把 swap 那一行注释掉。不过在生产环境里直接关 swap 有风险——万一物理内存真的不够系统会直接 OOM Kill 掉进程。所以更稳妥的做法是调低swappinesssysctl -w vm.swappiness1写进/etc/sysctl.conf永久生效。swappiness1表示只有在内存极度紧张时才使用 swap既避免了 ES 被频繁换出又保留了一点缓冲余地。3.4 内存和 CPU 的分配逻辑ES 的 JVM 堆大小通过jvm.options里的-Xms和-Xmx控制。这两个值必须设成一样否则 JVM 会在运行过程中反复调整堆大小带来不必要的 GC 压力。堆设多大合适一个常见的经验法则是不超过物理内存的 50%且不超过 32GB。不超过 50% 是因为 ES 除了堆内存还需要大量堆外内存来做文件缓存和 mmap留一半给系统是合理的。不超过 32GB 是因为 JVM 在堆超过 32GB 时会失去指针压缩优化反而降低性能。比如一台 16GB 内存的机器堆设 8GB 比较合适-Xms8g -Xmx8g在config/jvm.options里改这两行就行。改完之后可以用jstat -gc pid观察 GC 情况确认堆大小生效。4. 下载、解压、目录规划这些小事里的门道4.1 选对安装包别下错版本ES 6.4.3 的安装包在官方归档里能找到文件名类似elasticsearch-6.4.3.tar.gz。注意不要下成elasticsearch-6.4.3.rpm虽然 rpm 包也能装但 tar.gz 更灵活目录结构清晰后续改配置、升级都方便。下载之前先确认系统架构uname -m输出x86_64就下对应的 64 位包。ES 6.4.3 不支持 32 位系统这个没得商量。4.2 目录规划别放在 /root 下面很多人图省事直接把 ES 解压到/root或者当前用户的家目录下。这样做的问题在于ES 出于安全考虑不允许以 root 用户启动。如果你把文件放在/root下即使创建了普通用户那个用户也没有权限访问/root目录。合理的做法是规划一个专门的目录比如/usr/local/elasticsearch或者/opt/elasticsearch。我习惯用/usr/local/elasticsearch解压后把目录重命名一下tar -zxvf elasticsearch-6.4.3.tar.gz -C /usr/local/ mv /usr/local/elasticsearch-6.4.3 /usr/local/elasticsearch4.3 创建专用用户ES 不允许 root 启动ES 强制要求以非 root 用户运行这是硬性规定绕不过去。创建一个专用用户useradd esuser passwd esuser然后把 ES 目录的所有权给这个用户chown -R esuser:esuser /usr/local/elasticsearch后续所有启动、停止操作都用esuser来执行。如果你用 systemd 管理service 文件里也要指定Useresuser。注意不要试图用chmod 777来绕过权限问题这不仅解决不了 root 启动的限制还会带来严重的安全隐患。老老实实建用户、改属主。4.4 数据目录和日志目录的独立规划默认情况下ES 的数据和日志都放在安装目录下的data和logs文件夹里。生产环境建议把这两个目录独立出来放到单独的磁盘分区上。原因有两个一是数据增长快独立分区方便扩容二是日志写入频繁和系统盘分开可以避免 IO 争抢。在config/elasticsearch.yml里配置path.data: /data/elasticsearch/data path.logs: /data/elasticsearch/logs记得提前创建好目录并改好属主mkdir -p /data/elasticsearch/data /data/elasticsearch/logs chown -R esuser:esuser /data/elasticsearch5. elasticsearch.yml 里真正需要改的那几行5.1 cluster.name 和 node.name集群识别的基础cluster.name决定了一个节点属于哪个集群。同一个集群里的所有节点必须用相同的cluster.name否则它们互相发现不了。默认值是elasticsearch生产环境建议改成有业务含义的名字比如prod-search-cluster。node.name是节点在集群里的唯一标识默认会用主机名。如果主机名不规范建议显式指定比如node-1、node-2。cluster.name: prod-search-cluster node.name: node-15.2 network.host 和 http.port决定谁能访问network.host默认是localhost意味着只有本机可以访问。如果你需要从其他机器访问 ES必须改成具体 IP 或者0.0.0.0。但改成0.0.0.0会触发 ES 的生产模式检查它会要求你同时配置一系列发现相关的参数否则拒绝启动。所以更稳妥的做法是绑定到具体的内网 IPnetwork.host: 192.168.1.100 http.port: 9200http.port是 REST API 的端口默认 9200。如果这个端口被占用可以改成别的但客户端也要跟着改。5.3 discovery.zen.ping.unicast.hosts单机也要配即使是单机部署ES 6.4.3 在network.host不是 localhost 的情况下也会要求配置发现相关的参数。单机的话把discovery.zen.ping.unicast.hosts设成自己的地址就行discovery.zen.ping.unicast.hosts: [192.168.1.100:9300]9300是节点间通信的端口和9200是两回事。集群环境下这里要列出所有 master 候选节点的地址。5.4 一个最小可用的配置示例把上面几项合起来一个单机可用的elasticsearch.yml核心配置大概是这样cluster.name: prod-search-cluster node.name: node-1 path.data: /data/elasticsearch/data path.logs: /data/elasticsearch/logs network.host: 192.168.1.100 http.port: 9200 discovery.zen.ping.unicast.hosts: [192.168.1.100:9300]其他配置项保持默认即可。ES 6.4.3 的配置文件里注释很详细每一项都有说明不确定的可以先不动。6. 启动、验证、以及那些让人抓狂的报错6.1 第一次启动用前台模式看日志第一次启动建议用前台模式这样所有日志直接打在终端上出问题能第一时间看到su - esuser cd /usr/local/elasticsearch ./bin/elasticsearch如果一切正常你会看到一堆日志滚动最后出现started字样。这时候另开一个终端用 curl 验证curl http://192.168.1.100:9200返回的 JSON 里会有cluster_name、version.number等信息version.number应该是6.4.3。6.2 常见报错一max virtual memory areas too low这个前面讲过就是vm.max_map_count没调。报错信息很明确直接按 3.2 节的方法改就行。需要注意的是改完sysctl.conf后一定要sysctl -p否则不生效。6.3 常见报错二max file descriptors too low报错信息类似max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]这是limits.conf没配或者没生效。检查ulimit -n的输出如果是 4096 或者 1024说明配置没起作用。常见原因是改了limits.conf但没有重新登录或者用了 systemd 但没在 service 文件里加LimitNOFILE。6.4 常见报错三memory locking requested but not available如果你在elasticsearch.yml里开了bootstrap.memory_lock: true但系统没有给 ES 用户锁定内存的权限就会报这个错。解决办法是给esuser加上memlock限制esuser soft memlock unlimited esuser hard memlock unlimited加在/etc/security/limits.conf里。或者简单点把bootstrap.memory_lock设成false但这样 ES 的内存可能被换出性能会受影响。生产环境建议开启并配好权限。6.5 常见报错四root 用户启动被拒绝can not run elasticsearch as root这个没什么好说的建个普通用户用普通用户启动。别想着改代码绕过那是给自己挖坑。6.6 常见报错五端口被占用java.net.BindException: Address already in use说明 9200 或 9300 端口已经被别的进程占了。用netstat -tlnp | grep 9200查一下是谁占的要么停掉那个进程要么改 ES 的端口。6.7 后台运行与 systemd 管理前台验证没问题后可以后台运行./bin/elasticsearch -d-d表示 daemon 模式。但更规范的做法是用 systemd 管理创建一个/etc/systemd/system/elasticsearch.service[Unit] DescriptionElasticsearch 6.4.3 Afternetwork.target [Service] Typesimple Useresuser Groupesuser LimitNOFILE65536 LimitNPROC4096 EnvironmentJAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk ExecStart/usr/local/elasticsearch/bin/elasticsearch Restarton-failure [Install] WantedBymulti-user.target然后systemctl daemon-reload、systemctl start elasticsearch、systemctl enable elasticsearch。用 systemd 的好处是开机自启、崩溃自动重启、日志统一管理。7. 装完之后的安全加固和性能微调7.1 别让 ES 裸奔在公网上ES 6.4.3 默认没有认证机制任何人只要能访问 9200 端口就能读写所有数据。这在生产环境是绝对不能接受的。最基本的做法是通过防火墙限制访问来源firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port protocoltcp port9200 accept firewall-cmd --reload只允许内网网段访问 9200。如果必须对外提供服务那就要上 X-Pack 或者在前置一层反向代理做认证。X-Pack 在 6.4.3 里是收费的基础认证功能可以免费试用但长期用还是建议用 Nginx 做 Basic Auth 或者接入统一认证网关。7.2 索引分片和副本的规划ES 6.x 默认每个索引创建 5 个主分片和 1 个副本。对于小数据量的场景5 个分片太多了会造成资源浪费。可以在创建索引时指定curl -X PUT 192.168.1.100:9200/myindex -H Content-Type: application/json -d { settings: { number_of_shards: 1, number_of_replicas: 0 } }单机环境副本设 0因为副本分片和主分片不能在同一节点上设了也没用反而让集群状态一直处于 yellow。集群环境再根据节点数调整。7.3 定期清理和监控ES 的日志和数据会持续增长建议配一个定时任务清理过期索引。另外_cluster/health接口要定期检查curl http://192.168.1.100:9200/_cluster/health?pretty关注status字段green 表示一切正常yellow 表示副本未分配red 表示有主分片丢失。yellow 在单机环境下是正常的red 就要立刻排查了。8. 我在多次部署中攒下的几条经验第一条JDK 版本一定要在装 ES 之前就定死不要装到一半发现 Java 不对再回头折腾。我吃过这个亏ES 都解压好了启动报 Java 版本不兼容又回头卸 JDK 重装白白浪费半小时。第二条vm.max_map_count和limits.conf这两个系统参数建议在装 ES 之前就配好。很多人是启动报错了才去查虽然也能解决但心态容易崩。提前配好启动一次过体验完全不一样。第三条第一次启动务必用前台模式。后台模式虽然方便但出错了日志在文件里你得去翻logs/elasticsearch.log不如前台直接看来得快。等确认能正常启动了再切到 systemd 或者后台模式。第四条数据目录和日志目录一定要独立规划。我见过有人把数据放在安装目录下后来升级 ES 的时候直接覆盖了安装目录数据全丢。虽然 ES 的数据可以通过快照恢复但没做快照的话就真没了。第五条单机环境副本数设 0。这个坑很隐蔽因为集群状态显示 yellow 并不影响使用但很多人会纠结为什么不是 green。单机就是 yellow这是正常的别去瞎调。最后再提一个容易被忽略的点ES 6.4.3 的jvm.options里默认的 GC 配置是 CMS这个在 JDK 8 上是合适的。如果你换成了 G1需要自己评估因为 6.4.3 对 G1 的支持不如 7.x 那么成熟。没有特殊需求的话保持默认的 CMS 就行别乱改。