【ETCD扩容避坑指南】手把手带你从3节点无损扩容到5节点,附完整命令
etcd扩容这件事吧,说起来就是几步命令的事,但真上手的时候你会发现——坑一个接一个。我当初在生产环境扩容的时候,因为一个配置参数搞错,折腾了两个小时才恢复。今天就把我踩过的坑、总结的经验一次性给你讲清楚。
本文基于 etcd v3.5.x 实测,操作系统 CentOS 7+/Rocky Linux,Kubernetes v1.24+。
读完本文你将能:
- 理解etcd扩容的核心原理(搞懂原理才能不慌)
- 安全地从3节点扩容到5节点,保证集群不抖动
- 遇到常见报错能快速定位并修复
一、什么时候需要扩容?先判断清楚
别动不动就扩容,先看看是不是真的有必要。以下是典型的扩容信号:
|
现象 |
判断阈值 |
|
leader CPU持续 >80% |
需要分摊写压力 |
|
proposals pending持续高位 |
etcdctl endpoint status看raft term正常但pending高 |
|
db size > 30GB |
单节点磁盘/内存压力大 |
|
需要跨AZ部署提升容错 |
从3节点扩到5节点,允许2台故障 |
如果只是读请求多,考虑加follower节点就够了;如果是写压力大,加节点其实反而会降低写性能——因为每次写入都需要多数派确认,节点越多网络开销越大。
(顺便提一嘴,etcd官方推荐的节点数是3、5、7,偶数节点虽然也能跑,但容错能力跟少一台的奇数集群一样,还多浪费一台机器,别干这种事。)
二、扩容前必须搞懂的几件事
2.1 动态成员变更是怎么工作的?
etcd用Raft协议做动态成员变更,你不需要停服。流程是这样的:
- 你在任意节点执行
member add - Leader把新成员信息提交给所有现有成员
- 新成员以 learner模式加入(v3.5+默认),先同步snapshot和WAL日志
- 追平之后自动升级为voting member
关键点:数据同步时间取决于当前db大小和网络,几个GB的话几分钟,几十GB可能要几十分钟甚至几个小时。这段时间新节点不能投票,但集群正常服务。
2.2 前提条件(漏一个都会出问题)
- etcd版本完全一致 —— 这个太重要了,版本不一致会导致协议不兼容,集群直接崩。建议用
etcdctl version确认一下。 - TLS证书用同一套CA签发 —— 新节点的证书必须由现有集群的CA签,别自己生成一套新的。
- 机器间网络互通 —— 2379(客户端端口)和2380(集群内部通信端口)都要放通。
- 新节点data-dir为空 ——
/var/lib/etcd目录必须是空的,有旧数据会残留之前的成员信息,导致加入失败。 - 集群当前健康 —— 扩容前一定要检查。
三、实战演示:从3节点扩容到5节点
假设现有集群是这样的:
- etcd01: 10.0.1.10
- etcd02: 10.0.1.11
- etcd03: 10.0.1.12
新增:
- etcd04: 10.0.1.13
- etcd05: 10.0.1.14
步骤1:备份!备份!备份!
别嫌我啰嗦,这是救命的:
export ETCDCTL_API=3
etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d_%H%M%S).db \
--endpoints=https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379 \
--cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/etcd.pem \
--key=/etc/etcd/ssl/etcd-key.pem
备份完验证一下快照是否完好:
etcdctl snapshot status /backup/etcd-snapshot-xxx.db --write-out=table
步骤2:检查集群状态(这是扩容的“体检”)
export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS=https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379
export ETCDCTL_CACERT=/etc/etcd/ssl/ca.pem
export ETCDCTL_CERT=/etc/etcd/ssl/etcd.pem
export ETCDCTL_KEY=/etc/etcd/ssl/etcd-key.pem
# 看所有成员
etcdctl member list --write-out=table
# 看每个端点的健康状态
etcdctl endpoint health
# 看详细状态,重点看"IS LEADER"和"RAFT TERM"
etcdctl endpoint status --write-out=table
预期输出中,所有成员应该是 healthy: true,RAFT TERM应该一致。
步骤3:添加etcd04(第一个新节点)
3.1 在现有集群的任意节点上添加成员:
etcdctl member add etcd04 --peer-urls=https://10.0.1.13:2380
输出会类似:
Member 8e9e05c5b3e4a1b7 added to cluster 3a2b1c4d5e6f7890
ETCD_NAME="etcd04"
ETCD_INITIAL_CLUSTER="etcd01=https://10.0.1.10:2380,etcd02=https://10.0.1.11:2380,etcd03=https://10.0.1.12:2380,etcd04=https://10.0.1.13:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
输出里的这几行配置别直接复制用,实际配置要根据你的环境改
3.2 在etcd04节点上启动etcd服务:
小心,这里容易搞错。关键是 ETCD_INITIAL_CLUSTER_STATE 要设为 existing,要是设成 new 它会尝试创建新集群,会跟现有集群ID冲突。
配置示例(放到 /etc/etcd/etcd.conf 或 systemd service文件里):
ETCD_NAME=etcd04
ETCD_DATA_DIR=/var/lib/etcd
ETCD_LISTEN_PEER_URLS=https://0.0.0.0:2380
ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379
ETCD_INITIAL_ADVERTISE_PEER_URLS=https://10.0.1.13:2380
ETCD_ADVERTISE_CLIENT_URLS=https://10.0.1.13:2379
ETCD_INITIAL_CLUSTER="etcd01=https://10.0.1.10:2380,etcd02=https://10.0.1.11:2380,etcd03=https://10.0.1.12:2380,etcd04=https://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing
ETCD_INITIAL_CLUSTER_TOKEN=your-cluster-token
启动并检查:
systemctl start etcd
systemctl status etcd
3.3 验证etcd04是否成功加入:
etcdctl member list
应该能看到etcd04,状态从unstarted慢慢变成healthy。
步骤4:等数据同步完成
加入learner后,新节点开始同步数据。可以用 etcdctl endpoint status 看db大小,或者看 member list 里节点从 unstarted 变成 started。通常几分钟到半小时不等,耐心等待。
步骤5:重复步骤3-4,添加etcd05
一样的流程,把etcd04的信息加到initial-cluster里。
步骤6:最终验证
# 所有成员列出
etcdctl member list --write-out=table
# 逐个endpoint检查健康
etcdctl endpoint health --cluster
# 检查选主情况
etcdctl endpoint status --write-out=table --cluster
5个节点都healthy,RAFT TERM一致,就恭喜你扩成功了。
四、扩容失败怎么办?常见问题的诊断与修复
坑1:request cluster ID mismatch 报错
这就是我遇到的那个大坑。集群ID不匹配,通常是initial-cluster-state设成了new。日志里会看到:
rafthttp: request cluster ID mismatch
解决方法:
- 从集群中移除节点:
etcdctl member remove <member_id> - 清空
/var/lib/etcd/member目录 - 把配置里的
initial-cluster-state改成existing,重新启动
坑2:failed to find member 或启动后不加入集群
新节点的数据目录里有旧的成员信息。根本原因是etcd启动时会优先读本地的成员列表,而不是用你配的initial-cluster。
解决方法:
- 停止etcd服务,删除
/var/lib/etcd/member目录,清空data-dir,重启 - 确保配置的initial-cluster里包含所有现有成员
坑3:新节点一直unhealthy,无法同步数据
检查网络端口(2379/2380),以及证书是否正确。常见错误:
- 证书过期:
openssl x509 -in /etc/etcd/ssl/etcd.pem -noout -dates - 防火墙挡了2380端口:
telnet 10.0.1.13 2380不通
坑4:操作完才发现版本不一致
这个太致命了。不同版本之间的内部协议可能不兼容(protobuf schema变化)。扩容前务必核对:
etcdctl version | head -1
所有节点的etcd version必须一致。
五、彩蛋:两个你可能没听过但很好用的技巧
- learner模式(v3.5+) 可以查询新节点是否还在同步中:
etcdctl member list | grep learner
learner状态下它还没投票权,但已经在拉数据了,不怕影响quorum。
- 如果你想控制learner升级为voting的时机,可以手动触发:
etcdctl member promote <member-id>
不过正常情况下集群会自动处理,不用你操心中。
- 关于Kubespray环境:如果是用Kubespray部署的K8s集群,不建议直接etcdctl操作,最好按官方指南跑
scale.yml来加节点,否则下次重新部署会被覆盖掉。
六、要不要扩容?我的经验小结
- 读多写少:加follower有用
- 写多:加节点反而慢,不如拆分集群或优化写入量
- 容错:3→5才能多容忍1台故障,5→7也是一样的道理,够用就好
- 禁止:扩容期间做缩容或集群元数据变更
你有没有在etcd扩容时踩过什么我没提到的坑?评论区聊聊,说不定能帮到更多人。
更多推荐



所有评论(0)