高可用分布式共享存储服务——etcd

cuixiaogang

etcd 是一个分布式、高可用、强一致性的用于配置共享和服务发现的键值(Key-Value)存储系统,由 CoreOS(现属于 Red Hat)开发,最初用于服务发现和配置管理。它设计为轻量级、易部署,支持分布式环境下的数据可靠存储,是云原生生态(如 Kubernetes、Docker Swarm)的核心组件之一。

etcd中常用的术语

术语 描述 备注
Raft Raft算法,etcd实现一致性的核心 etcd有etcd-raft模块
Follower Raft中的从属节点 竞争Leader失败
Leader Raft中的领导协调节点,用于处理数据提交 Leader节点协调整个集群
Candidate 候选节点 当Follower接收Leader节点的消息超时时,会转变为Candidate
Node Raft状态机的实例 Raft中涉及多个节点
Member etcd实例,管理者对应的Node节点 可处理客户端请求
Peer 同一个集群中的另一个Member 其他成员
Cluster etcd集群 拥有多个etcd Member
Lease 租期 设置关键的租期,过期删除
Watch 监测机制 监控键值对的变化
Term 任期 某个节点成为Leader,到下一次竞选的时间
WAL 预写式日志 用于持久化存储的日志格式
Client 客户端 向etcd发起请求的客户端

核心特性

分布式一致性

  • 基于Raft算法:通过 Raft 共识算法实现领导者选举和日志复制,确保集群中数据的强一致性(即所有节点数据实时同步)。
  • 高可用性:集群至少需要3个节点(奇数个,防止脑裂),支持自动故障转移,部分节点宕机时仍能正常服务。

数据模型

  • 分层键空间:类似文件系统的目录结构(如/config/app1/key1),支持前缀查询(如获取/config/app1/下所有键)。
  • 持久化与临时键:键可设置 TTL(生存时间),临时键在租约(Lease)过期或连接中断时自动删除,适用于服务注册等场景。

Watch机制

  • 支持对单个键或前缀路径的变更监听(创建、修改、删除),客户端可实时获取事件通知,用于实现配置动态更新、分布式协调等功能。

简单易用的 API

  • 提供 HTTP/JSON 和 gRPC 接口,支持多语言客户端(Go、Python、Java 等),方便与现有系统集成。

安全与持久化

  • 数据持久化:通过预写日志(WAL)和快照(Snapshot)保证数据可靠性,支持自动压缩日志以减少存储开销。
  • 安全认证:支持 HTTPS 加密传输、用户认证(ACL)和角色权限管理,确保数据访问安全。

典型应用场景

服务注册与发现

微服务启动时将地址注册到 etcd(如/services/user-service/instance1),其他服务通过查询或监听该路径获取可用实例列表,实现动态负载均衡。

服务注册与发现示意图

分布式配置管理

存储应用配置(如数据库连接字符串、功能开关),配置变更时通过Watch机制实时通知所有客户端,避免重启服务即可更新配置。

分布式锁与同步

通过原子操作(如 Compare-And-Swap)实现分布式锁,确保多个节点对共享资源的互斥访问,典型场景包括分布式任务调度、资源抢占。

集群元数据管理

存储集群节点状态(如 Kubernetes 的节点、Pod、服务信息),作为分布式系统的 “大脑”,记录全局状态并保证各节点视图一致。

分布式协调

实现选主(Leader Election)、分布式队列等功能,例如在分布式系统中选举一个主节点负责集中管理任务。

etcd的核心架构

核心架构图

共识层:基于 Raft 算法的分布式共识

  • Raft 算法核心作用
    • 领导者选举:集群启动或 Leader 故障时,通过投票选出唯一 Leader,确保写操作的集中处理。
    • 日志复制:写请求由 Leader 接收,通过日志(Log Entry)复制到多数 Follower 节点,经多数确认(Quorum)后提交,保证数据强一致性。
    • 成员变更:支持动态调整集群节点,通过 “两阶段成员变更” 避免脑裂。
  • 节点角色
    • Leader:
      • 处理所有写请求,生成日志并同步给 Follower。
      • 响应 Follower 的心跳请求(Heartbeat),维持领导地位。
    • Follower:
      • 接收并持久化 Leader 同步的日志,响应读请求(或转发给 Leader)。
      • 超时未收到心跳时发起选举,转为 Candidate 角色。
    • Observer(可选):
      • 不参与共识算法(不投票、不存储日志),仅用于扩展读性能,提升大规模集群的读吞吐量。

