Elasticsearch集群Compose部署正常但Swarm stack部署报master_not_discovered_exception
问题原因
- Swarm 服务发现模式不兼容:Docker Swarm 中 overlay 网络默认的 endpoint 模式为
vip(虚拟IP负载均衡),你配置的discovery.seed_hosts=es02,es03解析后得到的是服务的负载均衡 VIP,而非单个 ES 容器的实际地址,ES 节点间无法正确识别对等节点身份,无法完成主节点选举,因此抛出master_not_discovered_exception异常。 - 网络配置冲突:你将
elastic网络设置为internal: true(仅允许集群内服务互访,禁止对外通信),但同时给 es01 配置了 9200 端口的宿主机映射,该配置会冲突导致端口映射失效,也会间接影响跨节点的容器互访。 - 缺少必要的系统配置与资源限制:ES 要求宿主机内核参数
vm.max_map_count不低于 262144,Swarm 集群所有工作节点都需要提前配置该参数,否则 ES 容器会启动失败,无法凑够选举主节点的法定人数。另外配置缺失 ulimit 限制,ES 运行需要足够的文件描述符权限,Swarm 部署时默认的 ulimit 阈值无法满足 ES 运行要求。 - 无调度约束:默认情况下 Swarm 可能将多个 ES 实例调度到同一个物理节点,本地存储卷、资源不足等问题都会导致 ES 实例启动失败。
修复方案
- 前置操作:在 Swarm 集群的所有工作节点执行以下命令配置内核参数,永久生效需要写入
/etc/sysctl.conf:
sysctl -w vm.max_map_count=262144 echo "vm.max_map_count=262144" >> /etc/sysctl.conf
- 修改 Docker Compose 配置,核心调整点如下:
- 所有 ES 服务新增
endpoint_mode: dnsrr,使用 DNS 轮询模式替代默认 VIP 模式,服务名解析直接返回所有后端容器 IP - 移除
elastic网络的internal: true配置(如果需要严格隔离可额外新增一张非 internal 网络用于暴露端口) - 给 ES 服务新增 ulimit 配置,满足 ES 运行的文件描述符要求
- 新增部署调度约束,保证每个 ES 实例调度到不同的 Swarm 节点
修改后的完整配置如下:
version: '3.4' services: es01: image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2 hostname: es01 environment: - node.name=es01 - cluster.name=es-docker-cluster - discovery.seed_hosts=es02,es03 - cluster.initial_master_nodes=es01,es02,es03 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65535 hard: 65535 volumes: - data01:/usr/share/elasticsearch/data ports: - 9200:9200 networks: - elastic deploy: mode: replicated replicas: 1 endpoint_mode: dnsrr placement: constraints: - node.labels.es_node == true # 可提前给Swarm节点打标签,也可替换为其他约束规则 es02: image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2 hostname: es02 environment: - node.name=es02 - cluster.name=es-docker-cluster - discovery.seed_hosts=es01,es03 - cluster.initial_master_nodes=es01,es02,es03 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65535 hard: 65535 volumes: - data02:/usr/share/elasticsearch/data networks: - elastic deploy: mode: replicated replicas: 1 endpoint_mode: dnsrr placement: constraints: - node.labels.es_node == true es03: image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2 hostname: es03 environment: - node.name=es03 - cluster.name=es-docker-cluster - discovery.seed_hosts=es01,es02 - cluster.initial_master_nodes=es01,es02,es03 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65535 hard: 65535 volumes: - data03:/usr/share/elasticsearch/data networks: - elastic deploy: mode: replicated replicas: 1 endpoint_mode: dnsrr placement: constraints: - node.labels.es_node == true kib01: image: docker.elastic.co/kibana/kibana:7.15.2 hostname: kib01 ports: - 5601:5601 environment: ELASTICSEARCH_URL: http://es01:9200 ELASTICSEARCH_HOSTS: '["http://es01:9200","http://es02:9200","http://es03:9200"]' networks: - elastic deploy: replicas: 1 volumes: data01: driver: local data02: driver: local data03: driver: local networks: elastic: driver: overlay
- 集群首次启动正常后,后续更新或重启部署时,需要删除所有 ES 服务的
cluster.initial_master_nodes环境变量,避免重复执行初始化逻辑导致集群脑裂。
内容的提问来源于stack exchange,提问作者Ravi Soni
相关产品推荐
相关产品推荐

