Featured image of post etcd 集群部署:3 节点高可用与 Raft 选举详解

etcd 集群部署:3 节点高可用与 Raft 选举详解

etcd 是 K8s 的数据心脏。本文覆盖 etcd 集群原理(为什么必须奇数节点)、3 节点集群部署、Raft 选举验证、故障模拟与 systemd 守护,etcd v3.5 实战。

写于 2020-09,背景:etcd v3.4 时代,K8s 1.19 仍以 3 节点 etcd 集群为生产推荐。本文聚焦 etcd 集群原理 + 实战部署。

一、etcd 是什么

etcd 是一个分布式键值数据库,天生为集群化设计:

  • 数据模型:扁平 key-value,类比 redis 但走磁盘(强一致)
  • 一致性算法:Raft(保证多节点数据强一致)
  • API:gRPC + HTTP/JSON,对 K8s 友好
  • 应用场景:服务发现、配置中心、K8s/calico/Docker Swarm 后端存储

etcd 是 CNCF 毕业项目,与 K8s 同源(都来自 Borg 系统的设计哲学)。

二、Raft 算法与集群规模

Raft 在做决策时需要超半数节点投票,所以 etcd 集群推荐奇数节点(3/5/7):

节点数最大容忍故障备注
10单实例,无集群意义
20双节点能启动,但挂一台就选不出主,不要用
31生产最低推荐
41跟 3 一样,浪费一台
52中型集群推荐
62跟 5 一样,浪费一台
73大型集群(>100 节点)

为什么不能是偶数? 4 节点和 3 节点都是容忍 1 台故障,但 4 节点多一台机器成本;6 节点和 5 节点同理。Raft 选举只看"过半",不看你有几个备份。

三、集群规划

3 master 节点同时跑 etcd(etcd 与 apiserver 同节点部署是 K8s 官方推荐):

主机名IP角色
master1192.168.139.133etcd-1 + apiserver
master2192.168.139.134etcd-2 + apiserver
master3192.168.139.135etcd-3 + apiserver

端口约定

  • 2379:客户端 API(kube-apiserver 调 etcd)
  • 2380:节点间 peer 通信(Raft 选举 + 数据同步)

四、生成 etcd 证书

K8s 集群里 etcd 走双向 TLS,证书前置在 2020-03-15《K8s 二进制部署》 已签发。关键步骤回顾:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# ca-config.json
cat > ca-config.json << EOF
{
  "signing": {
    "default": { "expiry": "876000h" },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "876000h"
      }
    }
  }
}
EOF

# etcd CA
cat > etcd-ca-csr.json << EOF
{
  "CN": "etcd",
  "key": { "algo": "rsa", "size": 2048 },
  "names": [{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "etcd", "OU": "Etcd Security" }],
  "ca": { "expiry": "876000h" }
}
EOF

cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare /etc/etcd/ssl/etcd-ca

# etcd server 证书(注意 hostname + IP 全列)
cat > etcd-csr.json << EOF
{
  "CN": "etcd",
  "key": { "algo": "rsa", "size": 2048 },
  "names": [{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "etcd", "OU": "Etcd Security" }]
}
EOF

cfssl gencert \
  -ca=/etc/etcd/ssl/etcd-ca.pem \
  -ca-key=/etc/etcd/ssl/etcd-ca-key.pem \
  -config=ca-config.json \
  -hostname=127.0.0.1,master1,master2,master3,192.168.139.133,192.168.139.134,192.168.139.135 \
  -profile=kubernetes \
  etcd-csr.json | cfssljson -bare /etc/etcd/ssl/etcd

五、安装 etcd 二进制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 解压
wget https://github.com/etcd-io/etcd/releases/download/v3.5.11/etcd-v3.5.11-linux-amd64.tar.gz
tar -xf etcd-v3.5.11-linux-amd64.tar.gz
mv etcd-v3.5.11-linux-amd64/etcd /usr/local/bin/
mv etcd-v3.5.11-linux-amd64/etcdctl /usr/local/bin/
mv etcd-v3.5.11-linux-amd64/etcdutl /usr/local/bin/

