ETCD 探索
写这篇文章的起因,是我在整理 Kubernetes 相关知识时,反复撞见 etcd 这个名字:集群状态存在它里面,服务发现依赖它,连不少配置中心的底层也是它。与其把它当黑盒,不如单独拿出来搭一遍、理一遍。在分布式系统中,经常会遇到这样的问题:
- 服务节点需要共享配置
- 系统需要做服务发现
- 分布式锁需要一个协调中心
- 集群需要一个一致性的状态存储
这些问题,本质上都需要一个 可靠的分布式协调系统。
单机时代这些需求可以用一个数据库表甚至一个文件解决,但节点一多,问题就变成了:多个副本之间如何对"当前状态"达成一致?谁说了算?某个节点挂了之后数据还可信吗?这正是分布式协调系统要回答的问题。
而在现代云原生体系中,最常用的组件就是 etcd。
例如:
- Kubernetes
- CoreDNS
- service mesh
- 分布式配置中心
这些系统的底层都依赖 etcd。
etcd 是什么
简单来说:
etcd 就是一个高可靠的分布式 Key-Value 数据库。
但它和普通数据库最大的区别是:
它是为"分布式协调"而设计的。
这个定位决定了它的取舍:它不追求海量数据和极限吞吐,而是追求"存进去的每一条数据都可信"。所以它适合存元数据、配置、状态这类小而关键的数据,而不适合当业务数据库用。
它的主要特点有:
- 强一致(Strong Consistency)
- 支持分布式集群
- 提供 Watch 监听机制
- 支持事务
- 提供租约(Lease)机制
这几个特性组合起来很有意思:
-
Watch 让客户端可以订阅某个 key 或前缀的变化,配置一改,所有节点实时感知,不用轮询。
-
Lease 相当于带 TTL 的"心跳凭证",key 可以绑定在租约上,客户端一旦停止续约,key 自动删除——服务注册与故障剔除就是靠它实现的。
-
事务(Txn)提供 compare-and-swap 语义,"如果这个 key 不存在就写入"可以原子完成,这是分布式锁的基础。
很多分布式系统都会用 etcd 做:
- 服务注册中心
- 配置中心
- 分布式锁
- Leader 选举
例如 Kubernetes 就把整个 集群状态 存在 etcd 中。kube-apiserver 是唯一直接读写 etcd 的组件,其他控制器都通过 apiserver 的 Watch 机制间接感知状态变化。
强一致性(Raft 共识算法)
etcd 内部使用的是 Raft 共识算法。
Raft 的核心思想是:
整个集群中会选举出一个 Leader 节点。
所有写请求必须经过 Leader。
流程大致是:
客户端 → Leader → 同步到多数节点 → 提交成功
只有当:
超过一半节点确认写入
数据才算真正提交。
这里可以再拆细一点。Raft 中每个节点处于三种角色之一:Leader、Follower、Candidate。正常运行时只有一个 Leader,它周期性地向所有 Follower 发送心跳;如果某个 Follower 在超时时间内收不到心跳,它会把自己变成 Candidate 发起选举,拿到多数票的节点成为新 Leader。
写入走的是日志复制(Log Replication):Leader 先把写操作追加到自己的日志,再并行发给所有 Follower;当多数派(majority,即超过半数)节点都持久化了这条日志,Leader 才把它标记为已提交,然后应答客户端。
"多数派"这个设计是强一致的关键:任何两个多数派集合必然有交集,所以即使 Leader 崩溃,新选出的 Leader 也一定包含所有已提交的数据,不会丢写。
这也解释了为什么 etcd 集群节点数推荐是奇数(3、5、7):
- 3 节点允许挂 1 个,5 节点允许挂 2 个
- 4 节点同样只允许挂 1 个(多数派需要 3 个),容错能力和 3 节点一样,反而多了一份同步开销
etcd 集群搭建
部署环境:
操作系统:
Debian 11.5.0
1 下载 etcd
从 github 下载 etcd 二进制文件:
https://github.com/etcd-io/etcd/releases
release 页面按平台提供压缩包,选对应架构(如 linux-amd64)下载即可。etcd 是单个静态二进制,没有额外的运行时依赖,这也是它部署简单的原因之一。
2 安装 etcd
解压之后,将以下文件放入:
/usr/local/bin
主要是:
etcd
etcdctl
两个文件分工明确:etcd 是服务端进程,etcdctl 是命令行客户端,后面所有的查询、写入、维护操作都通过它完成。放进 /usr/local/bin 是为了让它们进入 PATH,任意目录下都能直接调用。
3 设置执行权限
# 解压出来的文件可能没有可执行位,手动补上
sudo chmod +x /usr/local/bin/etcd
sudo chmod +x /usr/local/bin/etcdctl
到这一步,单个节点的二进制就绪。集群模式下,每个节点都重复上述安装步骤,再通过启动参数声明各自的名字、监听地址和初始集群成员列表;节点间通过 2380 端口互相通信,客户端通过 2379 端口访问。我这套环境启用了 TLS,证书统一放在 /opt/etcd/ssl 下,所以下面的验证命令都要带上证书参数。
4 验证集群
可以使用 etcdctl 查看数据:
# 列出集群中所有 key(只看 key 不看 value)
# --cacert/--cert/--key:TLS 双向认证所需的 CA 证书与客户端证书
# --endpoints:把三个节点都写上,客户端会自动负载与故障转移
ETCDCTL_API=3 etcdctl \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints="https://172.24.93.151:2379,https://172.24.93.149:2379,https://172.24.93.150:2379" \
get / --prefix --keys-only
命令能返回结果,说明客户端到集群的链路、证书、多数派状态都是通的。--prefix 表示按前缀匹配,配合 / 相当于遍历全部 key。
删除 key:
# 按前缀删除,空前缀 "" 会匹配所有 key —— 生产环境慎用
ETCDCTL_API=3 etcdctl \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints="https://172.24.93.151:2379,https://172.24.93.149:2379,https://172.24.93.150:2379" \
del --prefix ""
del --prefix "" 是全量删除。如果这套 etcd 正在给 Kubernetes 当后端,这条命令等于清空整个集群状态,执行前务必确认目标环境。
etcd 为什么比 Redis 更适合做分布式协调
很多人会问:
Redis 不是也可以做分布式锁吗?
确实可以。
但 Redis 是 AP 系统,而 etcd 是 CP 系统。
简单理解就是:
Redis 更注重 可用性与性能。
etcd 更注重 一致性与可靠性。
具体到锁的场景,差异体现在故障时的行为:Redis 主从复制是异步的,主节点写入锁之后还没同步就宕机,从节点提升为主后锁就"丢"了,两个客户端可能同时持有锁;Redlock 方案试图缓解这个问题,但社区对其安全性一直有争议。而 etcd 的写入必须经过多数派确认才算成功,Leader 切换不会丢已提交的数据,锁的语义在故障场景下依然成立。
代价当然是性能:一次写入要走一轮多数派同步和磁盘落盘,延迟和吞吐都远不如 Redis。所以结论不是谁更好,而是分场景——缓存、计数、允许偶发失效的轻量锁用 Redis;配置、选主、必须严格互斥的锁用 etcd。
踩坑与注意
搭建和使用过程中有几点值得记录:
-
ETCDCTL_API=3这个环境变量别漏。老版本 etcdctl 默认走 v2 API,v2 和 v3 的数据完全隔离,用错 API 版本会出现"明明写了却查不到"的假象。较新的版本已默认 v3,但在脚本里显式声明总没错。 -
--endpoints建议写全所有节点。只写一个节点时,该节点一旦故障,客户端就直接失联;写全之后客户端能自动切换到健康节点。 -
证书路径和 SAN 要匹配。启用 TLS 后,证书里的 IP/域名必须覆盖 endpoints 里实际使用的地址,否则握手阶段就会被拒。
-
节点数保持奇数。前面 Raft 部分已经解释过原因:偶数节点不增加容错能力,只增加同步成本。
-
etcd 对磁盘延迟敏感。Raft 日志每次提交都要 fsync,磁盘慢会直接拖高写延迟甚至触发 Leader 选举抖动,条件允许尽量用 SSD。
小结
etcd 本质上是"用性能换一致性"的一个典型设计:Raft 多数派提交保证了任何时刻读到的已提交数据都可信,Watch、Lease、事务这些机制则把"可信的状态存储"进一步变成了服务发现、选主、分布式锁的通用底座。搭建本身不复杂——两个二进制加一组启动参数——真正需要花心思的是 TLS 证书、节点规划和对它适用边界的判断。把它和 Redis 的分工想清楚,各用在各自擅长的场景,比争论谁强更有意义。
评论 / COMMENTS