前言
目前解决分布式系统下数据强一致性的主要算法理论是Paxos和Raft,偏向CAP定理一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)中的CP。
Raft在容错和性能方面和Paxos相当,不同之处在于它将问题分解成相对独立的子问题,逻辑较为清晰,更易于理解。关于Raft 详细介绍参考 GitHub - raft。
Etcd是遵循Raft算法理论开发出来的高一致性分布式存储,功能和遵循Paxos简化版ZAB(Zookeeper Atomic Broadcast)协议开发出来的Zookeeper差不多。
Etcd用是键(Key)值(Value)存储,类似文档的存储结构,支持SSL认证(ZK不支持),基准测试为 10,000
次写入/秒。
可以用来做服务注册与发现、消息发布与订阅、负载均衡、分布式通知与协调、分布式锁、leader 选举等。
如果文章有帮助,可以随手关注、点赞、转发下,一起学习进步,祝大家顺利。
安装步骤
我们在这里计划使用3台服务器node1(172.20.0.2)、node2(172.20.0.3)、node3(172.20.0.4)部署3个Etcd节点。
文内很多配置使用了域名(node1、node2、node3)来代替IP地址,这个需要提前在/etc/hosts
下配置并使其生效。比如:
echo '追加host配置' > /dev/null
echo '
172.20.0.2 node1
172.20.0.3 node2
172.20.0.4 node3' >> /etc/hosts
echo '使host配置生效' > /dev/null
/etc/init.d/network restart
Etcd需要用到接收客户端请求端口2379
和节点间通信端口2380
,需要配置安全组规则或者防火墙来保证端口畅通。
安装
用YUM安装Etcd。
echo '安装' > /dev/null
yum install etcd -y
查看Etcd命令行客户端etcdctl
的帮助信息会看到一行警告(WARNING),意思是系统环境变量没有指定etcdctl
的应用程序接口(Application Programming Interface, API)版本,现在默认使用的是v2
版。
etcdctl -h
我们追加环境变量来指定使用v3
版的API。两个版本的API使用方式有很多区别,如果更喜欢v2
可以不改,或者将环境变量设置成2
,文章后面的脚本就需要修改成v2
的。
echo '追加环境变量' > /dev/null
echo '
export ETCDCTL_API=3' >> /etc/profile
echo '使环境变量生效' > /dev/null
source /etc/profile
echo '查看版本' > /dev/null
etcdctl version
etcdctl
可以在任意Etcd节点使用,通过命令行可以查看简单帮助(etcdctl -h
)。建议参考 GitHub - etcdctl,非常详细,可以直接看到演示效果。
配置
Etcd配置文件完整路径是/etc/etcd/etcd.conf
,这里只使用了必要的配置参数,所有配置项的详细信息参考 Etcd - 配置。
不同节点需要改这些配置项ETCD_NAME、ETCD_DATA_DIR、ETCD_LISTEN_PEER_URLS、ETCD_LISTEN_CLIENT_URLS、ETCD_INITIAL_ADVERTISE_PEER_URLS、ETCD_ADVERTISE_CLIENT_URLS
。
echo '备份配置' > /dev/null
cp /etc/etcd/etcd.conf /etc/etcd/etcd.conf.bak
echo '覆盖配置文件中的内容' > /dev/null
echo '#[Member]
# 当前etcd名字,可以自定义
ETCD_NAME="node1"
# 数据文件目录,可以自定义,这里是根据[ETCD_NAME].etcd
ETCD_DATA_DIR="/var/lib/etcd/node1.etcd"
# 用于监听其他etcd消息的当前etcd地址列表,多个IP或端口号不同的地址用逗号分隔。IP指定为0.0.0.0相当于监听所有网卡接口的同一个端口,IP不能是域名
ETCD_LISTEN_PEER_URLS="http://172.20.0.2:2380"
# 用于监听客户端消息的当前etcd地址表,和ETCD_LISTEN_PEER_URLS要求一致
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379,http://172.20.0.2:2379"
#[Clustering]
# 告诉其他etcd,当前etcd通信的地址列表,也就是ETCD_LISTEN_PEER_URLS中的全部或部分地址,可以用域名
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://node1:2380"
# 告诉其他etcd,当前etcd与客户端通信的地址列表,也就是ETCD_LISTEN_CLIENT_URLS中的全部或部分地址,可以用域名
ETCD_ADVERTISE_CLIENT_URLS="http://node1:2379"
# 集群中初始成员(etcd),格式为[ETCD_NAME]=[ETCD_LISTEN_PEER_URL],可以用域名
ETCD_INITIAL_CLUSTER="node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380"
# 集群初始令牌,每个集群的令牌唯一,集群中所有etcd用相同令牌
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
# 可选值为new|existing,加入已存在的集群用existing
ETCD_INITIAL_CLUSTER_STATE="new"' > /etc/etcd/etcd.conf
用于监听客户端消息的当前etcd地址表ETCD_LISTEN_CLIENT_URLS
配置http://localhost:2379
是因为etcdctl
默认是通过127.0.0.1:2379
来连接Etcd的,不配置那就意味着Etcd没有监听这个地址,etcdctl
执行相关命令会连接不上报错。
Error: dial tcp 127.0.0.1:2379: connect: connection refused
启停操作
启动的时候要至少过半的节点启动才会成功,否则执行命令后会卡住不动,因为Etcd遵循Raft协议,要过半节点才能选主和执行写操作。
# 启动
systemctl start etcd
# 启动/重启
systemctl restart etcd
# 停止
systemctl stop etcd
测试
查看集群成员列表
echo '查看集群所有成员状态' > /dev/null
etcdctl -w table endpoint status --cluster
从结果可以看到每个成员的地址(ENDPOINT
)、ID、是否主节点(IS LEADER
)等信息。Etcd是基于Raft分布式一致性协议实现的,我们从结果还可以看到任期(RAFT TERM
)和日志索引(RAFT INDEX
)。
重新选举
从上面结果看到node1为主节点,我们在node1执行下面语句,停掉node1的Etcd,并查看node2和node3的节点状态。
echo 'etcd停止' > /dev/null
systemctl stop etcd
echo '查看node2和node3状态' > /dev/null
etcdctl -w table endpoint status --endpoints=node2:2379,node3:2379
从下面结果可以看到node2成为了主节点,集群进入了下一个任期4
。
我们通过node1重启它的Etcd,然后看看所有成员的状态。
systemctl start etcd
etcdctl -w table endpoint status --cluster
可以看到主节点不变,还是node2。关注日志索引(RAFT INDEX
)可以看到,选主也会导致它自增。
读写数据
任意节点写入的值,都可以在其他节点看到。执行下面脚本前后查看日志索引(RAFT INDEX
)会发现值+2
,因为有两次写操作put
和del
。
echo '写入键值对name=china' > /dev/null
etcdctl put name china
echo '获取键为name的值' > /dev/null
etcdctl get name
echo '获取所有键值对' > /dev/null
etcdctl get --from-key ''
echo '删除键name' > /dev/null
etcdctl del name