# 验证
etcd --version
etcdctl version
# etcdctl version: 3.5.11
# API version: 3.5

六、etcd 配置文件

每个 master 节点单独写一份 /etc/etcd/etcd.config.yml注意 name、listen URL、advertise URL 都不同

6.1 master1 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
name: 'master1'
data-dir: /var/lib/etcd
wal-dir: /var/lib/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://192.168.139.133:2380'
listen-client-urls: 'https://192.168.139.133:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
initial-advertise-peer-urls: 'https://192.168.139.133:2380'
advertise-client-urls: 'https://192.168.139.133:2379'
initial-cluster: 'master1=https://192.168.139.133:2380,master2=https://192.168.139.134:2380,master3=https://192.168.139.135:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-outputs: [default]
force-new-cluster: false

6.2 master2 / master3 配置

只改 namelisten/advertise URL 的 IP,其他保持一致。

七、systemd 单元文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Unit]
Description=Etcd Service
Documentation=https://coreos.com/etcd/docs/latest/
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.config.yml
Restart=on-failure
RestartSec=10
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
Alias=etcd3.service

K8s 默认从 /etc/kubernetes/pki/etcd/ 找 etcd 证书,加软链:

1
2
mkdir -p /etc/kubernetes/pki/etcd
ln -s /etc/etcd/ssl/* /etc/kubernetes/pki/etcd/

八、启动与验证

1
2
3
4
# 所有 master 节点
systemctl daemon-reload
systemctl enable --now etcd.service
systemctl status etcd.service

关键验证(master1 执行):

1
2
3
4
5
6
7
8
# 注意 v2/v3 API 切换
export ETCDCTL_API=3

etcdctl --endpoints="https://192.168.139.133:2379,https://192.168.139.134:2379,https://192.168.139.135:2379" \
  --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem \
  --cert=/etc/kubernetes/pki/etcd/etcd.pem \
  --key=/etc/kubernetes/pki/etcd/etcd-key.pem \
  endpoint status --write-out=table

预期输出:

1
2
3
4
5
6
7
+----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+
|       ENDPOINT       |        ID        | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+----------------------+------------------+---------+---------+-----------+------------+
| 192.168.139.133:2379 | 67214e12874d646b |  3.5.11 |   29 kB |     false |         2 |          8 |
| 192.168.139.134:2379 | a6947d51f82bebc4 |  3.5.11 |   29 kB |     false |         2 |          8 |
| 192.168.139.135:2379 | f0598e5ca7a8066f |  3.5.11 |   29 kB |      true |         2 |          8 |
+----------------------+------------------+---------+---------+-----------+------------+

关键字段

  • IS LEADER:true 表示该节点是当前 leader(Raft 选举结果)
  • RAFT TERM:选举轮次,term 越大说明发生过越多次选举
  • DB SIZE:etcd 数据库占磁盘大小

九、故障模拟

9.1 模拟 master1 宕机

1
2
# 在 master1 上
systemctl stop etcd

观察其他节点:

1
2
# 观察 5~10 秒
watch -n 1 "etcdctl endpoint status --cluster --write-out=table"

预期:master1 不可达,master3 仍为 leader(因为 3 节点允许挂 1 台)。

9.2 模拟 master2 也宕机

1
2
# master2
systemctl stop etcd

此时只剩 master1,集群不可用(写操作会失败),但 etcd 不会自动重启——必须人工恢复至少 2 节点。

9.3 恢复

1
2
3
# master2 / master3 启动
systemctl start etcd
# 集群自动重新选主(5~10 秒内)

9.4 灾难恢复:强制重建集群

如果 3 台都挂,etcd 数据还在,但需要 --force-new-cluster 强制单节点启动:

1
2
3
4
5
6
# 改配置
echo "initial-cluster-state: 'existing'" >> /etc/etcd/etcd.config.yml
# 注意 force-new-cluster=true 只能用于一台

# 重启
systemctl start etcd

十、etcdctl 常用命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 列成员
etcdctl member list

# 健康检查
etcdctl endpoint health --cluster

# 写读测试
etcdctl put /test/foo "hello"
etcdctl get /test/foo

# 备份
etcdctl snapshot save /tmp/etcd-backup.db
etcdctl snapshot status /tmp/etcd-backup.db --write-out=table

# 碎片整理(v3.4 之前需要)
etcdctl defrag

# 告警
etcdctl alarm list
etcdctl alarm disarm

十一、性能调优

参数含义默认调优建议
heartbeat-interval心跳间隔(ms)100高延迟网络调大到 200~500
election-timeout选举超时(ms)1000至少 5 倍 heartbeat
quota-backend-bytes后端存储限额2GB集群 > 8GB 数据调大
snapshot-count触发快照的事务数100000写多场景调小到 10000
max-snapshots保留快照数5视磁盘空间

关键监控指标

  • etcd_disk_wal_fsync_duration_seconds(p99 < 10ms,否则 IO 瓶颈)
  • etcd_network_peer_round_trip_time_seconds(p99 < 50ms)
  • etcd_server_has_leader(必须 = 1)
  • etcd_server_proposals_committed_total / etcd_server_proposals_applied_total(差值 = pending,>5 要警惕)

十二、常见坑

  1. 证书 hostname/IP 漏配:peer 通信报 “x509: certificate is valid for …” 错误
  2. firewalld 没关:节点间 2380 端口不通,选举失败
  3. initial-cluster-state: 'new' 重复使用:第二次启动报 “member already exists”,改成 'existing'
  4. etcd 与 apiserver 时钟不同步:证书验证失败,先 ntpdate time1.aliyun.com 对齐
  5. snapshot 备份恢复后集群 ID 变了:用 etcdctl snapshot restore 时记得加 --initial-cluster--initial-advertise-peer-urls

十三、前置知识 / 下一步

前置

下一步

  1. API Server 高可用(2021-03-15)—— Nginx 四层 + Keepalived VIP
  2. K8s 集群管理(2021-12-15)—— 升级、节点隔离、备份恢复

参考资料

2024+ 视角

etcd 在 2024-2026 年依然是 K8s 数据底座,但生态发生了几个关键变化:

  1. etcd v3.5 全面稳定:v3.5.x 引入的 etcdutl(替代旧 etcdctl 快照管理)、Raft ReadIndex 优化、Learner 节点都已 GA。生产推荐 v3.5.10+(修复了多个 Raft 性能回退问题)。
  2. K8s 1.30+ 的 etcd 管理变更:自 K8s 1.30 起,kubeadm 默认 etcd 容器镜像从 k8s.gcr.io/etcd 切到 registry.k8s.io/etcd,并支持 etcd Learner 节点用于接入已有 etcd 集群(适合 etcd 单独运维团队)。
  3. etcd 性能与碎片整理:v3.4 之前的 defrag 命令在 v3.5 已不再是必须的——v3.5 后端实现了在线碎片回收(QuotaBackendBytes + AutoCompactionMode=periodic)。但 DB Size 超过 quota-backend-bytes 仍会触发告警。
  4. 可观测性增强:社区推荐 etcd-dashboard(来自 RedHat)+ Prometheus 抓取 etcd_disk_wal_fsync_duration_secondsetcd_server_has_leader 等 20+ 关键指标。K8s SIG-Instrumentation 提供官方 kube-etcd-monitor Helm Chart。
  5. 替代方案的边界Kine(将 etcd API 翻译成 SQL)让 MySQL/PostgreSQL 可作为 K8s 后端,但生产级 etcd 仍不可替代——Raft 协议下的强一致 + 低延迟(p99 < 10ms)是 etcd 的护城河。
  6. 运维注意:2024 年起,多个 K8s 重大故障源于 etcd 时钟漂移(节点间 NTP 偏差 > 1s 触发 leader 选举)。建议在所有 master 节点部署 chrony 而非 ntpd,并监控 node_timex_offset_seconds

总之,这篇 2020 年的 etcd 部署骨架至今仍可作为新集群起点,主要补全是:升级到 v3.5.x、用 etcdutl 管理快照、补 Prometheus 监控。

使用 Hugo 构建
主题 StackJimmy 设计