资讯动态

Docker部署Elasticsearch 7.6.0集群实战

发布时间:2026/10/13 4:04:17 来源:尧图企业网站定制
1. 为什么选择 Docker 方式部署 Elasticsearch 7.6.0 集群先说个背景。某公司有套日志检索系统单节点 Elasticsearch 扛了快一年随着索引越来越多查询一慢就有人来拍桌子。后来核心业务也要接进来做全文检索并发一上来单节点 GC 频繁、写入拒绝率飙升扩容迫在眉睫。当时第一反应是直接在几台裸机上装 ES但回头看了一眼现有的运维体系全部应用已经容器化再单独维护一套裸机 ES中间件的部署、监控、升级全要走两套流程累。于是确定用 Docker 部署 7.6.0 集群。为什么锁 7.6.0这个版本在 7.x 系列里算是比较稳的一代支持搜索、聚合、Kibana 可视化、索引生命周期管理等常用功能兼容性也经过大量生产验证。虽然没有后续版本的新特性但对一个中期项目来说稳定压倒一切。7.6.0 也引入了模糊查询的优化以及 _update_by_query 性能提升对日志检索场景很实用。Docker 部署的优势不需要我多吹同构环境、快速扩容、回滚方便这些大家都懂。但真正落地时有几个点容易踩坑容器内存参数调优、JVM 堆内存设置、节点发现机制、跨容器通信。这些我后面都会展开讲。2. 整体方案设计与核心原理剖析2.1 部署架构规划我规划的是 3 节点集群三台物理机每台跑一个 ES 容器。三节点的好处是主分片加副本至少需要两台可用3 节点能容忍单节点故障同时保证数据可靠性另外ES 的选举机制在奇数节点下更干净不会出现脑裂风险。三个节点角色分配如下节点名称角色职责说明es-master-amaster data参与集群选举存储数据处理读写请求es-master-bmaster data同上es-data-cdata only纯数据节点分担读写压力有人会问为什么不用专门的 coordinating-only 节点项目规模没到那个量级。日志系统日均增量约 20GB三节点混合角色足够。如果未来单日增量超过 100GB再考虑抽离独立协调节点。其实官方推荐的最小生产集群是 3 个 master 候选节点这是为了防止脑裂。脑裂的本质是网络分区导致两个子集群各自选出 master数据写入不统一恢复后难以合并。7.6.0 的选举机制要求最少 2 个候选节点才能完成选举所以 3 个 master 候选是最稳妥的。我把第三个节点也设成了 master 候选就是为了留这个安全余量。2.2 关于最新版本的重新理解7.6.0 在时间线上不是最新版了但我在选型时看重的不是版本号新旧而是功能稳定性与团队熟悉度。后来的 7.x 版本与 8.x 版本虽然引入了更多特性但默认开启了安全认证8.x对内部网络的日志集群来说这意味着需要额外维护证书体系。7.6.0 则可以在内网直接跑把 TLS 那一层省掉。另外7.6.0 之后很多 API 发生了调整沿用旧习惯反而容易踩坑。要强调一点如果你在 2024 年以后才看到这篇文章选型时建议优先考虑 8.x 的 LTS 版本7.6.0 的部署方式作为理解 ES 集群原理的参考依然很有价值。3. 从零开始Docker 部署 ES 7.6.0 集群的全过程3.1 准备工作镜像与系统参数先拉镜像我用的镜像是 docker.elastic.co/elasticsearch/elasticsearch:7.6.0。这个镜像在 Docker Hub 也有但 Elastic 官方仓库的镜像内容最全和 Kibana 的版本匹配也最准确。docker pull docker.elastic.co/elasticsearch/elasticsearch:7.6.0镜像拉下来后直接 docker run 会报错因为 ES 7.6.0 要求系统的 vm.max_map_count 参数至少为 262144。这个参数控制进程能拥有的最大内存映射区域数ES 底层用 Lucene 做索引Lucene 对每个索引文件会建立映射映射数量不够系统就拒绝创建索引。查看当前值sysctl vm.max_map_count如果低于 262144需要调大。临时修改sysctl -w vm.max_map_count262144永久生效需要写入 /etc/sysctl.confecho vm.max_map_count262144 /etc/sysctl.conf这个参数是必须调的不调的话ES 容器的日志里会出现 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 的报错。这是我踩过的第一个坑当时还以为是镜像有问题折腾了半天才发现是宿主机的锅。3.2 目录准备与权限处理ES 的数据目录和日志目录不建议放在容器内部否则容器一删数据全没。我用本机目录挂载进去。目录结构如下mkdir -p /data/elasticsearch/{node-a,node-b,node-c}/{data,logs}权限问题很关键。ES 容器默认以 uid 1000 的用户运行宿主机挂载目录如果归属 root容器内无法写入。解决方式是直接 chown 给 uid 1000chown -R 1000:1000 /data/elasticsearch如果你不确定 uid可以先 docker run 一个临时容器看 id也可以直接加上 user 参数强制指定。我个人习惯直接 chown 1000简单粗暴在多数 Linux 发行版上没问题。3.3 编写 elasticsearch.yml 配置ES 的关键配置集中在 elasticsearch.yml。三个节点的配置略有差异主要是 node.name 和 network.host 不同。下面贴出节点 A 的配置其余节点替换节点名和 IP 就行。cluster.name: es-docker-cluster node.name: es-master-a node.master: true node.data: true network.host: 0.0.0.0 http.port: 9200 transport.port: 9300 discovery.seed_hosts: [node-a-ip:9300, node-b-ip:9300, node-c-ip:9300] cluster.initial_master_nodes: [es-master-a, es-master-b, es-data-c] path.data: /usr/share/elasticsearch/data path.logs: /usr/share/elasticsearch/logs三个节点的 node.name 分别设成 es-master-a、es-master-b、es-data-ccluster.initial_master_nodes 里三个名字都要列上。首次启动时ES 靠这个配置完成初始 master 选举。gateway.recover_after_nodes 参数我建议加到配置里作用是当集群节点数恢复到设定值时再开始分片恢复。如果集群意外宕机重启时所有节点同时恢复数据分片不 rush恢复过程更平滑gateway.recover_after_nodes: 2 gateway.recover_after_time: 5m gateway.expected_nodes: 3这段配置的含义是集群至少 2 个节点在线且等待 5 分钟或者达到预期 3 节点时才启动分片分配。表达稍显冗余但实际效果是给数据恢复留缓冲避免全部节点同时恢复导致 IO 飙升。3.4 Docker Compose 编排启动我习惯用 Docker Compose 管理多节点编排比逐个 docker run 清晰。写一个 docker-compose.yml三段服务对应三个节点。version: 3.8 services: es-node-a: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.0 container_name: es-node-a hostname: es-node-a environment: - ES_JAVA_OPTS-Xms4g -Xmx4g ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 volumes: - /data/elasticsearch/node-a/data:/usr/share/elasticsearch/data - /data/elasticsearch/node-a/logs:/usr/share/elasticsearch/logs - /data/elasticsearch/conf/node-a.yml:/usr/share/elasticsearch/config/elasticsearch.yml ports: - 192.168.1.10:9200:9200 - 192.168.1.10:9300:9300 networks: - es-net es-node-b: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.0 container_name: es-node-b hostname: es-node-b environment: - ES_JAVA_OPTS-Xms4g -Xmx4g ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 volumes: - /data/elasticsearch/node-b/data:/usr/share/elasticsearch/data - /data/elasticsearch/node-b/logs:/usr/share/elasticsearch/logs - /data/elasticsearch/conf/node-b.yml:/usr/share/elasticsearch/config/elasticsearch.yml ports: - 192.168.1.11:9200:9200 - 192.168.1.11:9300:9300 networks: - es-net es-node-c: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.0 container_name: es-node-c hostname: es-node-c environment: - ES_JAVA_OPTS-Xms4g -Xmx4g ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 volumes: - /data/elasticsearch/node-c/data:/usr/share/elasticsearch/data - /data/elasticsearch/node-c/logs:/usr/share/elasticsearch/logs - /data/elasticsearch/conf/node-c.yml:/usr/share/elasticsearch/config/elasticsearch.yml ports: - 192.168.1.12:9200:9200 - 192.168.1.12:9300:9300 networks: - es-net networks: es-net: driver: bridge说一下几个环境变量的含义ES_JAVA_OPTS设置 JVM 堆内存。生产环境建议设为物理内存的一半且 Xms 与 Xmx 保持一致。我每台机器 16GB 内存ES 分配 4GB剩下留给操作系统 page cache 和其他进程。堆内存不是越大越好超过 32GB 时 JVM 的压缩指针失效内存利用率反而下降。ulimits.memlock锁定内存防止 ES 堆内存被交换到磁盘导致性能骤降。ES 官方建议启用 bootstrap.memory_lock。配合 docker-compose 里设置 memlock 为 -1并在 elasticsearch.yml 里加 bootstrap.memory_lock: true实测效果很稳。nofile文件描述符上限ES 的数据文件很多并发查询时文件打开数容易超过默认值设置 65536 是官方建议值。network.host: 0.0.0.0 表示监听所有网卡配合端口映射把 9200 和 9300 暴露到宿主机 IP 上。这样三个节点之间可以通过宿主机的 IP 互相通信。9300 是节点间传输端口9200 是 HTTP 访问端口。3.5 启动前检查与启动命令启动前我习惯先 validate 一下 Compose 文件docker compose config这个命令会检查 YAML 语法错误如果哪里格式不对会直接报出来。确认没问题后启动docker compose up -d启动后立刻看日志docker logs -f es-node-a首次启动会有初始化过程看到类似以下输出说明正在组集群[INFO ] [o.e.c.s.MasterService] [es-master-a] zen-disco-node-join[{es-master-b}{...}] [INFO ] [o.e.c.s.ClusterApplierService] [es-master-a] master node changed {previous [], current [{es-master-a}{...}]}说明节点 B 加入进来了master 选举成功。如果日志一直卡在等待节点大概率是 discovery.seed_hosts 填错了 IP或者防火墙没放行 9300 端口。4. 集群验证与可视化接入4.1 检查集群健康状态启动完成后先看集群是否正常。访问任意节点的 9200 端口curl http://192.168.1.10:9200/_cluster/health?pretty正常返回的 status 是 green表示所有主分片和副本分片都已分配。如果返回 yellow说明副本分片未分配最常见原因是副本数量设置超过节点可用数比如单节点集群上副本数设为 1。还要检查节点列表curl http://192.168.1.10:9200/_cat/nodes?v能列出三个节点、各自的角色、堆内存使用情况说明集群组起来了。4.2 查看分片分配情况分片分配是否均匀直接影响查询性能用这个命令看curl http://192.168.1.10:9200/_cat/shards?v我当时建了一个测试索引设了 3 个主分片、1 个副本三节点环境下分片应该分散在不同节点比如某个索引的分片可能分布在下表这种模式索引分片状态节点test_index0STARTEDes-master-atest_index0STARTEDes-data-c 副本test_index1STARTEDes-master-btest_index1STARTEDes-master-a 副本test_index2STARTEDes-data-ctest_index2STARTEDes-master-b 副本分片分布均匀写入和查询才能分散到三个节点共同分担。4.3 接入 Kibana 可视化集群跑起来后我还用 Docker 起了一个 Kibana 7.6.0统一版本很重要版本不一致会导致 Kibana 连不上 Elasticsearch或是页面数据加载异常。docker run -d \ --name kibana \ -p 5601:5601 \ -e ELASTICSEARCH_HOSTShttp://192.168.1.10:9200 \ docker.elastic.co/kibana/kibana:7.6.0访问 http://192.168.1.10:5601 就能打开 Kibana 页面。在 Dev Tools 里执行查询能直观看到集群返回的数据。5. 实操中的常见问题与排查技巧5.1 内存锁失败memory locking requested but locked memory limit is too low这个报错的意思是 ES 想锁定内存但系统给容器的锁定内存上限太低。排查路径检查 docker-compose 里是否设置了 ulimits memlock -1检查宿主机的 /etc/security/limits.conf 是否允许 docker 进程锁定足够内存检查 elasticsearch.yml 中 bootstrap.memory_lock 是否为 true我当时遇到的问题出在 docker-compose 没生效因为用的是 docker-compose 旧版本ulimits 的写法不支持。后面升级到新版或者改用 docker run 加 --ulimit memlock-1问题解决。5.2 节点互相找不到三台机器上各自启动了容器但 cluster 里始终只有自己。排查步骤按优先级来先确认 transport 端口 9300 在宿主机防火墙是放行的。我在内网环境遇到过几次安全组规则把 9300 忘了开导致节点间无法通信。确认 discovery.seed_hosts 填的是宿主机 IP 而不是容器 IP。如果填 172.17.0.x 这种容器 IP容器重建后地址就变了。确认三个节点的 cluster.name 完全一致ES 集群只接受同名集群的节点加入。5.3 分片未分配UNASSIGNED 问题集群状态 yellow 时重点关注未分配的分片。排查命令curl http://192.168.1.10:9200/_cluster/allocation/explain?pretty这个接口会直接告诉你是哪个分片未分配、为什么未分配。常见原因如下表原因解决方案副本数大于可分配节点数调低副本数或加节点数磁盘空间不足低于 watermark清理旧索引或加磁盘分片数据损坏删除损坏副本重新分配节点数低于 gateway.recover_after_nodes等待节点全部上线在日志场景里磁盘水位是最常见的分片未分配原因。ES 默认在磁盘使用率超过 85% 时不再分配新分片超过 90% 时尝试迁移分片。如果你的数据盘快要满了建议定期用索引生命周期管理删掉过期索引。5.4 扩容与缩容技巧集群跑一段时间后要加节点操作步骤很简单新机器上准备目录、配置和启动容器在 elasticsearch.yml 里把新节点加到 discovery.seed_hosts启动容器等待节点自动加入集群不需要重启旧节点ES 会自动发现新节点我这里有个温馨提示如果你修改了集群的初始 master 配置千万别在所有节点上同时重启否则会触发脑裂。按顺序滚动重启一个节点起来并稳定后再操作下一个。5.5 关于脑裂的详细预防ES 的脑裂原因是节点间网络不通但各自以为自己是 master。7.6.0 的选举机制已经比 2.x 时代成熟很多但预防原则依然不变设置 minimum_master_nodes 节点数7.x 之后系统会自动根据集群规模计算一般不需要手动设置但如果你自定义了建议至少为候选节点数一半加一。master 候选节点设置为奇数3 个或 5 个避免平票不要随意给所有节点开启 node.master: true否则网络抖动时选举难度增大5.6 性能调优三板斧集群稳定运行只是第一步性能调优才是长期课题。我总结最有价值的三条第一索引分片数根据数据量动态调整。7.6.0 默认一个分片 50GB 左右比较合适日志场景下超过 100GB 再考虑拆分索引比如按天建索引索引模板里设好分片数和副本数。第二写入性能卡在 replica 上。索引的副本数设为 1 是底线如果允许短时间数据丢失风险可以临时设为 0大批量导入完成后再改回 1。我做过峰值约 2 倍的写入提速实验把副本临时归零的效果很明显。第三查询慢先看 filter cache 和 fielddata。ES 的 filter cache 默认不限制内存如果查询里用到了大量 filter建议设置 indices.requests.cache.size 为堆内存的 2%。fielddata 在 7.x 默认是关闭的如果因为排序或聚合需要开启要严格控制大小否则 OOM 分分钟。6. 日常运维的监控与备份策略6.1 监控指标Docker 方式部署的 ES除了常规的 CPU、内存监控还需要关注 JVM 层面的指标。Elasticsearch 自带的 _cat/thread_pool 接口可以看到各个线程池的状态curl http://192.168.1.10:9200/_cat/thread_pool/write?v如果 write 线程池出现大量 rejected 计数说明写入压力过大需要扩容或者优化批量写入参数。另外一个常用的是 _nodes/stats 接口可以看到每个节点的堆内存使用、GC 次数和耗时curl http://192.168.1.10:9200/_nodes/stats/jvm?pretty观察到 Full GC 频繁且耗时长说明堆内存设置不合理或者内存泄漏。ES 集群最怕的就是 Full GC 引起的节点失联尽量避免堆内存占比超过物理内存一半。6.2 数据备份的推荐做法Docker 方式下最直接的备份方式是直接备份挂载的数据目录但这个方式有隐患——如果备份期间有新数据写入数据目录处于不一致状态恢复时可能丢数据。推荐用快照方式备份。在 elasticsearch.yml 里配置快照仓库路径path.repo: /usr/share/elasticsearch/backup然后把宿主机目录挂载进容器用 API 创建快照curl -X PUT http://192.168.1.10:9200/_snapshot/my_backup -H Content-Type: application/json -d {type: fs, settings: {location: /usr/share/elasticsearch/backup}}创建快照curl -X PUT http://192.168.1.10:9200/_snapshot/my_backup/snapshot_20240101?wait_for_completiontrue快照的优点是支持增量备份只备份变化的数据块对磁盘和服务性能的影响都小于直接拷贝数据目录。恢复操作三个节点同时执行也可以快照机制自己会协调。7. 写在最后的实战体会这套 Docker 化 ES 集群方案从落地到现在已经平稳运行了小半年。中间经历过两次宿主机重启、一次磁盘告警集群都靠副本和分片自动恢复机制扛住了。和裸机部署相比容器化带来的最大收益不是性能提升而是部署和扩容的确定性——同样的启动参数、同样的挂载目录在任何一台新机器上都能复制出完全一样的运行环境。这个特性对多机房扩容来说太宝贵了。最后再分享一个小技巧如果你需要频繁调整 ES 配置或者测试不同版本建议把 elasticsearch.yml 放到独立目录用 -v 挂载然后针对不同版本建不同 Compose 文件目录名带上版本号。这样做的好处是升级版本时可以保留老版本数据目录不动新版本挂新的数据目录有问题随时切回旧版本回滚成本几乎为零。别嫌麻烦生产环境多一份保险心里就多一分踏实。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