etcd 是一个分布式一致性k-v存储系统,可用于服务注册发现与共享配置,具有以下优点。
简单 : 相比于晦涩难懂的paxos算法,etcd基于相对简单且易实现的raft算法实现一致性,并通过gRPC提供接口调用
安全:支持TLS通信,并可以针对不同的用户进行对key的读写控制
高性能:10,000 /秒的写性能
1. etcd单机模式
单机模式的etcd运行很简单,只需到官网下载最新的二进制文件,执行即可
./bin/etcd
以上将使用默认配置运行etcd,监听本地的2379端口,用于与client端交互,监听2380用于etcd内部交互,当然,这里单机不会使用到。
./etcdctl set hello world
world
./etcdctl get hello
world
通过etcdctl工具进行测试,以上表示单机可以工作。
2.etcd集群模式
这里的集群模式是指完全集群模式,当然也可以在单机上通过不同的端口,部署伪集群模式,只是那样做只适合测试环境,生产环境考虑到可用性的话需要将etcd实例分布到不同的主机上,这里集群搭建有三种方式,分布是静态配置,etcd发现,dns发现
下面将在以下三台主机上搭建etcd集群,
infra0 10.0.1.111
infra1 10.0.1.109
infra2 10.0.1.110
2.1 配置项说明
--name
etcd集群中的节点名,这里可以随意,可区分且不重复就行
--listen-peer-urls
监听的用于节点之间通信的url,可监听多个,集群内部将通过这些url进行数据交互(如选举,数据同步等)
--initial-advertise-peer-urls
建议用于节点之间通信的url,节点间将以该值进行通信。
--listen-client-urls
监听的用于客户端通信的url,同样可以监听多个。
--advertise-client-urls
建议使用的客户端通信url,该值用于etcd代理或etcd成员与etcd节点通信。
--initial-cluster-token etcd-cluster-1
节点的token值,设置该值后集群将生成唯一id,并为每个节点也生成唯一id,当使用相同配置文件再启动一个集群时,只要该token值不一样,etcd集群就不会相互影响。
--initial-cluster
也就是集群中所有的initial-advertise-peer-urls 的合集
--initial-cluster-state new
新建集群的标志
2.2 静态配置
静态配置主要预先将集群的配置信息分配好,然后将集群分布启动,集群将根据配置信息组成集群。这里按如下的配置信息分别启动三个etcd。
./etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.111:2380 \
--listen-peer-urls http://10.0.1.111:2380 \
--listen-client-urls http://10.0.1.111:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.111:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra0=http://10.0.1.111:2380,infra1=http://10.0.1.109:2380,infra2=http://10.0.1.110:2380 \
--initial-cluster-state new
./etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.109:2380 \
--listen-peer-urls http://10.0.1.109:2380 \
--listen-client-urls http://10.0.1.109:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.109:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra0=http://10.0.1.111:2380,infra1=http://10.0.1.109:2380,infra2=http://10.0.1.110:2380 \
--initial-cluster-state new
./etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.110:2380 \
--listen-peer-urls http://10.0.1.110:2380 \
--listen-client-urls http://10.0.1.110:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.110:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra0=http://10.0.1.111:2380,infra1=http://10.0.1.109:2380,infra2=http://10.0.1.110:2380 \
--initial-cluster-state new
按如上配置分别启动集群,启动集群后,将会进入集群选举状态,若出现大量超时,则需要检查主机的防火墙是否关闭,或主机之间是否能通过2380端口通信,集群建立后通过以下命令检查集群状态。
查看集群成员
[root@localhost etcd-v2.3.4]# ./etcdctl member list
4057b0d6df067950: name=infra0 peerURLs=http://10.0.1.111:2380 clientURLs=http://10.0.1.111:2379 isLeader=false
dca0fb8da20901bc: name=infra2 peerURLs=http://10.0.1.110:2380 clientURLs=http://10.0.1.110:2379 isLeader=true
dcdce7909200f701: name=infra1 peerURLs=http://10.0.1.109:2380 clientURLs=http://10.0.1.109:2379 isLeader=false
检查集群健康状态
[root@localhost etcd-v2.3.4]# ./etcdctl cluster-health
member 4057b0d6df067950 is healthy: got healthy result from http://10.0.1.111:2379
member dca0fb8da20901bc is healthy: got healthy result from http://10.0.1.110:2379
member dcdce7909200f701 is healthy: got healthy result from http://10.0.1.109:2379
cluster is healthy
如上集群搭建成功。
2.3 etcd发现
静态配置前提是在搭建集群之前已经提前知道各节点的信息,而实际应用中可能存在预先并不知道各节点ip的情况,这时可通过已经搭建的etcd来辅助搭建新的etcd集群,具体过程如下,
首先需要在已经搭建的etcd中创建用于发现的url
curl -X PUT http://10.0.1.33:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3
返回:
{"action":"set","node":{"key":"/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size","value":"3","modifiedIndex":170010,"createdIndex":170010}}
如上表示创建一个集群大小为3的etcd发现url,创建成功后按如下配置启动各节点
./etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.111:2380 \
--listen-peer-urls http://10.0.1.111:2380 \
--listen-client-urls http://10.0.1.111:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.111:2379 \
--discovery http://10.0.1.33:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
./etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.109:2380 \
--listen-peer-urls http://10.0.1.109:2380 \
--listen-client-urls http://10.0.1.109:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.109:2379 \
--discovery http://10.0.1.33:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
./etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.110:2380 \
--listen-peer-urls http://10.0.1.110:2380 \
--listen-client-urls http://10.0.1.110:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.1.110:2379 \
--discovery http://10.0.1.33:2379/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
如上当所有etcd均注册到用于发现的url后,独立的各节点将形成集群。
另一方面,如果没有搭建好的etcd集群用于注册和发现,可使用etcd公有服务来进行服务注册发现。
在公有etcd服务上创建用于发现的url:
$curl https://discovery.etcd.io/new?size=3
返回
https://discovery.etcd.io/5a2e5485d3579557f1a29d0e6ebe3bfb
将返回的url替换上面的 --discovery部分,启动各个节点etcd,建立集群。
2.4 dns发现
dns 发现主要通过dns服务来记录集群中各节点的域名信息,各节点到dns服务中获取相互的地址信息,从而建立集群。etcd各节点通过--discovery-serv配置来获取域名信息,节点间将获取以下域名下的各节点域名,
_etcd-server-ssl._tcp.example.com
_etcd-server._tcp.example.com
如果_etcd-server-ssl._tcp.example.com下有值表示节点间将使用ssl协议,相反则使用非ssl。
_etcd-client._tcp.example.com
_etcd-client-ssl._tcp.example.com
另一方面,client端将获取以上域名下的节点域名,用于client端与etcd通信,ssl与非ssl的使用取决于以上那个域名下有值。
创建dns记录
$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra2.example.com.
$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com. 300 IN A 10.0.1.111
infra1.example.com. 300 IN A 10.0.1.109
infra2.example.com. 300 IN A 10.0.1.110
然后启动各个节点
$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra0.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra0.example.com:2379 \
--listen-client-urls http://infra0.example.com:2379 \
--listen-peer-urls http://infra0.example.com:2380
$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra1.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra1.example.com:2379 \
--listen-client-urls http://infra1.example.com:2379 \
--listen-peer-urls http://infra1.example.com:2380
$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra2.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra2.example.com:2379 \
--listen-client-urls http://infra2.example.com:2379 \
--listen-peer-urls http://infra2.example.com:2380
3.etcd接口访问
目前etcd已经支持很多语言的client端,目测主流语言均有对应的库, 具体见https://github.com/coreos/etcd/blob/master/Documentation/libraries-and-tools.md,不同语言只需要导入相应的库即可进行开发。
4. etcd使用场景简述
说完etcd的搭建,这里简单的描述两个常用的etcd使用场景,服务注册发现以及共享配置
4.1 服务注册与发现
随着近来微服务概念的提出,服务的分解必然引入大量的服务之间的相互调用,而传统的服务调用一般通过配置文件读取ip进行调用,这里有诸多限制,如不灵活,无法感知服务的状态,实现服务调用负载均衡复杂等缺点,而引入etcd后,问题将大大化简,这里划分为几个步骤
服务启动后向etcd注册,并上报自己的监听的端口以及当前的权重因子等信息,且对该信息设置ttl值。
服务在ttl的时间内周期性上报权重因子等信息。
client端调用服务时向etcd获取信息,进行调用,同时监听该服务是否变化(通过watch方法实现)。
当新增服务时watch方法监听到变化,将服务加入代用列表,当服务挂掉时ttl失效,client端检测到变化,将服务踢出调用列表,从而实现服务的动态扩展。
另一方面,client端通过每次变化获取到的权重因子来进行client端的加权调用策略,从而保证后端服务的负载均衡。
4.2 共享配置
一般服务启动时需要加载一些配置信息,如数据库访问地址,连接配置,这些配置信息每个服务都差不多,如果通过读取配置文件进行配置会存在要写多份配置文件,且每次更改时这些配置文件都要更改,且更改配置后,需要重启服务后才能生效,这些无疑让配置极不灵活,如果将配置信息放入到etcd中,程序启动时进行加载并运行,同时监听配置文件的更改,当配置文件发生更改时,自动将旧值替换新值,这样无疑简化程序配置,更方便于服务部署。
5. 总结
本文讲解了etcd集群的搭建,以及简述了一些应用场景,相比于zookeeper内部使用paxos算法保证一致性,而etcd则使用更简单的raft算法。另一方面,相比zookeeper, etcd算是新兴项目,因而在知名度及使用量来说和zookeeper有一定差距,不过,随着越来越多的知名项目在逐渐使用etcd(如docker),相信etcd将会在未来更多的出现在各种服务体系中。目前我们已经在生产环境中大量使用etcd,从使用效果来看,稳定性还是不错的,目前还没遇到什么大坑。
6.参考资料
https://coreos.com/etcd/
https://github.com/coreos/etcd
有话要说