Etcd 是一个强一致性的分布式 key-value 存储服务,提供了一种可靠的方式来存储需要由分布式系统或集群访问的数据。在网络分区时可以优雅地处理 Leader 选举,并且可以容忍机器故障,即是 Leader 节点也是一样。
这是 Etcd 官网中对 etcd 的介绍。Etcd 被广泛用于 K8s 系统中,已经成为一个广泛使用的分布式协调软件。这次主要是介绍下 Etcd 的一些安装及使用特性。
还是习惯性的使用 Docker 来辅助搭建,Etcd 的 Github 已经提供了安装教程。我把根据官网搞得 Dockerfile 贴一下,由于要在一个机器上开多个实例所以主要是参考了 Linux 系统的安装,而不是官网的 Docker安装方式。
# 引用的基础镜像
FROM myos
# 构建默认工作目录
RUN mkdir /home/admin/etcd
WORKDIR /home/admin/etcd
## 安装etcd
## 定义变量
ENV ETCD_VER=v3.5.0
ENV GOOGLE_URL=https://storage.googleapis.com/etcd
ENV DOWNLOAD_URL=$GOOGLE_URL
## 创建临时存放路径,下载并解压缩
RUN rm -f /tmp/etcd-$ETCD_VER-linux-amd64.tar.gz
RUN rm -rf /tmp/etcd-download && mkdir -p /tmp/etcd-download
RUN curl -L $DOWNLOAD_URL/$ETCD_VER/etcd-$ETCD_VER-linux-amd64.tar.gz -o /tmp/etcd-$ETCD_VER-linux-amd64.tar.gz
RUN tar xzvf /tmp/etcd-$ETCD_VER-linux-amd64.tar.gz -C /tmp/etcd-download --strip-components=1
RUN rm -f /tmp/etcd-$ETCD_VER-linux-amd64.tar.gz
RUN mv /tmp/etcd-download/etcd /usr/local/bin/ \
&& mv /tmp/etcd-download/etcdctl /usr/local/bin/ \
&& mv /tmp/etcd-download/etcdutl /usr/local/bin/
然后需要修改 Etcd 的配置文件,etcd.conf.yml。etcd 目前默认使用 2379 端口提供 HTTP 服务,2380 端口和 peer 通信(这两个端口已经被 IANA 官方预留给 Etcd)。但是为了好替换,参照了 Redis Cluster 的端口配置方式,将 2380 端口改成了 12379。修改的配置文件如下:
# 节点名称
name: 'node-2379'
# 数据保存的路径
data-dir: /home/admin/etcd/2379
# 成员之间通信的地址
listen-peer-urls: http://localhost:12379
# 该节点成员对等URL地址,且会通告群集的其余成员节点
initial-advertise-peer-urls: http://localhost:12379
# 集群中所有节点的信息
initial-cluster: node-2371=http://localhost:12371,node-2372=http://localhost:12372,node-2373=http://localhost:12373
然后执行如下命令,获得三个不同的配置文件,并启动,这样就开启了3个不同的实例。
sed 's/2379/2371/g' /home/admin/etcd/etcd.conf.yml > /home/admin/etcd/2371/etcd.conf.yml
sed 's/2379/2372/g' /home/admin/etcd/etcd.conf.yml > /home/admin/etcd/2372/etcd.conf.yml
sed 's/2379/2373/g' /home/admin/etcd/etcd.conf.yml > /home/admin/etcd/2373/etcd.conf.yml
etcd --config-file ./2371/etcd.conf.yml
etcd --config-file ./2372/etcd.conf.yml
etcd --config-file ./2373/etcd.conf.yml
然后我们运行 “etcdctl --endpoints 127.0.0.1:2371 member list -w 'table'”,这样就可以看到各个节点的状态了,这里有一列是 IS_LEARNER , 不是 IS_LEADER,不要弄混了,下面再详细介绍。
集群状态运行“etcdctl --endpoints 127.0.0.1:2371,127.0.0.1:2372,127.0.0.1:2373 endpoint status -w 'table'”,可以看到集群的整个状态。可以看到 2371 节点现在是 Leader 节点,Raft Term 是 2。
leader节点Leader Election
然后我们停掉 Leader 节点,观察下 Leader 选举过程。
leader election可以看到整个过程如下,整个过程完全遵照 Raft 协议执行:
- 在 term 2 时期从 Leader 节点 2371 接收心跳超时
- 开始一个新的任期选举
- 在新的 term 3 中 2372 节点先变成 Candidate 节点
- 首先收到了自己对自己的投票
- 向另外两个节点(包含停掉的 Leader 节点)发送投票请求
- 收到了另外一个存活节点 2373 的投票结果
- 现在收到了2张投票,0张反对票
- 由于2 > (3/2) 符合多数派原则,2372 节点变成 term 3的新 Leader 节点
同时我们也看一下另外一个存活的节点 2373 的日志。
其他节点可以看到首先在新的 term 3 中收到一个 2372 投票请求,由于term 3 是较新的 term,所以会先变为 term 3 的 Follower 节点。由于在 term 3 中并未投票,所以会投票给 2372 节点,这样就选举出来了新 Leader 2372。
这时候我们再运行“etcdctl --endpoints 127.0.0.1:2371,127.0.0.1:2372,127.0.0.1:2373 endpoint status -w 'table'”命令
可以看到 2372 节点成为集群里面新的 Leader ,而 RAFT TERM 也变成了3。
接着我们把之前停掉的的 2371 节点重新启动,在运行上面的命令。可以看到 2373 节点由加入到了集群中,并且变成了 Follower 节点,RAFT TERM 也跟随集群变成3。
其他说明
Learner节点
Learner 是 Etcd 3.4 版本中增加的新节点状态,类似于 Zookeeper 的 observer, 不参与 Raft 投票选举。通过这个新角色的引入,降低了加入新节点时给老集群的额外压力,增强了集群的稳定性。除此之外还可以使用它作为集群的热备或服务一些读请求。
如果 Etcd 集群需要加入一个新节点,新加入的成员因为没有任何数据,因此需要从 Leader 同步数据,直到赶上 Leader 的日志为止。这样可能会导致 Leader 的网络过载,从而导致 Leader 和 Follower 之间的心跳阻塞。然后就开始了新的 Leader Election,也就是说,具有新成员的集群更容易受到 Leader Election 的影响。Leader Election 以及随后向新成员的更新都容易导致一段时间的集群不可用。
为了解决这个问题,Raft 4.2.1 论文中介绍了一种新的节点角色:Learner。加入集群的节点不参与投票选举,只接收 Leader 的 Replication Message,直到与 Leader 保持同步为止。
Learner 在网络分区等场景下的处理,可以详细参考:https://etcd.io/docs/v3.3.12/learning/learner/
Etcd server 会验证 promote 请求以确保真实在提升之前,Learner 仅充当备用节点,Leader 无法转移给Learner。Learner 拒绝客户端读写(客户端平衡器不应将请求路由到Learner)另外,Etcd 限制了集群可以拥有的 Learner 总数,并避免了日志复制导致领导者过载。Learner 永远不会自我提升。
分区脑裂
Etcd 中不存在分区脑裂问题。
分区后少数派由于无法获取多数派的响应,所以肯定无法执行写操作(CP 系统,无法保证可用性)。那么读请求呢?答案是:默认无法读取!
Etcd 默认使用 ReadIndex 及 LeaseRead 的机制来实现“线性一致性读”。其原理就是 Etcd 在处理读请求时,那么它必须先要向 Leader 去查询 commit index 当 apply index 大于等于 commit index 时候才可以读取数据。此时少数派联系不上 Leader,所以必然会失败。但是这时候是支持非一致性读请求的。不过需要开启一个选项:
- SDK 访问需要配置 WithSerializable 选项(默认并不开启)
- etcdctl 访问需要配置 --consistency=s 选项
当网络分区时候如果 Leader 被分在了少数派,由于在每次心跳时候无法获取多数派的响应,所以少数派的 Leader 无法确认自己的身份,也就无法支持查询 commit index。
网友评论