数据层:分层键空间与存储引擎

  • 数据模型
    • 分层键空间:类似文件系统的树形结构(如 /app/config/db/url),支持前缀查询和范围操作,便于组织复杂配置。
    • 租约(Lease)与 TTL:键可绑定租约,租约过期时键自动删除,实现临时数据(如服务注册信息)的动态管理。
  • 存储引擎
    • BoltDB:嵌入式键值数据库,提供高效的磁盘存储,支持事务性读写,确保数据持久化。
    • 预写日志(WAL):所有写操作先写入 WAL,保证故障恢复时数据不丢失,通过定期快照(Snapshot)压缩日志,提升恢复效率。
    • 内存存储:键值数据同时存在于内存中,通过索引加速查询,兼顾性能与可靠性。

通信层

  • 节点间通信(集群内部)
    • Raft 协议通信:通过 peerURLs 暴露端口(默认 2380),用于 Leader 与 Follower 同步日志、心跳交互。
    • gRPC 协议:v3 版本后基于 gRPC 实现,支持高效的远程过程调用(RPC),包括日志复制、成员变更等操作。
  • 客户端通信(外部接口)
    • gRPC/HTTP 接口:通过 clientURLs 暴露端口(默认 2379),支持 HTTP/JSON 和 gRPC 协议,提供键值操作(PUT/GET/DELETE)、Watch 事件监听等功能。
    • 负载均衡:客户端可连接任意节点,Follower 会将写请求转发给 Leader,读请求可在本地节点处理(需确保数据最新,或通过 readIndex 机制强一致性读)。

部署与启动

单机部署(非高可用,适合开发 / 测试)

适用场景

  • 本地开发调试、单节点临时存储、非关键业务轻量使用。
  • 不建议生产环境使用(无容错、单点故障)。

部署步骤

  • 下载二进制文件:从etcd官网下载对应系统的二进制包(以 Linux 为例)
1
2
3
wget https://github.com/etcd-io/etcd/releases/download/v3.5.10/etcd-v3.5.10-linux-amd64.tar.gz
tar -xzvf etcd-v3.5.10-linux-amd64.tar.gz
cd etcd-v3.5.10-linux-amd64
  • 启动单节点服务
1
2
3
4
5
./etcd \
--name=node1 \ # 节点名称
--data-dir=/var/lib/etcd \ # 数据存储目录
--listen-client-urls=http://0.0.0.0:2379 \ # 客户端监听地址
--advertise-client-urls=http://<机器IP>:2379 # 向客户端公开的地址
  • 验证运行状态
1
etcdctl --endpoints=http://<机器IP>:2379 endpoint status

集群部署(生产环境,高可用性)

适用场景

  • 生产环境必须使用集群(至少 3 个节点,奇数节点容错能力强,如3/5/7个节点)。
  • 支持自动选举领导者,故障节点自动剔除(需满足多数节点存活)。

部署步骤(手动指定成员列表方式)

  • 准备集群节点

3 个节点示例(IP 分别为 192.168.1.101、192.168.1.102、192.168.1.103),每个节点执行以下操作:

  • 创建配置文件

以192.168.1.101节点为例,其他节点需修改ETCD_NAME、ETCD_ADVERTISE_CLIENT_URLS和ETCD_INITIAL_ADVERTISE_PEER_URLS的值,配置文件(/etc/etcd/etcd.conf)如下:

1
2
3
4
5
6
7
8
9
ETCD_NAME="node1"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.101:2379"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380" # 节点间通信端口
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.101:2380"
ETCD_INITIAL_CLUSTER="node1=http://192.168.1.101:2380,node2=http://192.168.1.102:2380,node3=http://192.168.1.103:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" # 集群唯一标识符
ETCD_INITIAL_CLUSTER_STATE="new" # 初始化新集群
  • 启动集群
