跳到主要内容

部署与高可用

集群规划​

单节点 etcd 挂了就全挂,生产必须做集群。集群规模、磁盘、网络这三件事在部署前就要定下来,后面再改成本很高。

节点数量​

Raft 靠多数派工作,所以节点数必须是奇数:

规模多数派可挂节点适用场景
321绝大多数生产集群,推荐
532规模大、跨可用区、容错要求高
743很少见,复制成本高,收益递减

3 节点是性价比最高的选择:能扛一台故障,写延迟可控,运维复杂度也低。5 节点适合跨三个可用区部署(每个区至少一个,再多一个冗余),但要接受更长的写路径。7 节点以上基本没必要,每次写要等 4 个成员落盘,延迟和带宽都不划算。

不要用 2 节点或 4 节点

2 节点多数派是 2,挂一台就写不进去;4 节点多数派是 3,和 3 节点一样只能挂一台,却多了一台的开销。偶数节点除了浪费机器,没有任何好处。

硬件与网络​

etcd 对 CPU 和内存要求不高,但对磁盘 fsync 延迟极其敏感:

  • 磁盘:必须 SSD 或 NVMe,最好独占一块盘,不要和日志、镜像、业务数据混用。WAL 的每次写都要 fsync,机械盘或云盘 IOPS 不够会直接拖垮整个控制面。
  • CPU / 内存:2 到 4 核、8 GB 内存起步。db 大小控制在 8 GB 以内,内存至少要能放下索引和热数据。
  • 网络:成员之间 RTT 最好在 10ms 以内,至少同区域。跨地域部署会让选举和日志复制变得很不稳定。
  • 时间同步:所有节点开 NTP/chrony。Raft 的选举超时和证书校验都依赖时钟大致准确。

生产环境建议把 etcd 部署在独立的三台机器上,和业务负载隔离。K8s 的 kubeadm 集群里 etcd 和 control plane 混部是常态,但至少保证控制面节点不跑重 IO 业务。

端口与目录​

端口用途访问范围
2379客户端 APIapiserver、etcdctl、运维工具
2380成员间 peer 通信仅集群成员之间
2381metrics(kubeadm 默认)监控系统

目录规划建议:

/etc/etcd/                 # 配置和证书
/var/lib/etcd/ # 数据目录,独立磁盘挂载点
/var/backups/etcd/ # 快照备份

三节点部署​

下面以 10.0.0.11、10.0.0.12、10.0.0.13 三台 Ubuntu 22.04 为例,成员名分别是 etcd-01、etcd-02、etcd-03。

配置文件​

先在三台机器上装好二进制(步骤见上一篇),然后写 /etc/etcd/etcd.conf.yml。以 etcd-01 为例:

name: etcd-01
data-dir: /var/lib/etcd

# 客户端监听:本机 + 内网地址,方便 apiserver 和 etcdctl 访问
listen-client-urls: http://10.0.0.11:2379,http://127.0.0.1:2379
advertise-client-urls: http://10.0.0.11:2379

# 集群内部通信
listen-peer-urls: http://10.0.0.11:2380
initial-advertise-peer-urls: http://10.0.0.11:2380

# 初始集群,三台必须完全一致
initial-cluster: etcd-01=http://10.0.0.11:2380,etcd-02=http://10.0.0.12:2380,etcd-03=http://10.0.0.13:2380
initial-cluster-state: new
initial-cluster-token: etcd-cluster-prod

# 生产建议打开自动压缩,细节见运维篇
auto-compaction-mode: periodic
auto-compaction-retention: 1h
quota-backend-bytes: 8589934592

logger: zap
log-level: info

etcd-02 和 etcd-03 只需要改三处:

配置项etcd-02etcd-03
nameetcd-02etcd-03
listen-client-urls / advertise-client-urls10.0.0.1210.0.0.13
listen-peer-urls / initial-advertise-peer-urls10.0.0.1210.0.0.13

initial-cluster 三台保持完全一致,这是最容易写错的地方。任何一台的列表里少了成员、或者 IP 写错,都会出现「各起各的集群」或者「连不上初始集群」。

启动与验证​

三台依次启动:

sudo systemctl enable --now etcd
sudo systemctl status etcd --no-pager

