Elasticsearch从0-1部署成功实战

[复制链接]
发表于 3 天前 | 显示全部楼层 |阅读模式
Elasticsearch 从 0 到 1 部署上线全攻略:从架构计划到踩坑复盘

不是装个 docker run 就叫部署。真正的从 0 到 1,是把高可用架构、安全认证、索引计划、数据同步、性能调优、监控监控告警这条链路全部走通,并沉淀成团队的尺度部署 SOP。
这篇文章,我联合自己生产环境搭建 3 节点 ES 集群的实战经验,从架构选型 → 环境调优 → 集群部署 → 索引计划 → 数据接入 → 性能加固 → 踩坑复盘七个阶段展开,全程贴合生产落地尺度。不管你是准备面试,照旧真要上手干活,这篇都能帮你少走弯路。
一、整体部署流程全景

先上一张全景图,心里有个数,后面每一步都在这个框架里填肉:

二、架构规划:3 节点起步,要能扛住生产

2.1 硬件选型(生产尺度)

环境类型CPU内存磁盘类型节点数实用场景开发测试4C8GSATA SSD1功能验证生产集群16C32GNVMe SSD3台起业务承载
别在磁盘上省钱。ES 是 I/O 密集型应用,NVMe SSD 和 SATA SSD 的随机读写差距可以到 5-10 倍,直接影响搜索延迟和写入吞吐。
2.2 集群架构图

生产最小可用集群,我一般这样规划:

2.3 三个关键决议点

1. 主节点要不要独立?
小规模集群(3-5 节点),Master 和 Data 可以复用。但集群规模一大(>10 节点),务必把 Master 节点独立出来(node.roles: [master])。否则 Master 被 GC 卡顿拖住,整个集群的选主和状态管理都会出问题。
2. 冷热分离怎么做?
通过节点标签 node.attr.box_type: hot/warm 区分,热数据放 SSD,冷数据放 HDD。再配合 ILM(索引生命周期管理)自动迁移,日志日志类数据可以省 50% 以上的存储本钱。
3. 脑裂怎么防?
这是面试高频考点,也是生产真会遇到的问题:

  • 主节点数量保持奇数(3 或 5),保证投票不会出现平票
  • 7.x+ 通过 cluster.initial_master_nodes 规范首次选举,之后自动管理
  • 避免跨机房部署主节点,网络分区是脑裂的头号元凶
关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包罗大厂高频面试题、源码剖析和性能调优案例。
关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。
三、系统调优:部署前的必修课

80% 的 ES 启动失败,都是系统参数没调好。这一步别偷懒。
3.1 关闭 Swap

ES 极度依赖内存,Swap 交换会导致查询性能骤降,甚至节点假死。
  1. swapoff -a
  2. # 永久关闭:注释 /etc/fstab 中 swap 分区行
复制代码
3.2 调高虚拟内存映射数

ES 大量使用内存映射文件(mmap),默认值 65530 不敷用,直接启动失败。
  1. echo "vm.max_map_count=262144" >> /etc/sysctl.conf
  2. sysctl -p
复制代码
3.3 调高文件句柄数

ES 会打开大量索引文件,默认 1024 完全无法满足生产需求。
  1. echo "* soft nofile 65536" >> /etc/security/limits.conf
  2. echo "* hard nofile 65536" >> /etc/security/limits.conf
复制代码
实战建议:把上面三步写成一个 init-es-os.sh 脚本,纳入运维尺度化流程。每次新机部署先跑一遍,省得踩坑。
四、集群部署:Docker-Compose 一键拉起

4.1 版本选择

推荐 7.17.x(生产主流 LTS 版本),兼容性和稳固性最佳。JDK 用 ES 自带的就行,不消额外安装,避免版本兼容问题。
4.2 焦点配置 elasticsearch.yml
  1. # 集群名称:同集群所有节点必须一致
  2. cluster.name: es-prod-cluster
  3. # 节点名称:建议与主机名对应,便于故障排查
  4. node.name: es-node-1
  5. # 节点角色:生产建议分离,测试可兼用
  6. node.roles: [master, data]
  7. # 绑定网卡:生产绑定内网 IP,禁止 0.0.0.0 直接暴露公网
  8. network.host: 192.168.1.101
  9. http.port: 9200
  10. # 集群发现:种子节点列表,填写所有节点 IP
  11. discovery.seed_hosts: ["192.168.1.101", "192.168.1.102", "192.168.1.103"]
  12. # 初始主节点:仅首次集群启动配置,集群形成后必须删除!
  13. cluster.initial_master_nodes: ["es-node-1", "es-node-2", "es-node-3"]
  14. # 数据与日志日志路径:禁止放在系统盘
  15. path.data: /data/es/data
  16. path.logs: /data/es/logs