1
2
3
systemctl start etcd  # 若配置为系统服务
# 或直接运行二进制文件
./etcd --config-file=/etc/etcd/etcd.conf
  • 验证集群状态
1
2
etcdctl --endpoints=http://192.168.1.101:2379 member list
# 输出应包含所有节点,状态为 `alive`

注意:集群的部署方式还支持以动态发现的方式来部署

  • 使用 DNS 发现 或工具(如 etcd discovery service)自动获取节点列表,适合云环境动态扩缩容。
  • 核心配置项:ETCD_DISCOVERY(指定发现服务 URL),其余步骤与静态配置类似。

容器化部署

Docker部署

  • 单节点示例
1
2
3
4
5
6
7
8
9
10
docker run -d \
--name=etcd \
--net=host \ # 直接使用主机网络(避免端口冲突)
-v /var/lib/etcd:/var/lib/etcd \ # 挂载数据目录
quay.io/coreos/etcd:v3.5.10 \
etcd \
--name=node1 \
--data-dir=/var/lib/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://<主机IP>:2379
  • 集群部署示例
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
# docker-compose.yml
version: '3'
services:
etcd1:
image: quay.io/coreos/etcd:v3.5.10
command:
- etcd
- --name etcd1
- --data-dir
- /etcd-data
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd1:2379
- --listen-peer-urls
- http://0.0.0.0:2380
- --initial-advertise-peer-urls
- http://etcd1:2380
- --initial-cluster
- etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- --initial-cluster-token
- etcd-cluster
- --initial-cluster-state
- new
volumes:
- etcd-data1:/etcd-data
networks:
- etcd-network
etcd2:
# 类似 etcd1 配置,修改名称和端口映射
...
volumes:
etcd-data1:
etcd-data2:
networks:
etcd-network:

Kubernetes 部署(StatefulSet)

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
44
45
46
47
48
49
50
51
52
# etcd-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
spec:
serviceName: etcd-headless
replicas: 3
selector:
matchLabels:
app: etcd
template:
metadata:
labels:
app: etcd
spec:
containers:
- name: etcd
image: quay.io/coreos/etcd:v3.5.10
command:
- etcd
- --name=$(POD_NAME)
- --data-dir=/var/lib/etcd
- --listen-client-urls=http://0.0.0.0:2379
- --advertise-client-urls=http://$(POD_NAME).etcd-headless:2379
- --listen-peer-urls=http://0.0.0.0:2380
- --initial-advertise-peer-urls=http://$(POD_NAME).etcd-headless:2380
- --initial-cluster=etcd-0=http://etcd-0.etcd-headless:2380,etcd-1=http://etcd-1.etcd-headless:2380,etcd-2=http://etcd-2.etcd-headless:2380
- --initial-cluster-token=etcd-cluster
- --initial-cluster-state=new
volumeClaimTemplates:
- metadata:
name: etcd-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: etcd-headless
spec:
clusterIP: None
ports:
- port: 2379
name: client
- port: 2380
name: peer
selector:
app: etcd

部署注意事项

安全配置

  • 启用TLS加密(客户端与节点、节点间通信):
1
2
3
4
5
6
7
--client-cert-auth \
--trusted-ca-file=/etc/etcd/ca.pem \
--cert-file=/etc/etcd/node.pem \
--key-file=/etc/etcd/node-key.pem \
--peer-trusted-ca-file=/etc/etcd/ca.pem \
--peer-cert-file=/etc/etcd/peer.pem \
--peer-key-file=/etc/etcd/peer-key.pem
  • 启用ACL认证(通过etcdctl auth enable初始化)。

存储优化

  • 使用 SSD 存储数据目录(–data-dir),提升 IO 性能。
  • 定期执行etcdctl compactetcdctl defragment清理历史版本。

监控与备份

  • 监控指标:通过--metrics=prometheus暴露Prometheus接口,结合Grafana监控集群健康(如领导者变更、请求延迟、存储大小)。
  • 定期快照备份:
