【实战记录】Docker Compose 单机部署 EMQX 3 节点集群(5.8.8 社区版)完整踩坑与解决方案

一、背景与目标

在做 MQTT 架构 PoC / 技术验证 时,常见需求包括:

  • 同一台服务器 上部署 EMQX 集群

  • 使用 Docker Compose

  • 不使用企业版、不引入 License

  • 验证:

    • 集群可用性
    • 后续 TLS / 认证 / Topic 设计

本文记录一次 完整、真实的实战过程 ,以及最终稳定可复现的解决方案


二、结论先行(重要)

如果你不使用企业版、不配置 License,又需要多节点集群:

请使用 EMQX 5.8.x(如 5.8.8)

不要使用 5.9.0 及以上版本

原因见下文 License 章节。


三、核心问题与踩坑总结

1️⃣ EMQX 5.9.0+ 的 License 行为变化(关键)

EMQX v5.9.0 开始

  • EMQX 切换到 BSL 1.1(商业源许可证)
  • 即使使用 emqx/emqx(社区镜像)
  • 在技术层面限制多节点启动

当你尝试多节点集群时,日志会直接报错并退出:

text 复制代码
SINGLE_NODE_LICENSE,
make sure this node and peer nodes are configured with a valid license

📌 结论

版本 是否可无 License 集群
5.8.x ✅ 可以
5.9.0+ ❌ 不可以

2️⃣ Docker + Erlang 集群的经典大坑

EMQX 集群底层是 Erlang 分布式系统,对节点名要求非常严格:

❌ 常见错误方式
  • 使用 hostname
  • 使用 Docker DNS alias
  • 使用宿主机 IP
  • 使用 FQDN / 不一致的 host

这些都会导致典型错误:

text 复制代码
Node emqx1@xxx not responding to pings

甚至连:

bash 复制代码
emqx eval 'node().'

都会失败。


3️⃣ 正确、稳定、最少踩坑的方式(结论)

在 Docker Compose 同机部署场景中:

✅ 使用 Docker 内部固定 IP

❌ 不依赖 hostname / DNS

这是最稳妥、最容易复现的方案。


四、最终方案设计

技术选型

  • EMQX:5.8.8
  • 部署方式:Docker Compose
  • 节点数:3
  • 网络:Docker bridge + 固定 IP
  • 数据:本地目录持久化

五、最终目录结构

text 复制代码
.
├── docker-compose.yml
├── emqx1
│   ├── data
│   └── log
├── emqx2
│   ├── data
│   └── log
└── emqx3
    ├── data
    └── log

六、完整 docker-compose.yml(可直接使用)

yaml 复制代码
services:
  emqx1:
    image: emqx/emqx:5.8.8
    container_name: emqx1
    restart: unless-stopped
    networks:
      emqx-net:
        ipv4_address: 172.31.255.11
    environment:
      EMQX_NODE__NAME: emqx1@172.31.255.11
      EMQX_NODE__COOKIE: emqxclustersecret
      EMQX_CLUSTER__DISCOVERY_STRATEGY: static
      EMQX_CLUSTER__STATIC__SEEDS: emqx1@172.31.255.11,emqx2@172.31.255.12,emqx3@172.31.255.13
    volumes:
      - ./emqx1/data:/opt/emqx/data
      - ./emqx1/log:/opt/emqx/log
    ports:
      - "1883:1883"
      - "18083:18083"

  emqx2:
    image: emqx/emqx:5.8.8
    container_name: emqx2
    restart: unless-stopped
    networks:
      emqx-net:
        ipv4_address: 172.31.255.12
    environment:
      EMQX_NODE__NAME: emqx2@172.31.255.12
      EMQX_NODE__COOKIE: emqxclustersecret
      EMQX_CLUSTER__DISCOVERY_STRATEGY: static
      EMQX_CLUSTER__STATIC__SEEDS: emqx1@172.31.255.11,emqx2@172.31.255.12,emqx3@172.31.255.13
    volumes:
      - ./emqx2/data:/opt/emqx/data
      - ./emqx2/log:/opt/emqx/log

  emqx3:
    image: emqx/emqx:5.8.8
    container_name: emqx3
    restart: unless-stopped
    networks:
      emqx-net:
        ipv4_address: 172.31.255.13
    environment:
      EMQX_NODE__NAME: emqx3@172.31.255.13
      EMQX_NODE__COOKIE: emqxclustersecret
      EMQX_CLUSTER__DISCOVERY_STRATEGY: static
      EMQX_CLUSTER__STATIC__SEEDS: emqx1@172.31.255.11,emqx2@172.31.255.12,emqx3@172.31.255.13
    volumes:
      - ./emqx3/data:/opt/emqx/data
      - ./emqx3/log:/opt/emqx/log

