(转载)服务发现系统etcd介绍
- 服务发现系统etcd介绍
最近在折腾服务发现系统,在这之前折腾过一段时间CoreOS,那时候就接触etcd了,不过当时只是把etcd看做一个K/V存储系统来使用的,觉得和redis没什么区别,后来才知道etcd主要也是用于服务发现的。etcd 的灵感来自于 ZooKeeper 和 Doozer,侧重于:
简单:支持 curl 方式的用户 API (HTTP+JSON)
安全:可选 SSL 客户端证书认证
快速:单实例可达每秒 1000 次写操作
可靠:使用 Raft 实现分布式
etcd是用Go语言编写的,使用raft一致性算法来管理高可用日志。etcdctl是一个简单的命令行工具,使用起来就和curl一样。etcd已经托管在github上了,我们下载一个稳定版的(0.4.6),不要下载0.5.0版本,0.5.0是基于开发版的,在折腾kubernetes的时候直接下载的0.5.0,可被坑惨了。部署环境还是三台ubuntu14.04机器,都是部署在/opt/etcd目录下
192.168.1.100 Docker-1 server
192.168.1.101 Docker-2 client
192.168.1.102 Docker-3 client
我们先在Docker-1上练练手,首先安装etcd,etcd用Go语言编写,安装时需要go环境,我这三台机器运行Docker,go环境都已经部署ok了,所以直接编译即可
cd /opt/etcd
./build
执行完上述命令后,会在源码目录发现生成了一个bin目录,里面有两个文件bench、etcd。先在我们来启动etcd
./bin/etcd
etcd启动后,会监听在4001端口,用来和client进行通讯,而监听的7001端口则是服务端和服务端之间进行通讯的端口。下面我们来设置一个key
#set
curl http://127.0.0.1:4001/v2/keys/guol -XPUT -d value='test etcd'
#get
curl http://127.0.0.1:4001/v2/keys/guol
操作起来很简单,现在我们已经启动了一个单节点的etcd服务,并且初步尝试了etcd的api,下面我们看看一些具体的内容。
etcd api
etcd的api系统是一个空间层次分明的系统,key space是由目录和密钥构成的。我们先启动一个etcd系统,并查看该etcd的版本
#start
./bin/etcd -data-dir /tmp/etcd/ -name Docker-1
#get version
curl http://127.0.0.1:4001/version
在启动etcd时,多了两个参数,-data-dir指定etcd系统存储etcd配置、日志和快照的目录,-name该节点在集群中的名称。
接下来,我们设置第一对K/V,key=message1,value='hello world'
curl -s http://127.0.0.1:4001/v2/keys/message1 -XPUT -d value='hello world'
我们看看返回的对象都包含了哪些值:action,action反映当前请求的模式(get/set/delete),因为当前的请求是通过HTTP的PUT方法修改节点的值,所以action是set。node对象中有四个元素:node.key,HTTP请求设置的key名称,etcd使用类似文件系统的结构来表示K/V对,所以所有的key前面都以'/'开头。node.value,设置的key的值。node.createdIndex,一个唯一的索引,在每次etcd变更时一个单调递增的整数,这个特性可以反映出etcd系统已经创建了多少个key,你可以看到索引的值是8,那是因为之前我已经插入了几对K/V值了,在etcd有变更后,也有一些内部命令在幕后做一些变更,例如增加或者同步servers。node.modifiedIndex,和node.createdIndex很像,该属性也是一个索引,set、delete、update、create、compareAndSwap、compareAndDelete等动作都会引起value值的改变,get、watch动作不会引起已经存储的值的改变,因此这两个特性不会改变node.modifiedIndex的值。
获取一个key的值,我们获取key(message1)的值
curl -s http://127.0.0.1:4001/v2/keys/message1
你也可以用HTTP的PUT方法改变一个key的值,我们改变message1的value为'hello etcd'
curl -s http://127.0.0.1:4001/v2/keys/message1 -XPUT -d value='hello etcd'
这次我们看到返回值中多了一个prevNode属性,prevNode属性根据名称一看就知道是返回的该key在改变前的值,prevNode的格式和node一样,你也会看到index都会递增。
我们也可以用HTTP的DELETE方法删除一个key,我们删除key(message1)
curl -s http://127.0.0.1:4001/v2/keys/message1 -XDELETE
这时候再查看message1的值会发现已经没有该key了
你也可以在etcd系统中给key设置一个过期时间,只需要在设置key时加上一个ttl参数即可
curl -s http://127.0.0.1:4001/v2/keys/message2 -XPUT -d value='hello etcd' -d ttl=5
看到设置的message2的生存期只有5s,过期后会自动删除。在node的输出中多了一个expiration属性,该属性提示多久后key会过期并被删除。ttl属性显示key的ttl值。后面多余的输出是使用以下命令测试的:
curl -s http://127.0.0.1:4001/v2/keys/message2 -XPUT -d value='hello etcd' -d ttl=5|jq .;echo 'sleep....';sleep 6;curl -s http://127.0.0.1:4001/v2/keys/message2 |jq .
我们也可以监视key的变更,在key有改变后,我们会收到相关通知。在调用HTTP请求时,提供wait=true参数,即可监控一个key。
#窗口1
curl -s http://127.0.0.1:4001/v2/keys/message2 -XPUT -d value='hello etcd 1'
curl -s http://127.0.0.1:4001/v2/keys/message2?wait=true
新开一个终端,执行以下命令
curl -s http://127.0.0.1:4001/v2/keys/message2 -XPUT -d value='hello etcd 2'
再看看窗口1有什么变化
自动的创建顺序key,在一个目录中使用POST方法,你可以创建一些key,使key的名字看起来是顺序的,执行以下测试命令:
curl -s http://127.0.0.1:4001/v2/keys/message3 -XPOST -d value='hello etcd 1'
curl -s http://127.0.0.1:4001/v2/keys/message4 -XPUT -d value='hello etcd 1'
curl -s http://127.0.0.1:4001/v2/keys/message5 -XPUT -d value='hello etcd 1'
curl -s http://127.0.0.1:4001/v2/keys/message3 -XPOST -d value='hello etcd 2' | jq .
可以看到我们已经针对message3这个key使用了POST方法,在插入两个新key之后,再次更新message3的value,发现messages3在etcd中的key已经变了(key/index),下面我们看看message3这个key中到底存储什么信息,为了使输出的key是顺序的,我们加了sorted参数
curl -s 'http://127.0.0.1:4001/v2/keys/message3?recursive=true&sorted=true'
可以看到存有message3的历史值记录,在某些情况下,需要自动创建key的目录,但在某些情况下,你希望创建或者删除一个目录。创建一个目录就像创建key一样简单,但是你不能提供一个value,而是要增加一个参数dir=true
curl -s http://127.0.0.1:4001/v2/keys/message7 -XPUT -d dir=true
看到没有value属性,而是多了一个dir属性。有创建目录的需求,就有删除目录的需求,我们现在删除刚刚创建的message7目录,和删除key的方法一样,也是使用HTTP的DELETE方法,只不过要再添加一个dir=true参数
curl -s 'http://127.0.0.1:4001/v2/keys/message7?dir=true' -XDELETE
如果director里面已经包含keys了,则必须增加recursive=true参数
curl -s 'http://127.0.0.1:4001/v2/keys/message7?recursive=true' -XDELETE
在etcd系统中,我们可以存储两种类型的东西:keys和directors。keys存储的是单个字符值,directors存储的是一组keys或者存储其他directors。下面我们创建一个key(message_key),在创建该key之前,我们先看看etcd系统已经存在哪些key了,要查看整个系统的key,只要增加recursive=true参数即可。
curl -s http://127.0.0.1:4001/v2/keys/?recursive=true
因为我已经清空etcd系统了,现在创建key:message_key
curl -s http://127.0.0.1:4001/v2/keys/message_key -XPUT -d value='this is a key'
创建directory:message_directory
curl -s http://127.0.0.1:4001/v2/keys/message_directory -XPUT -d dir=true
现在我们看看整个etcd系统中有多少key
curl -s http://127.0.0.1:4001/v2/keys/?recursive=true
可以看到整个etcd中只有一个key和一个directory,现在我们在message_directory目录下面创建一个key(message_key)
curl -s http://127.0.0.1:4001/v2/keys/message_directory/message_key -XPUT -d value='this is a key in directory'
我们现在看看etcd系统都有哪些key了
curl -s http://127.0.0.1:4001/v2/keys/?recursive=true
可以看到整个etcd中有一个key和一个directory,而directory中又包含了一个key。
如何创建一个隐藏的节点(隐藏key/隐藏directory),我们创建一个隐藏key时使用'_'作为key的前缀即可,当你发送HTTP GET请求时,隐藏key并不会被显示出来
curl -s http://127.0.0.1:4001/v2/keys/_message -XPUT -d value='a hidden key'
etcd系统中也可以存储一些小的配置文件、json文档、xml文档等等。
curl -s http://127.0.0.1:4001/v2/keys/file -XPUT --data-urlencode value@upfile
etcd会跟踪一些集群使用的统计信息,比如带宽、启动时间之类的。
Leader Statistics:leader有查看整个etcd集群视图的能力,而且会跟踪两个有趣的统计信息:到集群中同等机器的延迟和rafr rpc请求失败和成功的数量。
curl -s http://127.0.0.1:4001/v2/stats/leader
Self Statistics:每个节点内部的统计信息
curl -s http://127.0.0.1:4001/v2/stats/self
Store Statistics:存储统计信息包含了在该节点上的所有操作信息
curl -s http://127.0.0.1:4001/v2/stats/store
如何查询集群的配置
curl -s http://192.168.1.100:7001/v2/admin/config
看看整个集群中有多少成员
curl -s http://192.168.1.100:7001/v2/admin/machines
在集群中删除Docker-2节点
curl -s http://192.168.1.100:7001/v2/admin/machines/Docker-2 -XDELETE
再次查看整个集群的成员
etcd节点的配置文件可以有三种设置方法:命令行、环境变量、配置文件,命令行优先级最高,环境变量的又高于配置文件。
下面我们看看etcd的命令都支持哪些命令行参数:
Options:
--version 显示版本号
-f -force 强制使用新的配置文件
-config=<path> 指定配置文件的路径
-name=<name> 该节点在etcd集群中显示的名称
-data-dir=<path> etcd数据的存储路径
-cors=<origins> Comma-separated list of CORS origins.
-v 开启verbose logging.
-vv 开启very verbose logging.
Cluster Configuration Options:
-discovery=<url> Discovery service used to find a peer list.
-peers-file=<path> 包含节点信息的文件列表
-peers=<host:port>,<host:port> 逗号分割的节点列表,这些节点应该匹配节点的-peer-addr标记指定的信息
Client Communication Options:
-addr=<host:port> 客户端进行通讯的公共地址端口
-bind-addr=<host[:port]> 监听的地址端口,用来进行客户端通讯
-ca-file=<path> 客户端CA文件路径
-cert-file=<path> 客户端cert文件路径
-key-file=<path> 客户端key文件路径
Peer Communication Options:
-peer-addr=<host:port> 节点间进行通讯的地址端口
-peer-bind-addr=<host[:port]> 监听的地址端口,节点间来进行通讯
-peer-ca-file=<path> 节点CA文件路径
-peer-cert-file=<path> 节点cert文件路径
-peer-key-file=<path> 节点key文件路径
-peer-heartbeat-interval=<time> 心跳检测的时间间隔,单位是毫秒
-peer-election-timeout=<time> 节点选举的超时时间,单位是毫秒
Other Options:
-max-result-buffer 结果缓冲区的最大值
-max-retry-attempts 节点尝试重新加入集群的次数
-retry-interval Seconds to wait between cluster join retry attempts.
-snapshot=false 禁止log快照
-snapshot-count 发布快照前执行的事物次数
-cluster-active-size 集群中的活跃节点数
-cluster-remove-delay 多少秒之后再删除集群中的节点
-cluster-sync-interval 在备模式下,两次同步之间的时间差
配置文件方式,etcd默认从/etc/etcd/etcd.conf读取配置
addr = "127.0.0.1:4001"
bind_addr = "127.0.0.1:4001"
ca_file = ""
cert_file = ""
cors = []
cpu_profile_file = ""
data_dir = "."
discovery = "http://etcd.local:4001/v2/keys/_etcd/registry/examplecluster"
http_read_timeout = 10.0
http_write_timeout = 10.0
key_file = ""
peers = []
peers_file = ""
max_cluster_size = 9
max_result_buffer = 1024
max_retry_attempts = 3
name = "default-name"
snapshot = true
verbose = false
very_verbose = false
[peer]
addr = "127.0.0.1:7001"
bind_addr = "127.0.0.1:7001"
ca_file = ""
cert_file = ""
key_file = ""
[cluster]
active_size = 9
remove_delay = 1800.0
sync_interval = 5.0
自己写一遍,记得牢!