Skip to content

对 EMQX 集群执行滚动更新 ​

使用 EMQX Operator 3.0 对 EMQX 集群执行平滑滚动更新。

前提条件 ​

  • 安装 EMQX Operator 3.0,并配置 kubectl 以访问 Kubernetes 集群。
  • 确保 Kubernetes 环境可以为 LoadBalancer Service 分配外部地址。
  • 准备可支持至少 3,000 个并发连接的有效 EMQX Enterprise 许可证,并将本示例中的许可证占位符替换为许可证密钥。详情请参见管理许可证。
  • 安装 jq,用于查看节点疏散状态。
  • 安装 MQTTX CLI,用于建立测试连接。
  • 对于 Core-Replicant 集群,请至少配置两个 Core 副本。EMQX Operator 不接受 Core 副本数少于两个的 Core-Replicant 配置。

滚动更新的工作原理 ​

如果对 EMQX 自定义资源的修改会改变 Pod 模板,例如修改镜像、镜像拉取策略、资源请求、Core 模板或 Replicant 模板,EMQX Operator 会按照以下阶段执行滚动更新:

  1. 更新 Core 节点: Operator 会在同一个 StatefulSet 中逐个更新 Core Pod。Operator 使用目标模板重新创建每个 Pod,并等待该 Pod 就绪后再继续更新。至少有一个更新后的 Core 节点就绪后,才能开始更新 Replicant 节点。
  2. 更新 Replicant 节点: Operator 采用类似 Deployment 的滚动更新方式,最多按照 maxSurge 指定的数量创建更新后的 Replicant Pod,同时最多按照 maxUnavailable 指定的数量疏散上一修订版本的 Replicant Pod。这两个参数用于控制滚动更新速度和可用性。在该修订版本的 Replicant Pod 迁移完成之前,Operator 会保留上一修订版本的至少一个 Core 节点,确保迁移期间每个修订版本都有可用的 Core 节点。
  3. 完成滚动更新: Replicant Pod 迁移完成后,Operator 会继续更新其余 Core 节点。当所有目标 Pod 均已就绪,且不再存在上一修订版本的 Replicant Pod 时,滚动更新完成。

默认情况下,Operator 会在移除 Pod 前按照配置的速率疏散 MQTT 连接和会话。如需禁用节点疏散,请将 .spec.updateStrategy.evacuationStrategy.type 设置为 Disabled。

以下步骤通过修改 Pod 模板中的注解来演示此机制,不会升级 EMQX。如需升级 EMQX,请更新 .spec.image;Operator 会使用相同的滚动更新机制。

配置更新策略 ​

  1. 创建一个 apps.emqx.io/v3beta1 EMQX CR,并配置更新策略。
yaml
apiVersion: apps.emqx.io/v3beta1
kind: EMQX
metadata:
  name: emqx
spec:
  image: emqx/emqx:6.3.2
  config:
    roots:
      license:
        key: "..."
  updateStrategy:
    type: RollingUpdate
    evacuationStrategy:
      # MQTT 客户端疏散速率,单位为连接数/秒:
      connectionEvictionRate: 1000
      # MQTT 会话疏散速率,单位为会话数/秒:
      sessionEvictionRate: 1000
      # 删除 Pod 前的等待时间:
      waitTakeover: 10
    replicants:
      maxUnavailable: 1
      maxSurge: 1
  coreTemplate:
    spec:
      replicas: 2
  replicantTemplate:
    spec:
      replicas: 3
  listenersServiceTemplate:
    spec:
      type: LoadBalancer

WARNING

maxUnavailable 和 maxSurge 不能同时为 0。如果 maxUnavailable 为 100%,则 maxSurge 必须大于 0。有关字段定义、默认值和校验规则,参见 ReplicantsUpdateStrategy。

  1. 将以上内容保存为 emqx-update.yaml,并使用 kubectl apply 部署:
bash
$ kubectl apply -f emqx-update.yaml
emqx.apps.emqx.io/emqx created
  1. 检查 EMQX 集群状态。

确保 STATUS 为 Ready。此过程可能需要一些时间。

bash
$ kubectl get emqx
NAME      STATUS   AGE
emqx      Ready    8m33s