第一台启动时会一直重试,因为另外两台还没起来,拿到 2 票才能选出 leader。三台都起来后,日志里会看到 leader 选举完成。用 etcdctl 验证:

export ETCDCTL_ENDPOINTS=http://10.0.0.11:2379,http://10.0.0.12:2379,http://10.0.0.13:2379

# 三个成员都在
etcdctl member list -w table

# 三个端点都健康
etcdctl endpoint health --cluster

# 看谁是 leader、db 大小、raft 进度
etcdctl endpoint status -w table

member list 应该看到三行,IS LEADER 只有一行为 true。endpoint status 里三台的 RAFT TERM 应该一致,RAFT INDEX 相差不大。

先启一台没有意义

三节点集群首次启动时,第一台会卡在 no leader 等另外两台,这是正常的。不要因为看到这个日志就反复重启,也不要用 --force-new-cluster 去「修复」它。

扩缩容​

增加成员用 member add,新成员以 existing 状态加入:

# 在现有集群上预添加成员,拿到新成员 ID
etcdctl member add etcd-04 --peer-urls=http://10.0.0.14:2380

# 新节点配置 initial-cluster-state: existing,initial-cluster 用返回的完整列表
# 启动新节点后检查
etcdctl member list -w table

移除成员:

etcdctl member remove <member-id>

扩容要一台一台加,加完确认新成员追平(RAFT INDEX 接近 leader),再加下一台。缩容同理,一次移除一个,确认剩余成员保持多数派健康。不要一次性对多个成员做变更,中间态可能丢多数派。

TLS​

生产环境必须开 TLS。etcd 的 TLS 分两套:

  • 客户端 TLS:apiserver、etcdctl、监控系统访问 2379 时使用。
  • Peer TLS:成员之间 2380 通信使用。两套可以共用 CA,但证书最好分开,便于权限控制和轮换。

证书要求​

证书用途SAN 必须包含
CA签发和校验无
server服务端 2379本机 IP、127.0.0.1、localhost、客户端使用的域名
client客户端认证无特殊要求,CN 可用于标识身份
peer成员间 2380本机 IP、127.0.0.1、localhost

服务端证书的 SAN 一定要覆盖客户端实际连接的地址。apiserver 连的是 https://10.0.0.11:2379,证书里就得有 10.0.0.11;只写 localhost 会报 x509: certificate is valid for localhost, not 10.0.0.11。

生成证书​

用 OpenSSL 生成一套最小可用的证书(生产建议用 cfssl 或公司 PKI):

mkdir -p /etc/etcd/certs && cd /etc/etcd/certs

# 1. CA
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-subj "/CN=etcd-ca" -out ca.crt

# 2. 服务端证书,SAN 按节点实际地址改
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=etcd-01" -out server.csr
cat > server.ext <<'EOF'
subjectAltName=IP:10.0.0.11,IP:127.0.0.1,DNS:localhost
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 825 -sha256 -extfile server.ext -out server.crt

# 3. 客户端证书
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=etcd-client" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 825 -sha256 -out client.crt

# 4. peer 证书
openssl genrsa -out peer.key 2048
openssl req -new -key peer.key -subj "/CN=etcd-01-peer" -out peer.csr
cat > peer.ext <<'EOF'
subjectAltName=IP:10.0.0.11,IP:127.0.0.1,DNS:localhost
EOF
openssl x509 -req -in peer.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 825 -sha256 -extfile peer.ext -out peer.crt

每个节点都要用自己的 SAN 签一张 server 和 peer 证书。客户端证书可以一套通用,也可以给 apiserver、监控、运维各签一张,方便审计和吊销。

服务端参数​

把监听地址和 advertise 地址全部改成 https://,再加 TLS 参数:

listen-client-urls: https://10.0.0.11:2379,https://127.0.0.1:2379
advertise-client-urls: https://10.0.0.11:2379
listen-peer-urls: https://10.0.0.11:2380
initial-advertise-peer-urls: https://10.0.0.11:2380

cert-file: /etc/etcd/certs/server.crt
key-file: /etc/etcd/certs/server.key
client-cert-auth: true
trusted-ca-file: /etc/etcd/certs/ca.crt

