Skip to content

RKE2 新增 master 节点失败:多节点同时加入触发 etcd learner 死锁(too many learner members)

问题现象

已有 1 个 server 节点的 RKE2 集群扩容时,两台新 server 节点同时启动 rke2-server,双双卡在 activating 状态超过 10 分钟,始终无法加入集群。

节点 A(192.168.122.21)日志循环报:

text
Adding member sza122021.local-xxxx=https://192.168.122.21:2380 to etcd cluster [...]
Waiting for other members to finish joining etcd cluster: etcdserver: too many learner members in cluster

节点 B(192.168.122.22)日志循环报:

text
Failed to test etcd connection: failed to get etcd status: rpc error: code = Unavailable
  desc = connection error: desc = "transport: authentication handshake failed: context deadline exceeded"

环境信息

  • RKE2 版本:v1.35.6+rke2r1
  • 操作系统:Rocky Linux 9.8 (Blue Onyx)
  • 集群形态:3 × server(control-plane + etcd),首节点 192.168.122.20 已正常
  • CNI:Calico,kube-proxy IPVS 模式

根因分析

两个问题叠加形成 etcd learner 死锁

  1. 两台节点同时启动加入:etcd 集群同一时间只允许存在 1 个 learner(学习成员)。节点 B 先注册为 learner,节点 A 的 member add 因此被拒,循环报 too many learner members in cluster
  2. 节点 B 上的 iptables.service 默认 REJECT 规则阻断 etcd 对等端口 2380:Rocky Linux 9 预置的 /etc/sysconfig/iptablesINPUT/FORWARD 链末尾带有 REJECT --reject-with icmp-host-prohibited,导致 leader 无法向 learner(节点 B)同步 raft 数据,learner 永远无法追平日志、无法被提升(promote)为正式成员。

于是:B 永远是 learner → A 永远加不进来 → 死锁。

排查步骤

bash
# 1. 在首节点通过 etcd 容器查看成员列表(重点看 IS LEARNER 列)
export CONTAINER_RUNTIME_ENDPOINT=unix:///run/k3s/containerd/containerd.sock
ETCD_CTR=$(crictl ps --name "^etcd" -q | head -1)
crictl exec $ETCD_CTR etcdctl \
  --cacert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt \
  --cert=/var/lib/rancher/rke2/server/tls/etcd/server-client.crt \
  --key=/var/lib/rancher/rke2/server/tls/etcd/server-client.key \
  member list -w table

# 2. 检查新节点防火墙兜底规则(本案例的诱因)
ssh root@<新节> 'systemctl is-enabled iptables; iptables -L INPUT -n | tail -2; iptables -L FORWARD -n | tail -2'

本案例中修复防火墙后再次查看成员列表:节点 B 已被提升为正式成员(IS LEARNER=false),节点 A 从未注册成功(不在成员列表中)。

修复步骤

bash
# 1) 两台新节点:停止服务、清除防火墙 REJECT 规则并禁止其开机恢复
systemctl stop rke2-server
systemctl disable iptables
iptables -D INPUT   -j REJECT --reject-with icmp-host-prohibited
iptables -D FORWARD -j REJECT --reject-with icmp-host-prohibited

# 2) 从未成功加入的节点(A):清理残留 etcd 数据
rm -rf /var/lib/rancher/rke2/server/db

# 3) 若集群中残留卡死的 learner 成员(本案例无),在首节点移除:
# crictl exec $ETCD_CTR etcdctl <证书参数...> member remove <成员ID>

# 4) 关键:逐个启动,任何时刻只允许一个新成员加入
systemctl start rke2-server   # 先启动节点 B(已是正式成员,直接同步)
kubectl get nodes             # 等待节点 B Ready
systemctl start rke2-server   # 再启动节点 A(作为唯一 learner 加入)
kubectl get nodes             # 等待节点 A Ready

验证

  • 3 个节点全部 Ready(control-plane,etcd);
  • etcdctl member list:3 个成员全部 startedIS LEARNER=false
  • etcdctl endpoint health --cluster:3 个端点全部 true
  • 在两个新节点上分别运行测试 Pod,集群 DNS 解析正常,跨节点 CNI 网络正常。

经验教训

  1. RKE2 多 server 节点扩容必须逐个进行:启动一个,等其 Ready 且确认 etcd learner 提升完成(member listIS LEARNER=false)后,再启动下一个。并发加入必然触发 too many learner members
  2. learner 卡住时优先查网络:learner 需要通过 2380 端口从 leader 同步数据,任何防火墙拦截都会使其永远无法提升,进而阻塞后续所有节点加入。
  3. 加入失败需重试时:从未成功加入的节点先清理 /var/lib/rancher/rke2/server/db 再启动;已成功注册但数据损坏的节点,则需先在集群侧 member remove 再清理数据重加。
  4. Rocky/RHEL 9 部署 RKE2 前必须处理防火墙:即使未安装 firewalld,iptables.service 的默认 REJECT 规则同样会阻断集群端口(etcd 2379/2380、API 6443、supervisor 9345、CNI 等),应提前 systemctl disable --now iptables ip6tables。同类问题参见《RKE2 新增 master 节点失败:iptables 防火墙拦截 RKE2/Calico 端口》。