etcd教程-快速入门使用(截图实操)集群搭建 + 原理解释

etcd教程-快速入门使用(截图实操)集群搭建 + 原理解释

大家好,我是此林。

etcd 是一个高可用的键值对存储系统,常用于分布式系统中保存配置、服务发现和协调信息。它是 CNCF 旗下的项目之一,也是 Kubernetes 的核心组件之一,用来存储集群状态。

可以说,云原生场景下经常使用到 etcd,一般我们会把 etcd 作为注册中心来使用。

1. etcd 是什么?

etcd 单词可以拆分成 etc + d,etc 就是 Linux 里的 /etc 目录,d 就是 distribution,代表分布式。

使用 Raft 一致性算法保证分布式系统数据一致性(强一致性、保障了CP)

提供 键值对存储接口(KV)

支持 Watch 机制(监听键变化)

提供 Lease / TTL(用于临时键,比如服务注册)

2. etcd 常见使用场景

场景说明配置中心分布式系统中的配置动态变更通知服务发现服务注册自己的信息,客户端从 etcd 查询分布式锁利用 etcd 的原子操作实现分布式锁领导选举通过 Lease 和 Watch 实现 leader 的自动选举和变更通知Kubernetes 存储K8s 所有 API 对象最终存储在 etcd 中

3. etcd 安装

这里使用版本 3.5.21。

Release v3.5.21 · etcd-io/etcd

下载完成后,上传到 Linux 服务器,使用下面的命令解压。

tar -zxvf etcd-v3.5.21-linux-amd64.tar.gz

进入目录后,其实最核心的也就是三个二进制可执行文件,etcd(主服务Server端),etcdctl(etcd客户端连接工具),etcdutl(etcd工具集)。

其他都是些文档。

把这三个二进制文件复制到 /usr/local/bin 里。

# 创建目标目录(如果不存在)

sudo mkdir -p /usr/local/bin

# 赋予可执行权限

chmod +x etcd etcdctl etcdutl

# 复制到系统路径

sudo cp etcd etcdctl etcdutl /usr/local/bin/

配置环境变量,/usr/local/bin 加入 /etc/profile 文件末尾。

vim /etc/profile

文件末尾有这一行就行。

export PATH=$PATH:/usr/local/bin

使环境变量生效。

source /etc/profile

4. 快速开始

输入 etcd 启动 etcd 服务端。

打开另一个终端,输入命令:

etcdctl put greeting "hello, etcd"

etcdctl get greeting

当然,也可以格式化输出为 json。

etcdctl --endpoints=$ENDPOINTS --write-out="json" get foo

输出:

{"header":{"cluster_id":289318470931837780,"member_id":14947050114012957595,"revision":3,"raft_term":4,

"kvs":[{"key":"Zm9v","create_revision":2,"mod_revision":3,"version":2,"value":"SGVsbG8gV29ybGQh"}]}}

看到这里,恭喜你,已经 etcd 入门了!

5. 角色用户配置

下面是官方文档给出的配置示例,不急,我们慢慢来看。

1. 设置环境变量

export ETCDCTL_API=3

# 设置使用 etcdctl 的 v3 API(新版 etcd 默认使用 v3)

# 这个只在当前shell生效,重启会失效,永久设置要在/etc/profile里

ENDPOINTS=localhost:2379

# 设置 etcd 访问地址(如果是集群也可写多个:127.0.0.1:2379,127.0.0.2:2379)

2. 创建 root 角色并查看

etcdctl --endpoints=${ENDPOINTS} role add root

# 添加一个名为 root 的角色

etcdctl --endpoints=${ENDPOINTS} role get root

# 查看 root 角色的权限信息(刚创建时是空的)

可以发现,root 角色有键值对读写权限。

3. 创建 root 用户并绑定 root 角色

etcdctl --endpoints=${ENDPOINTS} user add root

# 添加一个用户 root(执行后会提示你输入密码)

etcdctl --endpoints=${ENDPOINTS} user grant-role root root

# 给用户 root 分配 root 角色

etcdctl --endpoints=${ENDPOINTS} user get root

# 查看用户 root 的角色信息

4. 创建 role0 角色并设置权限,创建 user0 用户并赋予角色

etcdctl --endpoints=${ENDPOINTS} role add role0

# 添加一个角色 role0

etcdctl --endpoints=${ENDPOINTS} role grant-permission role0 readwrite foo