peer-cert-file: /etc/etcd/certs/peer.crt
peer-key-file: /etc/etcd/certs/peer.key
peer-client-cert-auth: true
peer-trusted-ca-file: /etc/etcd/certs/ca.crt

参数含义:

参数作用
--cert-file / --key-file客户端流量的服务端证书和私钥
--trusted-ca-file校验客户端证书的 CA
--client-cert-auth强制客户端出示证书,不开等于只加密不认证
--peer-cert-file / --peer-key-file成员间通信的证书
--peer-trusted-ca-file校验其他成员证书的 CA
--peer-client-cert-auth强制成员间双向认证

--client-cert-auth 和 --peer-client-cert-auth 建议都打开。只开 TLS 不开认证,任何能连上 2379 的客户端都能读写,安全性约等于没有。

客户端访问​

etcdctl \
--endpoints=https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379 \
--cacert=/etc/etcd/certs/ca.crt \
--cert=/etc/etcd/certs/client.crt \
--key=/etc/etcd/certs/client.key \
endpoint health --cluster

也可以写进环境变量:

export ETCDCTL_ENDPOINTS=https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379
export ETCDCTL_CACERT=/etc/etcd/certs/ca.crt
export ETCDCTL_CERT=/etc/etcd/certs/client.crt
export ETCDCTL_KEY=/etc/etcd/certs/client.key
证书过期是最常见的「集群突然挂了」

etcd 证书过期后,成员之间握手失败,日志里是 transport: authentication handshake failed 或 x509: certificate has expired。监控里加一条证书过期告警(比如剩余 30 天),比事后救火便宜得多。kubeadm 集群的 etcd 证书默认一年有效期,记得定期 kubeadm certs renew。

快照备份与恢复​

etcd 的数据是集群状态的全部,备份是最后一道防线。没有验证过恢复的备份等于没有备份,这一节一定要在测试环境完整演练一遍。

做快照​

snapshot save 可以在线执行,不影响集群正常读写:

mkdir -p /var/backups/etcd

etcdctl \
--endpoints=https://10.0.0.11:2379 \
--cacert=/etc/etcd/certs/ca.crt \
--cert=/etc/etcd/certs/client.crt \
--key=/etc/etcd/certs/client.key \
snapshot save /var/backups/etcd/etcd-$(date +%F-%H%M).db

# 检查快照完整性、revision、hash
etcdctl snapshot status /var/backups/etcd/etcd-2026-10-25-1200.db -w table

快照里包含当前所有 key、revision 和成员信息,不含 WAL 里未提交的部分。备份策略建议:

  • 每小时一次全量快照,保留 7 天;每天再留一份到对象存储或异地。
  • 快照文件别和数据目录放同一块盘,磁盘坏了两个一起没。
  • 定期(比如每季度)做一次恢复演练,确认快照能真的恢复出集群。
  • 记录快照对应的 revision,恢复后方便和业务侧对账。

恢复演练​

恢复的核心是:用快照重建一个新集群,而不是把快照文件塞回原数据目录。snapshot restore 的目标目录必须是空目录或不存在的目录,已经有数据的目录会直接报错。

假设三个节点全挂了,手上有一份 /var/backups/etcd/etcd-2026-10-25-1200.db。在三台机器上分别执行恢复,注意 --name、--data-dir、peer URL 每台不同:

# 在 etcd-01 上执行
sudo etcdutl snapshot restore /var/backups/etcd/etcd-2026-10-25-1200.db \
--name etcd-01 \
--data-dir /var/lib/etcd-restore \
--initial-cluster etcd-01=https://10.0.0.11:2380,etcd-02=https://10.0.0.12:2380,etcd-03=https://10.0.0.13:2380 \
--initial-advertise-peer-urls https://10.0.0.11:2380 \
--initial-cluster-token etcd-cluster-restored

etcd-02、etcd-03 用同一份快照、同样的 --initial-cluster 和 token,只改 --name、--data-dir 和 peer URL。然后把三台的配置指向新的数据目录、token 改成 etcd-cluster-restored,启动即可。

验证:

etcdctl --endpoints=https://10.0.0.11:2379 \
--cacert=/etc/etcd/certs/ca.crt \
--cert=/etc/etcd/certs/client.crt \
--key=/etc/etcd/certs/client.key \
endpoint health --cluster