复制代码
踩坑提醒:cluster.initial_master_nodes 只在集群首次启动时使用。集群成功组建后,肯定要从配置中移除这一项,否则节点重启时大概引发异常选举。
4.3 JVM 内存配置(技能亮点)
  1. # config/jvm.options
  2. # 堆内存设置为相同值,避免堆扩容缩容带来的性能抖动
  3. -Xms16g
  4. -Xmx16g
复制代码
这里有个面试必问的知识点:堆内存不超过物理内存的 50%,且绝对不超过 32G。
为什么?因为 JVM 的 Compressed Oops(压缩对象指针) 在 32G 以内生效。一旦超过 32G,指针从 32 位变成 64 位,同样的数据量占用内存反而增加 10%-20%。所以 32G 是 ES 堆内存的"黄金分割线"。
4.4 Docker-Compose 编排(3 节点)
  1. version: '3.7'
  2. services:
  3.   es01:
  4.     image: docker.elastic.co/elasticsearch/elasticsearch:7.17.6
  5.     container_name: es01
  6.     environment:
  7.       - node.name=es01
  8.       - cluster.name=prod-es-cluster
  9.       - discovery.seed_hosts=es02,es03
  10.       - cluster.initial_master_nodes=es01,es02,es03
  11.       - "ES_JAVA_OPTS=-Xms8g -Xmx8g"
  12.       - node.attr.box_type=hot
  13.     volumes:
  14.       - ./es01/data:/usr/share/elasticsearch/data
  15.       - ./es01/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
  16.     ports:
  17.       - 9200:9200
  18.     networks:
  19.       - es-net
  20.   es02:
  21.     image: docker.elastic.co/elasticsearch/elasticsearch:7.17.6
  22.     container_name: es02
  23.     environment:
  24.       - node.name=es02
  25.       - cluster.name=prod-es-cluster
  26.       - discovery.seed_hosts=es01,es03
  27.       - cluster.initial_master_nodes=es01,es02,es03
  28.       - "ES_JAVA_OPTS=-Xms8g -Xmx8g"
  29.       - node.attr.box_type=warm
  30.     volumes:
  31.       - ./es02/data:/usr/share/elasticsearch/data
  32.     networks:
  33.       - es-net
  34.   es03:
  35.     image: docker.elastic.co/elasticsearch/elasticsearch:7.17.6
  36.     container_name: es03
  37.     environment:
  38.       - node.name=es03
  39.       - cluster.name=prod-es-cluster
  40.       - discovery.seed_hosts=es01,es02
  41.       - cluster.initial_master_nodes=es01,es02,es03
  42.       - "ES_JAVA_OPTS=-Xms8g -Xmx8g"
  43.     volumes:
  44.       - ./es03/data:/usr/share/elasticsearch/data
  45.     networks:
  46.       - es-net
  47. networks:
  48.   es-net:
  49.     driver: bridge
复制代码
五、索引计划:准确 Mapping + 别名无缝切换

很多新人直接往 ES 灌数据,keyword 和 text 不分,效果聚合慢、排序错。这一步必须严谨。
5.1 创建商品索引(技能亮点)
  1. PUT /goods_v1
  2. {
  3.   "settings": {
  4.     "number_of_shards": 3,
  5.     "number_of_replicas": 1,
  6.     "refresh_interval": "30s",
  7.     "analysis": {
  8.       "analyzer": {
  9.         "ik_smart_pinyin": {
  10.           "type": "custom",
  11.           "tokenizer": "ik_smart",
  12.           "filter": ["lowercase"]
  13.         }
  14.       }
  15.     }
  16.   },
  17.   "mappings": {
  18.     "properties": {
  19.       "goodsId":     { "type": "keyword" },
  20.       "title":       {
  21.         "type": "text",
  22.         "analyzer": "ik_max_word",
  23.         "fields": {
  24.           "pinyin": { "type": "text", "analyzer": "ik_smart_pinyin" }
  25.         }
  26.       },
  27.       "price":       { "type": "scaled_float", "scaling_factor": 100 },
  28.       "createTime":  { "type": "date" },
  29.       "tags":        { "type": "keyword" }
  30.     }
  31.   }
  32. }