建立测试连接 ​

使用 MQTTX CLI 建立 MQTT 连接,以便在滚动更新期间观察连接疏散过程。MQTTX CLI 支持自动重连。

获取 emqx-listeners Service 的外部地址。以下命令同时适用于发布 IP 地址或主机名的负载均衡器。

bash
export EMQX_HOST="$(kubectl get service emqx-listeners -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}')"

确认 EMQX_HOST 中已包含地址后,使用 MQTTX 向端口 1883 上的默认 TCP 监听器建立 3,000 个连接:

bash
mqttx bench conn -h "${EMQX_HOST}" -p 1883 -c 3000
[10:05:21 AM] › ℹ  Start the connect benchmarking, connections: 3000, req interval: 10ms
✔  success   [3000/3000] - Connected
[10:06:13 AM] › ℹ  Done, total time: 31.113s

触发更新 ​

  1. 更新 Core 和 Replicant Pod 模板中的注解,以触发滚动更新。命令将时间戳与随机后缀组合,以降低重复使用相同注解值的可能性。
bash
ROLLOUT_ID="$(date +%s)-${RANDOM}"

kubectl patch emqx emqx --type=merge -p \
  "{\"spec\":{\"coreTemplate\":{\"metadata\":{\"annotations\":{\"docs.emqx.com/rollout-id\":\"${ROLLOUT_ID}\"}}},\"replicantTemplate\":{\"metadata\":{\"annotations\":{\"docs.emqx.com/rollout-id\":\"${ROLLOUT_ID}\"}}}}}"

预期输出:

text
emqx.apps.emqx.io/emqx patched
  1. 检查更新进度。
bash
$ kubectl get emqx emqx -o json | jq ".status.nodeEvacuations"
[
  {
    "nodeName": "emqx@10.244.4.56",
    "initialConnections": 33,
    "initialSessions": 0,
    "connectionEvictionRate": 200,
    "sessionEvictionRate": 200,
    "state": "waiting_takeover",
    "sessionRecipients": [
      "emqx@10.244.4.57",
      "emqx@10.244.4.58"
    ]
  }
]
字段说明
nodeName当前正在疏散的节点。
state节点疏散阶段。
sessionRecipientsMQTT 会话的接收节点。
sessionEvictionRate此节点的 MQTT 会话疏散速率(会话数/秒)。
connectionEvictionRate此节点的 MQTT 连接疏散速率(连接数/秒)。
initialSessions此节点的初始会话数。
initialConnections此节点的初始连接数。

可查看对应的 EMQX 节点状态中的 connections 和 sessions 计数器,估算节点疏散进度。

  1. 等待更新完成。
bash
$ kubectl get emqx
NAME      STATUS   AGE
emqx      Ready    8m33s

确保 STATUS 为 Ready。更新所需时间取决于 MQTT 客户端和会话的数量。

更新完成后,可使用 kubectl get pods 验证所有 Pod 是否都在运行预期模板。

滚动更新示意图 ​

下图以 3,000 个连接为例,展示 Replicant 滚动更新期间一种可能的连接分布。该图并非基于 Grafana 实测数据。

06001,2001,8002,4003,000更新进度总计 3,000emqx-replicant-86f864f9-0emqx-replicant-86f864f9-1emqx-replicant-86f864f9-2emqx-replicant-648c45c7-0emqx-replicant-648c45c7-1emqx-replicant-648c45c7-2Replicant 滚动更新示意图,maxSurge = 1,maxUnavailable = 1
标签/前缀说明
总计示例连接总数,在图中显示为最上方的曲线。
emqx-replicant-86f864f9原修订版本 Replicant Pod 集合的示例名称前缀。
emqx-replicant-648c45c7更新后 Replicant Pod 集合的示例名称前缀。

更新 Replicant 节点时,从正在疏散的 Pod 断开连接的客户端可能会重新连接到其他仍在提供服务的 Pod,包括更新后的 Pod。图中的曲线仅用于说明这种连接重新分布,并非实测数据。实际连接数可能发生波动,具体取决于节点疏散速率、集群可用容量和客户端重连行为。