1
etcdctl snapshot save /backup/etcd_snapshot_$(date +%Y%m%d_%H%M%S).db

节点数量

  • 遵循奇数节点原则(3/5/7 个节点),容错能力为 (n-1)/2(如3节点容忍1个故障)。

常用命令

基础键值操作

  • 存储键值(PUT)
1
2
3
4
5
# 普通键(永久)
etcdctl put /config/db/url "mysql://user:pass@localhost:3306"

# 带租约的临时键(10秒后过期)
etcdctl lease grant 10 | etcdctl put --lease="$(awk '{print $2}')" /service/user/instance1 "192.168.1.1:8080"
  • 获取键值(GET)
1
2
3
4
5
6
7
8
# 获取单个键
etcdctl get /config/db/url

# 前缀查询(递归获取目录下所有键)
etcdctl get /service/user/ --prefix

# 获取历史版本( revision=2)
etcdctl get /config/db/url --rev=2
  • 删除键值(DELETE)
1
2
3
4
5
# 删除单个键
etcdctl del /config/db/url

# 删除前缀(递归删除目录)
etcdctl del /service/user/ --prefix
  • 监听变更(WATCH)
1
2
3
4
5
# 实时监听单个键
etcdctl watch /config/db/url

# 监听前缀(配置变更通知)
etcdctl watch /config/ --prefix

租约管理(临时键生命周期)

  • 创建租约
1
2
3
# 生成10秒租约(返回 lease ID)
etcdctl lease grant 10
# 输出:lease 694d4... granted with TTL(10s)
  • 绑定租约到键
1
etcdctl put --lease=694d4... /lease-key "value"
  • 续租租约
1
2
3
4
5
# 手动续租(保持键存活)
etcdctl lease keep-alive 694d4...

# 自动续租(守护进程模式)
etcdctl lease keep-alive 694d4... --forever
  • 撤销租约
1
etcdctl lease revoke 694d4...  # 立即删除绑定的键

集群状态与管理

  • 集群成员管理
1
2
3
4
5
6
7
8
# 查看成员列表
etcdctl member list

# 添加新成员(节点2加入集群)
etcdctl member add node2 --peer-urls=http://192.168.1.2:2380

# 删除故障成员(ID=123)
etcdctl member remove 123
  • 检查节点健康
1
2
3
4
5
# 查看所有节点状态(健康/故障)
etcdctl endpoint health

# 查看单个节点详细状态
etcdctl endpoint status --write-out=table
  • 查看领导者信息
1
2
etcdctl endpoint leader
# 输出:Leader is http://192.168.1.1:2379

监控与维护

  • 查看集群指标
1
2
etcdctl endpoint leader
# 输出:Leader is http://192.168.1.1:2379
  • 压缩历史版本
1
2
# 压缩到 revision=1000(释放存储空间)
etcdctl compact 1000
  • 创建快照备份
1
2
3
4
5
# 备份到文件(默认压缩)
etcdctl snapshot save etcd_backup.db

# 查看备份信息
etcdctl snapshot status etcd_backup.db
  • 从快照恢复集群
1
2
# 停止所有节点,初始化新集群
etcdctl snapshot restore etcd_backup.db --data-dir=/new/data/path

安全和认证

  • 启用认证
1
2
# 开启 ACL 认证(需重启 etcd 生效)
etcdctl auth enable
  • 管理用户与角色
1
2
3
4
5
6
7
8
9
# 创建用户(密码:pass123)
etcdctl user add admin --password=pass123

# 创建角色并授权(只读 /config/ 前缀)
etcdctl role add read-only
etcdctl role grant-permission read-only read --prefix=/config/

# 绑定角色到用户
etcdctl user grant-role admin read-only

常用选项

选项 说明
–endpoints 指定集群端点(默认:http://localhost:2379)
–prefix 前缀匹配(用于 GET/DEL/WATCH 递归操作)
–rev 指定版本号(历史查询或回滚)
–lease 绑定租约 ID(创建临时键)
–write-out=json 以 JSON 格式输出(适合脚本解析)
–cacert 启用 HTTPS 时指定 CA 证书(生产环境必填)