对 EMQX 集群执行滚动更新
使用 EMQX Operator 3.0 对 EMQX 集群执行平滑滚动更新。
前提条件
- 安装 EMQX Operator 3.0,并配置
kubectl以访问 Kubernetes 集群。 - 确保 Kubernetes 环境可以为
LoadBalancerService 分配外部地址。 - 准备可支持至少 3,000 个并发连接的有效 EMQX Enterprise 许可证,并将本示例中的许可证占位符替换为许可证密钥。详情请参见管理许可证。
- 安装
jq,用于查看节点疏散状态。 - 安装 MQTTX CLI,用于建立测试连接。
- 对于 Core-Replicant 集群,请至少配置两个 Core 副本。EMQX Operator 不接受 Core 副本数少于两个的 Core-Replicant 配置。
滚动更新的工作原理
如果对 EMQX 自定义资源的修改会改变 Pod 模板,例如修改镜像、镜像拉取策略、资源请求、Core 模板或 Replicant 模板,EMQX Operator 会按照以下阶段执行滚动更新:
- 更新 Core 节点: Operator 会在同一个 StatefulSet 中逐个更新 Core Pod。Operator 使用目标模板重新创建每个 Pod,并等待该 Pod 就绪后再继续更新。至少有一个更新后的 Core 节点就绪后,才能开始更新 Replicant 节点。
- 更新 Replicant 节点: Operator 采用类似 Deployment 的滚动更新方式,最多按照
maxSurge指定的数量创建更新后的 Replicant Pod,同时最多按照maxUnavailable指定的数量疏散上一修订版本的 Replicant Pod。这两个参数用于控制滚动更新速度和可用性。在该修订版本的 Replicant Pod 迁移完成之前,Operator 会保留上一修订版本的至少一个 Core 节点,确保迁移期间每个修订版本都有可用的 Core 节点。 - 完成滚动更新: Replicant Pod 迁移完成后,Operator 会继续更新其余 Core 节点。当所有目标 Pod 均已就绪,且不再存在上一修订版本的 Replicant Pod 时,滚动更新完成。
默认情况下,Operator 会在移除 Pod 前按照配置的速率疏散 MQTT 连接和会话。如需禁用节点疏散,请将 .spec.updateStrategy.evacuationStrategy.type 设置为 Disabled。
以下步骤通过修改 Pod 模板中的注解来演示此机制,不会升级 EMQX。如需升级 EMQX,请更新 .spec.image;Operator 会使用相同的滚动更新机制。
配置更新策略
- 创建一个
apps.emqx.io/v3beta1EMQX CR,并配置更新策略。
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: LoadBalancerWARNING
maxUnavailable 和 maxSurge 不能同时为 0。如果 maxUnavailable 为 100%,则 maxSurge 必须大于 0。有关字段定义、默认值和校验规则,参见 ReplicantsUpdateStrategy。
- 将以上内容保存为
emqx-update.yaml,并使用kubectl apply部署:
$ kubectl apply -f emqx-update.yaml
emqx.apps.emqx.io/emqx created- 检查 EMQX 集群状态。
确保 STATUS 为 Ready。此过程可能需要一些时间。
$ kubectl get emqx
NAME STATUS AGE
emqx Ready 8m33s建立测试连接
使用 MQTTX CLI 建立 MQTT 连接,以便在滚动更新期间观察连接疏散过程。MQTTX CLI 支持自动重连。
获取 emqx-listeners Service 的外部地址。以下命令同时适用于发布 IP 地址或主机名的负载均衡器。
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 个连接:
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触发更新
- 更新 Core 和 Replicant Pod 模板中的注解,以触发滚动更新。命令将时间戳与随机后缀组合,以降低重复使用相同注解值的可能性。
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}\"}}}}}"预期输出:
emqx.apps.emqx.io/emqx patched- 检查更新进度。
$ 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 | 节点疏散阶段。 |
sessionRecipients | MQTT 会话的接收节点。 |
sessionEvictionRate | 此节点的 MQTT 会话疏散速率(会话数/秒)。 |
connectionEvictionRate | 此节点的 MQTT 连接疏散速率(连接数/秒)。 |
initialSessions | 此节点的初始会话数。 |
initialConnections | 此节点的初始连接数。 |
可查看对应的 EMQX 节点状态中的 connections 和 sessions 计数器,估算节点疏散进度。
- 等待更新完成。
$ kubectl get emqx
NAME STATUS AGE
emqx Ready 8m33s确保 STATUS 为 Ready。更新所需时间取决于 MQTT 客户端和会话的数量。
更新完成后,可使用 kubectl get pods 验证所有 Pod 是否都在运行预期模板。
滚动更新示意图
下图以 3,000 个连接为例,展示 Replicant 滚动更新期间一种可能的连接分布。该图并非基于 Grafana 实测数据。
| 标签/前缀 | 说明 |
|---|---|
| 总计 | 示例连接总数,在图中显示为最上方的曲线。 |
emqx-replicant-86f864f9 | 原修订版本 Replicant Pod 集合的示例名称前缀。 |
emqx-replicant-648c45c7 | 更新后 Replicant Pod 集合的示例名称前缀。 |
更新 Replicant 节点时,从正在疏散的 Pod 断开连接的客户端可能会重新连接到其他仍在提供服务的 Pod,包括更新后的 Pod。图中的曲线仅用于说明这种连接重新分布,并非实测数据。实际连接数可能发生波动,具体取决于节点疏散速率、集群可用容量和客户端重连行为。