复制代码
为什么这样计划?
字段类型选择计划理由goodsIdkeyword不参与分词,适合准确匹配和聚合排序titletext + ik_max_word全文检索,细粒度分词提升召回率title.pinyin子字段 + 拼音分词器支持"输入拼音搜商品"的场景pricescaled_float比 double 省空间,精度可控(分转元)tagskeyword标签准确匹配,用于过滤聚合5.2 别名策略:零停机切换的秘诀

索引以 _v1 结尾,配合别名 goods 对外暴露。后续重建索引时,只需将别名指向新索引 goods_v2,业务代码零修改,实现零停机切换。
  1. POST /_aliases
  2. {
  3.   "actions": [
  4.     { "remove": { "index": "goods_v1", "alias": "goods" } },
  5.     { "add":    { "index": "goods_v2", "alias": "goods" } }
  6.   ]
  7. }
复制代码
这个技巧在 Reindex(重建索引)、Mapping 变更、版本升级等场景下非常好用,是生产环境的标配利用。
六、数据接入:MySQL 到 ES 的高可靠同步方案

数据来源是 MySQL,放弃简单的 Logstash 全量同步,选择 Canal + MQ + 自定义消耗者 的异步链路,保证最终一致性。
6.1 数据流架构


6.2 Java 焦点代码:批量消耗 + 去重写入
  1. @KafkaListener(topics = "goods_binlog")
  2. public void consume(List<ConsumerRecord<String, String>> records) {
  3.     BulkRequest bulkRequest = new BulkRequest();
  4.     for (ConsumerRecord<String, String> record : records) {
  5.         GoodsDTO goods = JSON.parseObject(record.value(), GoodsDTO.class);
  6.         // 使用 goodsId 作为文档 ID,幂等写入
  7.         IndexRequest request = new IndexRequest("goods")
  8.                 .id(goods.getGoodsId())
  9.                 .source(JSON.toJSONString(goods), XContentType.JSON);
  10.         bulkRequest.add(request);
  11.     }
  12.     // 单批次 1000 条,超时 2 分钟
  13.     BulkResponse response = restHighLevelClient.bulk(bulkRequest,
  14.         RequestOptions.DEFAULT);
  15.     // 失败重试或记录死信
  16.     if (response.hasFailures()) {
  17.         log.error("ES bulk error: {}", response.buildFailureMessage());
  18.         // 写入重试队列
  19.     }
  20. }
复制代码
三个关键计划:

  • 次序保证:Canal 保证 binlog 次序,单分区 Kafka 避免乱序,消耗者单线程拉取,简化逻辑
  • 幂等写入:使用业务 ID(goodsId)作为 ES 文档 ID,天然幂等,不怕重复消耗
  • 熔断降级:ES 写入超时或拒绝时,立刻暂停消耗,熔断肯定时间后恢复,防止拖垮整条链路
七、安全加固 + 性能调优

7.1 安全加固(7.x 默认开启 XPack)
  1. # 批量设置内置用户密码
  2. bin/elasticsearch-setup-passwords interactive
  3. # 依次设置 elastic / kibana / logstash_system 等内置用户密码
复制代码
配置文件补充:
  1. xpack.security.enabled: true
  2. xpack.security.transport.ssl.enabled: true
复制代码
7.2 性能调优 Checklist

上线前逐项核对,这也是面试中最能体现经验的部分:
类别调优项落地方案JVM堆内存 ≤ 32G,且不超过物理内存 50%-Xms16g -Xmx16g,开启 G1GC,bootstrap.mlockall: true 锁内存OS文件形貌符 + 虚拟内存映射ulimit -n 65535,vm.max_map_count=262144索引按时间切分(如按天)利用 ILM 自动 rollover,删除过期索引写入增加 bulk 队列和线程池thread_pool.write.queue_size: 1000搜索禁用 wildcard 前缀通配符用 ngram 分词器代替,防止集群 OOM磁盘水位线告警low: 85% / high: 90% / flood_stage: 95%监控监控Prometheus + Grafana通过 elasticsearch_exporter 监控监控 GC 频率、搜索延迟、节点负载7.3 磁盘水位线配置

这个必须提前配,不然磁盘打满后 ES 会自动把索引设为只读,直接无法写入:
  1. cluster.routing.allocation.disk.watermark.low: 85%
  2. cluster.routing.allocation.disk.watermark.high: 90%
  3. cluster.routing.allocation.disk.watermark.flood_stage: 95%
复制代码
八、健康验证 + 冒烟测试

8.1 集群健康查抄
  1. # 查看集群健康状态
  2. curl -XGET http://192.168.1.101:9200/_cluster/health?pretty
  3. # 查看节点列表与角色
  4. curl -XGET http://192.168.1.101:9200/_cat/nodes?v