# 给角色 role0 授予对 key "foo" 的读写权限(readwrite)

# 你也可以设定范围权限,例如:--prefix

etcdctl --endpoints=${ENDPOINTS} user add user0

# 添加用户 user0,会提示输入密码

etcdctl --endpoints=${ENDPOINTS} user grant-role user0 role0

# 将角色 role0 分配给用户 user0

这里,我们使用 grant-permission 对 role0 的权限进行了修改,role0 只有对 key "foo" 才有读写权限

5. 启用认证

etcdctl --endpoints=${ENDPOINTS} auth enable

# 启用 etcd 的认证功能

# 启用后,所有的读写请求必须提供用户名和密码,否则会被拒绝

6. 权限验证测试

etcdctl --endpoints=${ENDPOINTS} --user=user0:123 put foo bar

# 使用 user0 用户(密码是 123)写入 key "foo",写入成功(因为具有读写权限)

etcdctl --endpoints=${ENDPOINTS} get foo

# 不带用户名直接访问 key "foo",失败(因为认证开启了,默认匿名用户无权限)

etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo

# 使用 user0 读取 key "foo",成功(因为拥有读权限)

etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo1

# 使用 user0 读取 key "foo1",失败(权限只对 foo 有效,foo1 不在授权范围)

可以看到,user0 对 key foo1,没有读写权限。

上面这个示例建议亲手去敲一遍,有助于快速理解角色-用户模型。

6. 集群搭建

6.1. 方法一:静态集群配置(推荐用于生产)

在 /etc/etcd 目录下创建三个配置文件(这里就用一台机器上不同端口来模拟集群)

etcd-node1.yaml

name: node1

data-dir: /tmp/etcd1

listen-peer-urls: http://127.0.0.1:2380

listen-client-urls: http://127.0.0.1:2379

initial-advertise-peer-urls: http://127.0.0.1:2380

advertise-client-urls: http://127.0.0.1:2379

initial-cluster: node1=http://127.0.0.1:2380,node2=http://127.0.0.1:3380,node3=http://127.0.0.1:4380

initial-cluster-token: local-etcd-cluster

initial-cluster-state: new

etcd-node2.yaml

name: node2

data-dir: /tmp/etcd2

listen-peer-urls: http://127.0.0.1:3380

listen-client-urls: http://127.0.0.1:3379

initial-advertise-peer-urls: http://127.0.0.1:3380

advertise-client-urls: http://127.0.0.1:3379

initial-cluster: node1=http://127.0.0.1:2380,node2=http://127.0.0.1:3380,node3=http://127.0.0.1:4380

initial-cluster-token: local-etcd-cluster

initial-cluster-state: new

etcd-node3.yaml

name: node3

data-dir: /tmp/etcd3

listen-peer-urls: http://127.0.0.1:4380

listen-client-urls: http://127.0.0.1:4379

initial-advertise-peer-urls: http://127.0.0.1:4380

advertise-client-urls: http://127.0.0.1:4379

initial-cluster: node1=http://127.0.0.1:2380,node2=http://127.0.0.1:3380,node3=http://127.0.0.1:4380

initial-cluster-token: local-etcd-cluster

initial-cluster-state: new

集群启动

etcd --config-file=/etc/etcd/etcd-node1.yaml

在每个终端分别执行命令。

参数说明:

1. initial-cluster-token: local-etcd-cluster

etcd 集群的初始化 token,用于标识一个新的集群。所有节点必须使用相同的 token 才能加入该集群。

2. initial-cluster-state: new

指定 etcd 是启动一个新的集群(new),还是加入一个已有的集群(existing)。 new:表示初始化新的 etcd 集群(初次部署) existing:表示已有集群的成员(用于节点重启或扩容)

3. name: node3

分别为三个 etcd 节点指定唯一的名称,后面用于集群识别。

4. data-dir: /tmp/etcd3

数据存储目录,etcd会在此目录保存所有数据

# 监听peer通信的URL(集群节点间内部通信)

listen-peer-urls: http://127.0.0.1:4380

# 监听客户端请求的URL(应用程序连接etcd的地址)

listen-client-urls: http://127.0.0.1:4379

# 向集群其他节点宣告的peer通信地址

# 必须可从其他节点访问,通常与listen-peer-urls一致

initial-advertise-peer-urls: http://127.0.0.1:4380

# 向客户端宣告的服务地址

# 必须可从客户端访问,通常与listen-client-urls一致

