从零到三:手把手教你用Docker Compose滚动扩展EMQX 5.0集群(附扩容脚本)

当业务流量突然激增时,一个原本稳定的双节点EMQX集群可能瞬间变得捉襟见肘。上周我就遇到了这样的场景:凌晨三点收到告警,两个节点CPU长期维持在90%以上,消息积压超过百万条。这时候,快速、安全地扩展集群规模就成了救命稻草。

本文将分享一套经过生产验证的扩容方案,重点解决三个核心问题:

  1. 如何在不中断服务的情况下动态添加新节点
  2. 滚动更新SEEDS列表的正确顺序与时机
  3. 扩容后的数据一致性与负载均衡验证

1. 扩容前的关键检查

在开始扩容操作前,必须确保现有集群处于健康状态。这就像给行驶中的汽车更换轮胎,必须先确认车辆还能保持平衡。

集群健康检查命令:

# 检查集群节点状态
docker exec emqx-node emqx ctl cluster status

# 查看节点负载情况
docker exec emqx-node emqx ctl status

关键指标阈值参考:

指标 安全阈值 危险阈值 检查方法
CPU使用率 <70% ≥90% top -p $(pgrep beam)
内存使用 <75% ≥90% free -m
消息堆积数 <10万 ≥50万 Dashboard监控
节点间延迟 <10ms ≥50ms ping 其他节点IP

如果发现以下情况,建议先解决问题再扩容:

  • 任一节点状态为unreachable
  • 节点间延迟超过20ms
  • Mnesia数据库同步进度落后超过5分钟

2. 扩容操作全流程

2.1 准备新节点环境

新节点需要与现有集群完全一致的环境配置。这个脚本可以自动完成环境检查:

#!/bin/bash
# 新节点环境检查脚本 check_env.sh

# 检查Docker版本
DOCKER_VERSION=$(docker --version | awk '{print $3}' | tr -d ',')
if [[ "$DOCKER_VERSION" < "20.10" ]]; then
    echo "ERROR: Docker版本过低,需要20.10以上"
    exit 1
fi

# 检查Docker Compose版本
COMPOSE_VERSION=$(docker-compose --version | awk '{print $3}' | tr -d ',')
if [[ "$COMPOSE_VERSION" < "1.29" ]]; then
    echo "ERROR: Docker Compose版本过低,需要1.29以上"
    exit 1
fi

# 检查防火墙端口
PORTS=(4370 5370 1883 8883 18083)
for port in "${PORTS[@]}"; do
    if ! sudo iptables -L -n | grep -q ":${port}"; then
        echo "WARNING: 端口 $port 未开放,请检查防火墙规则"
    fi
done

echo "环境检查通过"

2.2 动态更新SEEDS列表

这是整个扩容过程中最关键的步骤。错误的更新顺序可能导致集群分裂。正确的滚动更新流程应该是:

  1. 首先更新所有现有节点的SEEDS列表
    在原有列表末尾追加新节点信息,例如:

    environment:
      - EMQX_CLUSTER__STATIC__SEEDS=emqx@192.168.1.100,emqx@192.168.1.101,emqx@192.168.1.102
    

    改为:

    environment:
      - EMQX_CLUSTER__STATIC__SEEDS=emqx@192.168.1.100,emqx@192.168.1.101,emqx@192.168.1.102,emqx@192.168.1.103
    
  2. 按以下顺序滚动重启节点

    • 先重启负载最低的节点
    • 每次间隔至少2分钟
    • 使用健康检查确认节点已恢复
  3. 最后启动新节点
    新节点的SEEDS列表必须包含自身地址

2.3 自动化扩容脚本

以下脚本可以自动完成从环境检查到加入集群的全过程:

#!/bin/bash
# 集群扩容脚本 scale_emqx.sh

# 配置区
NEW_NODE_IP="192.168.1.103"  # 新节点IP
EXISTING_NODES=("192.168.1.100" "192.168.1.101")  # 现有节点IP列表
COOKIE="YourStrongSecretCookie"
EMQX_VERSION="5.7.2"

# 生成新SEEDS列表
SEEDS_LIST=""
for node in "${EXISTING_NODES[@]}"; do
    SEEDS_LIST+="emqx@${node},"
done
SEEDS_LIST+="emqx@${NEW_NODE_IP}"