复制代码
集群状态三色灯:
状态寄义是否必要处理Green所有主分片 + 副本都正常分配统统正常Yellow主分片正常,副本未分配单节点必现,非故障;多节点需排查Red存在主分片丢失,数据缺失告急排查,大概有数据丢失风险8.2 冒烟测试
  1. # 1. 创建测试索引(3 主分片 1 副本)
  2. curl -XPUT -u elastic:密码 http://192.168.1.101:9200/test_demo \
  3.   -H 'Content-Type: application/json' -d '
  4. {
  5.   "settings": {
  6.     "number_of_shards": 3,
  7.     "number_of_replicas": 1
  8.   }
  9. }'
  10. # 2. 插入测试数据
  11. curl -XPOST -u elastic:密码 http://192.168.1.101:9200/test_demo/_doc/1 \
  12.   -H 'Content-Type: application/json' -d '
  13. {"name":"es部署测试","status":"success"}'
  14. # 3. 查询验证数据一致性
  15. curl -XGET -u elastic:密码 http://192.168.1.101:9200/test_demo/_doc/1?pretty
复制代码
写入 → 查询 → 数据一致,恭喜,集群已经可以正式接客了。
九、生产踩坑复盘:6 个真实场景

这是全文最值钱的部分。每一个都是从生产变乱里提炼出来的经验。
坑 1:集群脑裂(出现多个 Master)

征象:集群出现两个 Master 节点,数据写入不一致。
根因:网络分区导致多个节点自以为主。
解决方案:

  • 主节点数量保持奇数,生产用 3 台专用主节点
  • 7.x+ 通过 initial_master_nodes 规范首次选举
  • 避免跨机房部署主节点,网络延迟是隐形杀手
坑 2:启动直接报错退出

征象:ES 进程启动后几秒就挂掉。
根因:系统内核参数不满足(虚拟内存映射数、文件句柄数不敷)。
解决方案:部署前统一执行系统调优脚本,纳入运维尺度化流程。别手动一项一项配。
坑 3:磁盘打满后索引只读

征象:突然无法写入数据,报 cluster_block_exception。
根因:触发 flood_stage 水位线(95%),ES 自动锁索引掩护集群。
解决方案:

  • 扩容磁盘 / 清理过期索引
  • 接入磁盘使用率告警,提前预警
  • 解锁索引:PUT //_settings {"index.blocks.read_only_allow_delete": null}
坑 4:堆内存频繁 OOM

征象:JVM 堆内存打满,节点频繁 Full GC 甚至 OOM 退出。
根因:堆内存设置不合理,或大查询 / 聚合占用内存过高。
解决方案:

  • 严格服从堆内存 ≤ 32G 规范
  • 优化深分页(用 search_after 替代 from+size)、大聚合查询
  • 增加数据节点分散压力
坑 5:分片热点,节点负载不均

征象:促销时个别分片写入 QPS 极高,节点 CPU 100%。
根因:分片数不敷,或路由策略导致数据倾斜。
解决方案:

  • 提前用 _split API 将热点索引分片数从 3 扩到 6,分摊压力
  • 对商品 ID 取模定制路由,避免数据倾斜
  • 开启分片均衡感知策略
坑 6:长 GC 导致 Master 失联

征象:节点突然从集群中消失,集群状态反复 Yellow/Red 切换。
根因:GC 时间 > 30s,节点被集群判定为失联,触发重新选主。
解决方案:

  • 堆内存严格 30G 以内(压缩指针生效区间)
  • 使用 G1GC 并调优 -XX:MaxGCPauseMillis=200
  • 分离主节点脚色,数据节点的查询压力不影响主节点稳固性
十、上线后的持续保障

集群跑起来只是开始,长期稳固还必要这些能力:

总结

Elasticsearch 从 0 到 1 的部署,绝不是 docker run 一下就完事。它是一条完整的工程链路:

每一个环节都有坑,但每一个坑都有解法。把这篇文章里的配置、代码、踩坑经验吃透,不管是面试照旧实际落地,你都能交出一份漂亮的答卷。
末了说一句:技能博客千千万,能动手跑通的才算自己的。建议读者照着文中的步骤实操一遍,比看十篇文章都管用。
假如本文对你有资助,欢迎关注我的公众号【Rain的Java大神之路】。
专注 Java 面试、源码、高并发实战,回复“Java”领取《大厂面试手册》,持续更新。

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表