etcdctl --endpoints=https://10.0.0.11:2379 \
--cacert=/etc/etcd/certs/ca.crt \
--cert=/etc/etcd/certs/client.crt \
--key=/etc/etcd/certs/client.key \
member list -w table
3.5 用 etcdctl 还是 etcdutl

3.5 里 etcdctl snapshot restore 还能用,但会提示迁移到 etcdutl。3.6 起快照相关操作统一走 etcdutl,命令除了前缀基本一样。新环境直接记 etcdutl snapshot restore。

只恢复一个成员​

如果另外两个成员的数据还在,只是有一个成员磁盘坏了,不需要全量恢复:

  1. 把坏掉的成员从集群里 member remove。
  2. 用 member add 把它加回来,新节点以空数据目录、existing 状态启动,自动从 leader 同步。
  3. 同步完成后检查 RAFT INDEX 和 db 大小。

只有多数派数据全部丢失时,才需要走上面的全量快照恢复。如果只剩一个成员的数据,也可以用它加 --force-new-cluster 单节点拉起,再重新加成员,但要注意它的数据可能落后于最后一次快照。

K8s 场景​

kubeadm 部署的集群里,etcd 是 kubelet 管理的静态 Pod,不是 Deployment:

  • 清单在 /etc/kubernetes/manifests/etcd.yaml,改文件 kubelet 会自动重建 Pod。
  • 数据目录在 /var/lib/etcd,证书在 /etc/kubernetes/pki/etcd/。
  • 只监听 127.0.0.1:2379 和节点 IP,外部访问要走 SSH 隧道或上机执行。
  • 三个控制面节点各有一个 etcd 成员,kubelet 挂了 etcd 容器不一定马上停,但失去自愈能力。

备份直接在控制面节点上执行:

sudo mkdir -p /var/backups
sudo etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
snapshot save /var/backups/etcd-$(date +%F-%H%M).db

恢复的大致流程(务必在维护窗口、确认备份可用后操作):

  1. 停 kube-apiserver:把 /etc/kubernetes/manifests/kube-apiserver.yaml 移出目录,等 Pod 消失。
  2. 停 etcd:把 /etc/kubernetes/manifests/etcd.yaml 移出目录。
  3. 用 etcdutl snapshot restore 恢复到 /var/lib/etcd-restore,三个控制面节点用同一份快照和 token。
  4. 把恢复目录权限设好,修改 etcd.yaml 的 hostPath 指向新目录(或把旧目录挪走、新目录改名成 /var/lib/etcd)。
  5. 移回 etcd.yaml,确认 etcd 健康后,再移回 kube-apiserver.yaml。
  6. 检查 kubectl get nodes、kubectl get pods -A,确认集群状态。

K8s 的 etcd 和 apiserver 是强依赖:etcd 不可用时 apiserver 会大量报错,但已经运行的 Pod 不受影响。恢复期间控制面不可用,业务 Pod 继续跑,这是 K8s 设计上的一点安慰。K8s 控制面的整体关系可以回看 Kubernetes 核心概念与架构。

托管集群不要自己动 etcd

EKS、ACK、GKE 这类托管 K8s,控制面和 etcd 由云厂商管理,你既没有 SSH 也没有 etcd 证书。备份和恢复走厂商的管控面备份功能,别想着连 etcd 端口,连不上也修不了。

小结​

  • 集群规模选 3 或 5,奇数、跨故障域、独立 SSD;网络 RTT 控制在 10ms 内,时钟要同步。
  • 三节点部署的关键是 initial-cluster 三台完全一致,首次启动用 new,加成员用 existing,一次只动一个成员。
  • TLS 分客户端和 peer 两套,SAN 要覆盖实际访问地址,client-cert-auth 和 peer-client-cert-auth 都要开。
  • 备份用 snapshot save 在线做,恢复用 etcdutl snapshot restore 重建集群;恢复演练比备份本身更重要。
  • kubeadm 集群的 etcd 是 kubelet 管的静态 Pod,恢复要先停 apiserver 和 etcd,改完 hostPath 再按顺序拉起。

集群跑起来只是及格线。下一篇进入日常运维:盯哪些指标、db 多大算危险、怎么接 Prometheus 告警。