# 创建docker-compose.yml
cat > docker-compose.yml <<EOF
version: '3.8'
services:
  emqx-node:
    image: emqx/emqx:${EMQX_VERSION}
    container_name: emqx
    restart: always
    network_mode: "host"
    environment:
      - EMQX_NODE__NAME=emqx@${NEW_NODE_IP}
      - EMQX_NODE__COOKIE=${COOKIE}
      - EMQX_CLUSTER__DISCOVERY_STRATEGY=static
      - EMQX_CLUSTER__STATIC__SEEDS=${SEEDS_LIST}
    volumes:
      - ./data:/opt/emqx/data
      - ./log:/opt/emqx/log
EOF

# 准备目录
mkdir -p data log && chown -R 1000:1000 data log

# 启动服务
docker-compose up -d

echo "新节点${NEW_NODE_IP}已启动,等待加入集群..."
sleep 30

# 验证集群状态
if docker exec emqx emqx ctl cluster status | grep -q "${NEW_NODE_IP}"; then
    echo "√ 节点已成功加入集群"
else
    echo "× 节点加入集群失败,请检查日志"
    docker-compose logs -t --tail=50
    exit 1
fi

3. 扩容后验证

3.1 数据一致性检查

# 检查Mnesia表同步状态
docker exec emqx-node emqx eval 'mnesia:system_info(tables).'

# 对比各节点主题树
docker exec emqx-node emqx eval 'emqx_router:print().' | grep -c "topic:"

3.2 负载均衡测试

使用emqx_bench工具模拟客户端流量:

# 在新节点上启动发布者
./emqx_bench pub -h ${NEW_NODE_IP} -t load_test -c 100 -I 10

# 在旧节点上启动订阅者
./emqx_bench sub -h ${EXISTING_NODES[0]} -t load_test -c 100

观察指标变化:

节点IP 连接数 消息速率(msg/s) CPU使用率
192.168.1.100 3,250 12,000 62%
192.168.1.101 3,180 11,800 58%
192.168.1.103 3,570 13,200 65%

理想情况下,各节点指标应该趋于均衡,差异不超过15%。

4. 常见问题排错指南

4.1 新节点无法加入集群

现象
新节点日志中出现[error] Mnesia(emqx@new_node): ** Node 'emqx@existing_node' not responding **

解决方案

  1. 检查所有节点的cookie是否一致
    docker exec emqx-node cat /opt/emqx/etc/emqx.conf | grep node.cookie
    
  2. 验证网络连通性
    docker run --rm --net=host appropriate/curl curl -v telnet://existing_node:4370
    
  3. 检查防火墙规则
    sudo iptables -L -n | grep 5370
    

4.2 集群出现脑裂

现象
cluster status显示部分节点为unreachable,但实际网络正常

恢复步骤

  1. 停止所有节点
    docker-compose down
    
  2. 选择数据最新的节点作为主节点
  3. 其他节点清空data/mnesia目录后重新启动

4.3 负载不均衡

调整策略

# 启用负载均衡插件
docker exec emqx-node emqx_ctl plugins load emqx_lb

# 设置节点权重
docker exec emqx-node emqx_ctl lb node weight emqx@new_node 80

5. 进阶:从三节点到N节点

当集群规模超过5个节点时,建议考虑以下优化:

  1. 网络拓扑
    使用专线或VPC对等连接降低节点间延迟

  2. 发现策略
    static改为dnsetcd实现动态发现

  3. 分区容忍
    配置emqx.conf中的cluster.core_nodes参数:

    cluster.core_nodes = emqx@node1,emqx@node2,emqx@node3
    
  4. 监控告警
    使用Prometheus监控关键指标:

    # prometheus.yml 配置示例
    scrape_configs:
      - job_name: 'emqx'
        static_configs:
          - targets: ['node1:18083','node2:18083','node3:18083']
        metrics_path: '/api/v5/prometheus/stats'
    

扩容完成后,记得更新负载均衡器配置,将新节点加入后端服务器组。在AWS ALB或Nginx中,可以使用API动态更新:

# AWS ALB示例
aws elbv2 register-targets \
    --target-group-arn your-target-group-arn \
    --targets Id=${NEW_NODE_IP},Port=1883

更多推荐