一、什么是ETCD
etcd是CoreOS团队于2013年6月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法,etcd基于Go语言实现,号称云原生分布式存储的基石
官网地址:https://etcd.io/
/etc 是linux 操作系统的配置存储目录, d distributed分布式缩写,简单理解为分布式配置数据库
CAP原理
也叫CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。
CAP原理的提出主要是针对分布式场景,所以P是必须具备的。
分布式键值存储系统对比
etcd | zookeeper | consul | NewSQL | |
---|---|---|---|---|
CAP | CP | CP | CP | CP |
开发语言 | go | java | go | C/C++ |
线性读 | Y | N | N | 有时候 |
多版本并发控制 | Y | N | N | 有时候 |
事务 | 数据内容比较、读或写 | 版本检查,写 | 数据内容比较、锁、读或写 | SQL式事务 |
用户权限 | RBAC | 访问控制列表 | 访问控制列表 | 每个数据库的权限和每个表的授权 |
数据更新通知 | 历史和当前健范围 | 当前键和目录 | 当前键(支持前缀) | 触发器(有时候) |
http/json api | Y | N | Y | 很少 |
最大数据库大小 | 几GB | 几百MB到几GB | 几百MB以上 | 几TB |
最小线性读时延 | 网络RTT | 不支持线性读 | 网络RTT+fsync | 取决于系统和网络时钟 |
其实eureka专注于服务发现,严格来说不算是分布式键值存储系统
NewSQL 分布式关系型数据库,主流有google公司的Cloud Spanner,CockroachDB(与PostgreSQL兼容的开源分布式SQL数据库,由熟悉Google Cloud Spanner的前谷歌员工开发),ClustrixDB(现归MariaDB所有,这个横向扩展的集群关系HTAP(混合事务/分析处理)数据库采用无共享架构设计。ClustrixDB主要与MySQL和MariaDB兼容)
总的来说,每种系统都有其优势,etcd的优势在于配置信息共享和方便运维,etcd设计理念是设计成大规模分布式系统的通用底座,因此在云原生领域得到广泛使用;Zookeeper优势在于稳定性在大数据领域应用很广;Consul的优势在于服务发现。
二、 实现原理
1.架构图
2.架构解析
从 etcd 的架构图中我们可以看到,etcd 主要分为四个部分。
- HTTP Server:用于处理用户发送的 API 请求以及其集群节点之间数据通信。
- Store:涉及KV存储,WAL文件(预写式日志),Snapshot管理等,用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等,是 etcd 对用户提供的大多数 API 功能的具体实现。
- Raft: etcd 的核心,是Raft 强一致性算法的具体实现。
- 复制状态机:抽象模块的模块,状态机的数据维护在内存中,定期持久化到磁盘,每次写请求会持久化到WAL文件,并根据写请求的内容修改状态机数据
3.数据读写过程
- 读取:由于集群所有节点数据都是强一致性的,读取可以随机读取,leader和follow 都可以读取
- 写入:如果请求发到follow会转发给leader,由leader写入,然后分发给follower
4.如何选举
- 集群开始的时候,所有节点的角色都是Follower
- 当节点在指定的时间没有收到Leader 或者 Candidate的有效消息时会发起选举投票,指定时间是选举超时时间,是一个随机的值(防止多节点同时发起选举,Leader无法被选出),心跳时间比这个时间短
- 投票过程:
1. Follower 递增Term
2. 自身状态变成Candidate
3. 投票给自己
4.向集群其它节点发起投票请求(Request Vote)
当超过集群一半节点都同意,状态变成Leader,立即向集群所有其它节点发送心跳
当发现其它Leader 节点并且Term不小于自己的term ,状态变成Follow,否则丢弃消息
数据通道
Stream类型通道: 主要用于传输数据量较小的信息,例如心跳、日志追加消息,节点之间维护长连接
Pipeline 类型通道: 主要用于处理数据量大的信息,比如snapshot,采用短连接,与心跳等消息区分处理,保证集群稳定
三、 应用场景
etcd的定位是通用的一致性key/value的存储,典型的应用场景包括分布式数据库、服务注册与发现、负载均衡、分布式锁、分布式队列、消息发布与订阅、分布式通知与协调、集群监控与leader竞选
四、 发展里程碑
etcd目前最新的是3.x版本,有3个重要里程碑版本,分别是etcd 0.4,etcd 2.0 和etcd 3.0; etcd 0.4是对外发布的第一个稳定版本,etcd2.0与etcd3.0版本差别比较,具体如下:
- v3版本采用gRPC+protobuf 取代 v2版本的Http+json的通讯方式,同时保留v2版本的方式,提高整体吞吐率,降低时延
- v3采用磁盘数据库代替v2 key-value内存数据库
- v3引入多版本的键空间(MVCC),支持访问历史版本key
- v3引入事务概念,etcd服务的多个请求合并成一个操作
- v3摒弃v2的key-value的层级和目录,采用扁平的二进制键空间
- v3更轻量级的基于租约(Lease)的key自动过期机制,取代基于TTL的key过期机制
- v3 watch机制重新设计,采用基于http2的server push,对事件进行多路复用优化
v2采用的是http长连接的事件驱动机制
etcd v3 版本依然保留etcd v2的协议和api,同时提供一套v3的API,也就是说,两个版本共享一套raft协议代码的,在于api不同,存储不同,数据相互隔离,采用v2版本API 只能用v2版本的API,v3版本只能用v3 版本的API
五、 常用操作
1. 安装部署
1) 单机部署
可以使用二进制或源码下载安装,但是需要自己写配置文件,推荐使用yum安装方式
hostnamectl set-hostname etcd-1
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
# yum 仓库中的etcd版本为3.3.11,如果需要最新版本的etcd可以进行二进制安装
yum -y install etcd #安装
systemctl enable etcd
systemctl start etcd #启动
默认配置文件vim /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379"
#ETCD_LISTEN_CLIENT_URLS="http://localhost:2379,http://xx.xx.xx.xx:2379" #如果需要其它机器访问需要添加机器ip的url
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"
2) 集群部署
集群部署分静态配置和服务发现,常用是静态配置
静态配置
1.三台机器分别安装etcd,ip分别为172.16.0.1;172.16.0.2;172.16.0.3;
yum install etcd -y
2.修改vim /etc/etcd/etcd.conf
#[Member]
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://172.16.0.1:2379" #对外提供服务地址
ETCD_LISTEN_PEER_URLS="http://172.16.0.1:2380" #监听的地址
ETCD_NAME="etcd1" # 节点名称
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://172.16.0.1:2380" #该节点同伴监听地址,这个值会告诉集群中其他节点
ETCD_ADVERTISE_CLIENT_URLS="http://172.16.0.1:2379" #对外公告的该节点客户端监听地址,同步给集群其它节点
ETCD_INITIAL_CLUSTER="etcd1=http://172.16.0.1:2380,etcd2=http://172.16.0.2:2380,etcd3=http://172.16.0.3:2380" # 集群中所有节点的信息
ETCD_INITIAL_CLUSTER_TOKEN="etcd-token" #每个集群唯一
ETCD_INITIAL_CLUSTER_STATE="new" #新建集群值为 new;已经存在的集群值为 existing
另外两个机器配置记得替换IP 172.16.0.1 和etcd1 节点名称
3.分别启动etcd服务
systemctl start etcd # 第一个节点启动会阻塞等待第二个节点启动
2. 访问安全
etcd 默认是没有开启访问控制的,访问安全包括用户的认证和授权,传输安全是指使用SSL/TLS来加密信道,本身证书也可以来做认证
1) 基于身份验证的访问控制
采用基于角色访问控制(RBAC)
etcdctl user add root #首开添加root账户,才能开始认证
etcdctl auth enable #开启认证 ,默认有root角色,拥有所有权限,授予root用户
etcdctl role add test --user=root:pwd # 添加角色
etcdctl role grant-permission --from-key test read "" --user=root:pwd #角色授予所有key读权限(read,write,readwrite)
etcdctl user grant-role test test # 用户test授予test角色
2) 基于证书的访问控制开(支持双向认证)
#[Security]
ETCD_CERT_FILE="server.pem" #服务端证书
ETCD_KEY_FILE="server-key.pem" #
ETCD_CLIENT_CERT_AUTH="true" #是否开启客户端端证书认证
ETCD_TRUSTED_CA_FILE="ca.pem" #客户端服务器TLS授信的CA证书路径,
ETCD_AUTO_TLS="true" # 客户端TLS是否使用自动生成的证书
ETCD_PEER_CERT_FILE="peer.pem" #节点通讯采用的客户端证书
ETCD_PEER_KEY_FILE="peer-key.pem"
ETCD_PEER_CLIENT_CERT_AUTH="true" #是否启用peer客户端证书认证
ETCD_PEER_TRUSTED_CA_FILE="ca.pem" 服务端TLS授信的CA证书路径
ETCD_PEER_AUTO_TLS="true" #是否使用自动生成的证书
3、 常用命令
默认安装自带etcdctl 命令行客户端,分两个版本ETCDCTL_API=2和ETCDCTL_API=3,两个版本不一样,操作的数据也不相容
以export ETCDCTL_API=3 为例
- help 查看所有命令集
etcdctl help
- put 命令
etcdctl put /test1 value1 #设置test1=value1
etcdctl put /test1 value2 # 存在直接覆盖
etcdctl put /test1 value3 --lease=1234abc #绑定租赁id,租赁时间到期自动删除
etcdctl put /test1 value4 --ignore-lease # 使用当前key的租赁
etcdctl put /test1 value5 --prev-kv #设置值,并返回上一个版本的值
- get查看
etcdctl get /test1 #获取key=/test1的值,返回key和value
etcdctl get "" --from-key #返回比空字符串大的key的键值对,该例返回所有数据
etcdctl get "dir" --prefix --keys-only --limit=5 # 返回"dir" 前缀的键最多5个
etcdctl get /test1 /test4 #获取[/test1,/test4) 范围的键值对,左闭右开,不包括/test4
etcdctl get /test1 --print-value-only #只返回值
etcdctl get /test1 --consistency='l' #获取key=/test1的键值对,consistency 'l' 代表线性读(执行raft), 's' 代表串行化读
etcdctl get /test1 -w json #查看key=/test1的版本数据,返回数据如下:
#cluster_id:请求的etcd集群ID。
#member_id:请求的etcd节点ID。
#revision:etcd服务端当前全局数据版本号。对任一key的put或delete操作都会使#revision自增1。revision=1是etcd的保留版本号,因此用户的key版本号将从2开始。
#raft_term:etcd当前raft主节点任期号。
#create_revision:当前key创建时全局数据版本号revision的值。
#mod_revision:当前key最后一次修改时全局数据版本号revision的值。
#version:当前key的数据版本号。key创建时version为1,对当前key进行put操作会使version自增1,将key删除后,重新创建,version又会从1开始计数
- del 命令
etcdctl del /test1 #删除指定key
etcdctl del /test1 --prev-kv #返回删除的键值对
etcdctl del /test --from-key # 删除比/test大的所有key,也支持--prefix 按前缀删除
- watch命令
etcdctl watch foo -- echo watch event received #watch key=foo 输出指定文字
etchctl watch foo --rev=2 #从2版本开始watch,返回中间所有修改的版本
etcdctl watch foo -- sh -c "env | grep ETCD_WATCH_"
# PUT
# foo
# bar
# ETCD_WATCH_REVISION=11
# ETCD_WATCH_KEY="foo"
# ETCD_WATCH_EVENT_TYPE="PUT"
# ETCD_WATCH_VALUE="bar"
- lease 命令(租约)
etcdctl lease grant 10 #创建一个10s的租约,返回租约的id,用于绑定key
etcdctl lease revoke 1234abc #删除id=1234abc 的租约
etcdctl lease timetolive 1234abc #查看id=1234abc 的租约剩余时间
etcdctl lease timetolive --keys 1234abc #查看id=1234abc 的租约剩余时间,并显示关联的key
etcdctl lease keep-alive -- once 1234abc #一次续约id=1234abc 的租约,并退出,不加--once 命令挂起,到期自动续约
etcdctl lease list #查看所有激活的租期
- member 命令(集群成员管理)
etcdctl member add newMember --peer-urls=https://127.0.0.1:12345
Member ced000fda4d05edf added to cluster 8c4281cc65c7b112
ETCD_NAME="newMember"
ETCD_INITIAL_CLUSTER="newMember=https://127.0.0.1:12345,default=http://10.0.0.30:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
etcdctl member update 2be1eb8f84b7f63e --peer-urls=https://127.0.0.1:11112
etcdctl member remove 2be1eb8f84b7f63e
etcdctl member list #查看集群列表
- endpoint 查看节点状态
etcdctl endpoint status -w table #查看默认节点状态,table格式输出
etcdctl endpoint --cluster status -w table # 查看集群状态
etcdctl endpoint --cluster hashkv -w json #查看集群hashkv
六、 集群故障处理
1. 数据备份
etcdctl snapshot save backup.db #数据备份到当前目录下backup.db
etcdctl -w table snapshot save backup.db
2. 数据恢复
etcdctl --data-dir=/var/lib/etcd/backup.etcd snapshot restore backup.db #恢复数据到新目录
#每个节点恢复数据,然后修改配置指定新的数据目录,最后重启所有节点
注意数据目录权限,权限不够,提示不是很友好
3. 数据迁移
场景1 更换高性能盘
1.停掉etcd服务
2.高性能盘上创建新的数据目录
3.copy旧数据文件到新数据目录中
4.修改配置文件的数据目录为新目录,然后重启服务
场景2 更换高性能物理机器,不停服切换
1.新的机器先安装好etcd服务
2.先启动安装好的一台etcd,单独起来,然后查看etcd的member的ID
3.到原有的三台etcd集群上,将第二部步中起来的etcd添加到集群(添加节点需要用到etcd的member ID 和peer-urls地址)
4.将原来三台的etcd的ETCD_INITIAL_CLUSTER地址添加进来,将ETCD_INITIAL_CLUSTER_STATE的new改为existing,删除数据目录,重启etcd
5.其它两台重复2-4步骤
6.确保新的三台已加入集群且数据一致,然后把原有三台剔除集群
运行时的重配置都需要经过两个阶段,1.先通知集群新的配置,采用member api 操作,2.启动新的节点,指定正确的initial-cluster,initial-cluster-state设置为existing,新节点启动会校验initial-cluster与当前集群的配置匹配
场景3 直接拿着etcd快照,进行etcd集群恢复
1.获取快照
2.先将新的三台组成集群,启动起来
3.用etcd快照恢复到新目录,在三台机器上都执行
4.分别修改目录配置到新目录,最后重启etcd