networks:
  emqx-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.255.0/24

七、启动步骤(严格按顺序)

bash 复制代码
# 创建目录
mkdir -p emqx1/{data,log} emqx2/{data,log} emqx3/{data,log}
chmod -R 777 emqx1 emqx2 emqx3

# 停止旧容器
docker compose down

# 清空旧数据(避免 5.9.x 残留)
rm -rf emqx1/data/* emqx2/data/* emqx3/data/*

# 启动
docker compose up -d

八、验证步骤

1️⃣ 容器状态

bash 复制代码
docker ps | grep emqx

应看到 3 个 Up


2️⃣ Erlang 节点验证(关键)

bash 复制代码
docker exec -it emqx1 emqx eval 'node().'
docker exec -it emqx2 emqx eval 'node().'
docker exec -it emqx3 emqx eval 'node().'

输出示例:

text 复制代码
emqx1@172.31.255.11
emqx2@172.31.255.12
emqx3@172.31.255.13

3️⃣ 集群状态

bash 复制代码
docker exec -it emqx1 emqx ctl cluster status

期望结果:

text 复制代码
running_nodes =>
  emqx1@172.31.255.11
  emqx2@172.31.255.12
  emqx3@172.31.255.13

九、Dashboard 访问

text 复制代码
http://宿主机IP:18083

默认账号:

text 复制代码
admin / public

十、经验总结(非常重要)

✅ 推荐做法

  • 使用 EMQX 5.8.x
  • Docker 内部 IP 做节点名
  • 显式指定 Erlang Cookie
  • 清理 data 再切版本

❌ 不推荐做法

  • 5.9.0+ 社区版无 License 集群
  • 使用宿主机 IP 作为节点名
  • 使用 hostname / DNS alias
  • 版本切换不清 data

十一、下一步可做的事情

在此集群基础上,后续可以非常顺利地扩展:

  1. MQTT 双向 TLS(设备证书)
  2. HAProxy / Nginx MQTT 负载均衡
  3. EMQX 集群监控(Prometheus)
  4. 设备侧 Netty → MQTT 迁移

十二、结语

这次实践的最大价值不在于"搭起来了",而在于:

踩过了 EMQX + Docker + Erlang + License 的所有关键坑

后续无论是上云、上 K8s、还是切企业版,这套认知都会持续受用。

相关推荐
weixin_462446232 小时前
【原创实践】使用 Docker 在 MySQL 容器中批量导出所有数据库
数据库·mysql·docker
l1t2 小时前
docker 启动 oracle容器报1521: bind: address already in use错误的解决
docker·oracle·容器
叫致寒吧2 小时前
Docker资源限制 与数据卷
linux·运维·docker
GISer_CV攻城狮2 小时前
MapLibre/Martin 地图服务器docker化安装部署
运维·服务器·docker
tzhou644522 小时前
Docker核心功能解析:网络、资源控制、数据卷与镜像构建
网络·docker·eureka
朱 欢 庆2 小时前
在docker容器里 使用Jenkins部署前端项目
前端·经验分享·docker·jenkins
weixin_46683 小时前
k8s-持久化存储
云原生·容器·kubernetes
原神启动115 小时前
Docker 场景化作业:生产环境容器操作实训
运维·docker·容器
呼啦啦呼啦啦啦啦啦啦16 小时前
docker制作镜像的两种方式(保姆级教学)
运维·docker·容器