advertise-client-urls: http://127.0.0.1:4379

# 初始集群配置(集群所有节点的通信地址)

initial-cluster: node1=http://127.0.0.1:2380,node2=http://127.0.0.1:3380,node3=http://127.0.0.1:4380

6.2 方法二:基于 discovery token 的动态发现(适合临时集群)

简单来说,方法二适合部署时不提前知道所有成员 IP 的情况。

这里的 discovery URL 每次使用都要新生成(不可复用)!

具体可以参考官方文档,这里不多演示。

How to Set Up a Demo etcd Cluster | etcd

curl https://discovery.etcd.io/new?size=3

https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92

# grab this token

TOKEN=token-01

CLUSTER_STATE=new

NAME_1=machine-1

NAME_2=machine-2

NAME_3=machine-3

HOST_1=10.240.0.17

HOST_2=10.240.0.18

HOST_3=10.240.0.19

DISCOVERY=https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92

THIS_NAME=${NAME_1}

THIS_IP=${HOST_1}

etcd --data-dir=data.etcd --name ${THIS_NAME} \

--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \

--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \

--discovery ${DISCOVERY} \

--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

THIS_NAME=${NAME_2}

THIS_IP=${HOST_2}

etcd --data-dir=data.etcd --name ${THIS_NAME} \

--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \

--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \

--discovery ${DISCOVERY} \

--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

THIS_NAME=${NAME_3}

THIS_IP=${HOST_3}

etcd --data-dir=data.etcd --name ${THIS_NAME} \

--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \

--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \

--discovery ${DISCOVERY} \

--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

现在 etcd 启动了,开始测试吧!

export ETCDCTL_API=3

ENDPOINTS=127.0.0.1:2379,127.0.0.1:3379,127.0.0.1:4379

etcdctl --endpoints=$ENDPOINTS endpoint status --write-out=table

可以看到,测试成功了,列出了所有节点。node1 是 leader,node2和node3是 follower。

6.3. 集群模式:Raft 共识算法

etcd 是一个 强一致性(strongly consistent) 的分布式键值存储系统,但它不是传统意义上的主从结构,而是基于 Raft 共识算法 构建的。

etcd 集群基于 Raft 算法,每个节点(member)在某一时刻的角色可能是以下之一:

角色含义Leader当前集群的主节点,负责接收客户端写请求,并将数据复制给 Follower。整个集群 只能有一个 Leader。Follower普通成员节点,响应来自 Leader 的日志复制和心跳,不能单独处理写入。Candidate 候选者,用于发起 Leader 选举的过渡角色。

这个就解决了传统主从集群模式下主节点宕机了,整个集群就不可用的问题。etcd 集群可以自动进行故障恢复,自动投票选出新的 master 节点。

etcd 的一致性模型是:

线性一致性(Linearizability):所有客户端看到的顺序与 Raft 日志顺序一致。

也就是说:

所有写操作必须通过 Leader;

Leader 会将写请求复制到多数派(超过半数)节点,才会被“提交”(解决了集群脑裂)

etcd(Raft)传统主从节点角色Leader + Follower + CandidateMaster + Slave容错方式多数节点存活即可服务(如 3 节点需 ≥2 存活)Master 挂了要人工或脚本切换数据复制同步复制,多数派一致后才提交主写从读,通常异步复制一致性强一致性(线性一致)最终一致性(可能存在延迟)

再看下之前的图:

字段名称说明示例值重要性​​ID​​节点的唯一标识符(16进制格式)84724583e7fe06d8用于识别集群中的特定节点​​VERSION​​etcd 服务器版本号3.5.21集群所有节点版本应一致​​DB SIZE​​后端数据库的物理大小20 kB监控存储增长的关键指标​​IS LEADER​​当前是否为领导者节点true/false集群健康关键指标(必须有且只有一个leader)​​IS LEARNER​​是否为学习者节点(非投票成员)false学习者节点不参与选举,用于灾备或读扩展​​RAFT TERM​​当前任期号(每次选举递增)2数值越大表示集群经历的领导选举次数越多​​RAFT INDEX​​当前最高日志条目索引号9表示写入操作的序列号​​RAFT APPLIED INDEX​​已应用到状态机的最高日志索引9应与RAFT INDEX接近,差值大表示有未应用的操作​​ERRORS​​节点错误信息(为空表示正常)``出现内容时表示节点异常

7. 其他功能特性

7.1 prefix 前缀

