写于 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):
| 节点数 | 最大容忍故障 | 备注 |
|---|
| 1 | 0 | 单实例,无集群意义 |
| 2 | 0 | 双节点能启动,但挂一台就选不出主,不要用 |
| 3 | 1 | 生产最低推荐 |
| 4 | 1 | 跟 3 一样,浪费一台 |
| 5 | 2 | 中型集群推荐 |
| 6 | 2 | 跟 5 一样,浪费一台 |
| 7 | 3 | 大型集群(>100 节点) |
为什么不能是偶数? 4 节点和 3 节点都是容忍 1 台故障,但 4 节点多一台机器成本;6 节点和 5 节点同理。Raft 选举只看"过半",不看你有几个备份。
三、集群规划
3 master 节点同时跑 etcd(etcd 与 apiserver 同节点部署是 K8s 官方推荐):
| 主机名 | IP | 角色 |
|---|
| master1 | 192.168.139.133 | etcd-1 + apiserver |
| master2 | 192.168.139.134 | etcd-2 + apiserver |
| master3 | 192.168.139.135 | etcd-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 配置
只改 name 和 listen/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 要警惕)
十二、常见坑
- 证书 hostname/IP 漏配:peer 通信报 “x509: certificate is valid for …” 错误
- firewalld 没关:节点间 2380 端口不通,选举失败
initial-cluster-state: 'new' 重复使用:第二次启动报 “member already exists”,改成 'existing'- etcd 与 apiserver 时钟不同步:证书验证失败,先
ntpdate time1.aliyun.com 对齐 - snapshot 备份恢复后集群 ID 变了:用
etcdctl snapshot restore 时记得加 --initial-cluster 和 --initial-advertise-peer-urls
十三、前置知识 / 下一步
前置:
下一步:
- API Server 高可用(2021-03-15)—— Nginx 四层 + Keepalived VIP
- K8s 集群管理(2021-12-15)—— 升级、节点隔离、备份恢复
参考资料
2024+ 视角
etcd 在 2024-2026 年依然是 K8s 数据底座,但生态发生了几个关键变化:
- etcd v3.5 全面稳定:v3.5.x 引入的
etcdutl(替代旧 etcdctl 快照管理)、Raft ReadIndex 优化、Learner 节点都已 GA。生产推荐 v3.5.10+(修复了多个 Raft 性能回退问题)。 - K8s 1.30+ 的 etcd 管理变更:自 K8s 1.30 起,
kubeadm 默认 etcd 容器镜像从 k8s.gcr.io/etcd 切到 registry.k8s.io/etcd,并支持 etcd Learner 节点用于接入已有 etcd 集群(适合 etcd 单独运维团队)。 - etcd 性能与碎片整理:v3.4 之前的
defrag 命令在 v3.5 已不再是必须的——v3.5 后端实现了在线碎片回收(QuotaBackendBytes + AutoCompactionMode=periodic)。但 DB Size 超过 quota-backend-bytes 仍会触发告警。 - 可观测性增强:社区推荐
etcd-dashboard(来自 RedHat)+ Prometheus 抓取 etcd_disk_wal_fsync_duration_seconds、etcd_server_has_leader 等 20+ 关键指标。K8s SIG-Instrumentation 提供官方 kube-etcd-monitor Helm Chart。 - 替代方案的边界:Kine(将 etcd API 翻译成 SQL)让 MySQL/PostgreSQL 可作为 K8s 后端,但生产级 etcd 仍不可替代——Raft 协议下的强一致 + 低延迟(p99 < 10ms)是 etcd 的护城河。
- 运维注意:2024 年起,多个 K8s 重大故障源于 etcd 时钟漂移(节点间 NTP 偏差 > 1s 触发 leader 选举)。建议在所有 master 节点部署 chrony 而非 ntpd,并监控
node_timex_offset_seconds。
总之,这篇 2020 年的 etcd 部署骨架至今仍可作为新集群起点,主要补全是:升级到 v3.5.x、用 etcdutl 管理快照、补 Prometheus 监控。