1. 通过 prefix 前缀查找 key

2. 通过 --preifx 删除 key

etcdctl --endpoints=$ENDPOINTS put k1 value1

etcdctl --endpoints=$ENDPOINTS put k2 value2

etcdctl --endpoints=$ENDPOINTS del k --prefix

7.2 etcd 事务

因为 etcd 本身就是 raft 强一致性的中间件,保证了 CAP 理论里的 CP。etcd 无论单机还是集群都支持事务,它内部使用了 MVCC 多版本并发控制机制。

Redis 保证了 CAP 理论里的 AP,属于最终一致性。

Redis 通过 multi + watch 命令并 不能保障事务,Redis 官方文档里说 Redis 设计就是为了高性能,一致性保障并不是最主要的。

​​MULTI+WATCH 的缺陷​​

仅提供乐观锁(CAS),若 WATCH 的键被其他客户端修改,事务会失败。无回滚机制,失败后需手动重试。

所以 Redis 里实现事务只能通过 Lua 脚本 来实现。

​​Lua 脚本的原子性​​

虽然单线程执行 Lua 能保证原子性,但:不支持跨键事务(所有操作必须在同一个脚本中)也就是说 cluster 分片集群模式下,lua 脚本可能无法保障事务。

场景举例:

你要同时更新一个用户的邮箱和手机号

# 插入数据

etcdctl put /users/12345/email "old.address@johndoe.com"

etcdctl put /users/12345/phone "123-456-7890"

# 开启事务

etcdctl txn --interactive

compares:

# 这里除了用value(),也可以使用version()版本号机制

value("/users/12345/email") = "old.address@johndoe.com"

success requests (get, put, delete):

put /users/12345/email "new.address@johndoe.com"

put /users/12345/phone "098-765-4321"

failure requests (get, put, delete):

get /users/12345/email

测试:

1. Atomicity(原子性)

事务是原子的,要么所有操作都成功,要么一个都不执行。

只有 value("/users/12345/email") 等于 "old.address@johndoe.com"时,邮箱和手机号才会一起更新。 如果不是,则不会更新任何内容。

这保证了不会出现只更新了一半字段的尴尬情况。

2. Consistency(一致性)

3. 注意点:避免在同一事务中对同一个 key 多次 put

❌ 错误示例:

# compares:

value(counter) = "1"

# success requests:

put counter 2

put counter 3 # 同一个事务里又对 counter 执行一次 put,会导致冲突或不可预期结果

这种情况虽然可能不会报错,但有时你不知道最终会落哪个值。etcd 没法保证顺序处理多次对同一个 key 的写入。

✅ 正确方式:

每个事务里对同一个 key 最多只能 put 一次。

7.3. watch 实时监听 key

etcdctl watch stock1

7.4. 设置 lease 租约

类似 redis 的 TTL 机制,但更强大(支持一个 leaseID 多键绑定、续租等)。

# 1. 创建一个 TTL=300秒 的租约(返回 Lease ID)

etcdctl lease grant 300

→ lease 2be7547fbc6a5afa granted with TTL(300s)

# 2. 将键值对 "sample=value" 绑定到这个租约

etcdctl put sample value --lease=2be7547fbc6a5afa

# 3. 获取键值对(此时可以正常读取)

etcdctl get sample

→ sample

→ value

# 4. 手动续租(保持 Lease 存活,即重置为300秒)

etcdctl lease keep-alive 2be7547fbc6a5afa

# 5. 主动撤销租约(立即删除所有关联的键)

etcdctl lease revoke 2be7547fbc6a5afa

# 6. 等待 300秒后(或撤销后),键值对自动消失

etcdctl get sample

→ (无输出,键已被删除)

7.5. 分布式锁

# 客户端1

etcdctl lock lock1

# 客户端2,此时会阻塞,直到客户端1释放锁

etcdctl lock lock1

7.6. 健康检测

etcdctl endpoint status (--endpoints=$ENDPOINTS|--cluster)

etcdctl endpoint health (--endpoints=$ENDPOINTS|--cluster)

关于etcd的分享就到这里了。

我是此林,关注我吧,带你看不一样的世界!

相关推荐

「吉拉新几级进化」吉拉特性 365体育投注账号被冻结

「吉拉新几级进化」吉拉特性

📅 06-28 👁️ 148
创世战车是哪个国家的介绍 365体育投注账号被冻结

创世战车是哪个国家的介绍

📅 07-01 👁️ 9649