转载:http://www.csdn.net/article/2015-01-22/2823659node
摘要:在“漫步云端:CoreOS实践指南”系列的前几篇,分别介绍了如何架设CoreOS集群,系统服务管家Systemd和集群的指挥所Fleet,本篇将介绍CoreOS生态中链接各个节点通讯和支撑集群服务协同运做的模块Etcd。git
注:本文首发于CSDN,转载请标明出处。github
【编者按】做为一个操做系统,CoreOS 采用了高度精简的系统内核及外围定制,将许多本来须要复杂人工操做或者第三方软件支持的功能在操做系统级别进行了实现,同时剔除了其余对于服务器系统非核心的软件,好比GUI和包管理器。CSDN特邀ThoughtWorks的软件工程师林帆带来了“漫步云端:CoreOS实践指南”系列文章,带你们了解CoreOS的精华和推荐的实践方法。本文为基础第五篇:分布式数据存储Etcd。Etcd是CoreOS生态系统中处于链接各个节点通讯和支撑集群服务协同运做的核心地位的模块,这篇文章将主要从系统运维工做者的角度介绍Etcd的操做和API的使用。算法
做者简介:数据库
林帆,生在80后尾巴的IT攻城狮,ThoughtWorks成都办公室CloudOps小组成员,平时喜欢在业余时间研究DevOps相关的应用,目前在备考AWS认证和推广Docker相关技术。服务器
分布式数据的存储一直是解决集群服务消息同步和协调操做的核心关注点。在这个系列的上一篇,介绍了用于集群管理的Fleet服务,而Fleet的功能的实现除了依赖于Systemd服务的DBus扩展,其分布式控制部分很大程度上得益于CoreOS提供的可靠且高效的分布式数据服务Etcd。事实上,Etcd是CoreOS生态系统中处于链接各个节点通讯和支撑集群服务协同运做的核心地位的模块,这篇文章将主要从系统运维工做者的角度介绍Etcd的操做和API的使用。框架
什么是分布式数据存储呢?从结果的角度上看,就是将数据分散存储在多台独立的设备上,从而提升数据的可靠性或读写性能的方法。从实现的角度上看,目前主流的NoSQL数据库,例如MongoDB、FoundationDB等都可以很方便地实现分布式存储。而Etcd本质上与一个NoSQL的数据库系统也有几分神似,但更准确的说法说是一个高可用的键值存储系统。与通常的NoSQL数据库不一样,Etcd在设计的初衷主要用因而共享配置和服务发现,它的灵感来自于ZooKeeper和Doozer。关于这三者以及其余同类的服务发现框架的比较能够参看这篇文章,简单来讲,Etcd对TTL的支持和HTTP Restful API是其比较大的亮点。与语言和平台无关的Restful API使得基于它的二次开发变得更加方便,而且可以对运行在应用容器中的程序提供(好比Docker)提供友好的支持。运维
在数据一致性方面,Etcd经过Raft一致性算法处理日志复制以保证强一致性。这里不打算对Raft算法进行深刻的探讨,关于Raft算法的具体原理能够参看桂阳的这篇文章。curl
Etcd是CoreOS的核心服务模块中起步最先的,它的第一行代码提交于2013年6月,早于Fleet(2013年10月)以及其余CoreOS模块,它也是许多其余服务模块的根基。分布式
值得一提的是,Etcd的最主要贡献者是两位华人开发者秦毅成和李响。最初的Etcd使用了一套独立的GoRaft库,在2014年中旬开始,Etcd作了一次大规模的代码重构,撤去了对GoRaft库的依赖,从新实现了一套更加精简和稳定的Raft算法。Etcdctl命令的代码也从单独的项目合并到了Etcd版本库中。从GitHub的代码提交量来看,Etcd一直是CoreOS的几个主要模块中活跃度最高的模块。
从Etcd的发展过程来看,其设计初衷是一个与ZooKeeper类似的具备订阅/通知(Subscript/Public)功能的配置共享服务。在集群启动的时候,咱们仅仅配置了一个集群标识的URL地址就使得各个节点相互认识,并能够经过Fleet得到其余节点的信息,这当中就有Etcd的默默的功劳。
CoreOS官方提供的discovery集群标识服务地址是https://discovery.etcd.io。在系列的第二篇构建CoreOS集群的过程当中,经过访问这个网站下的 /new 页面得到了一个标识地址,若是已经忘记了这个地址,能够在集群中的任意一个节点查看 /run/systemd/system/etcd.service.d/20-cloudinit.conf 文件,这个文件记录了节点启动时的一些信息。
$ cat /run/systemd/system/etcd.service.d/20-cloudinit.conf [Service] Environment="ETCD_ADDR=172.17.8.101:4001" Environment="ETCD_DISCOVERY=https://discovery.etcd.io/09363c5fcdfcbd42ed60b8931263fda1" Environment="ETCD_PEER_ADDR=172.17.8.101:7001"
直接访问这个标识地址就能获得当前集群的基本节点信息。
core@core-01 ~ $ curl https://discovery.etcd.io/09363c5fcdfcbd42ed60b8931263fda1 {"action":"get","node":{ ... }}
当新的CoreOS节点启动时,首先会经过Cloud-init启动Etcd服务(也是在此前经过user-data文件进行配置的),Etcd启动的过程当中,会经过这个标识地址得到集群中已有的节点的信息列表,而后将本身的信息也添加到这个列表中。提供这个节点信息的Discovery服务咱们会在另外的一篇文章里单独来讲。
在Etcd得到到集群的状态后,它会进行一系列的初始化工做,其中有一项是更新Etcd数据中关于集群成员的内容。所以之后的其余服务就能够经过Etcd的数据直接获得实时更新的集群成员信息了。
经过etcdctl命令能够查看到Etcd服务存储的这部分信息。例以下面这个命令可以列出Etcd存储的全部节点。
core@core-01 ~ $ etcdctl ls /_etcd/machines --recursive /_etcd/machines/f260afd8224c4854bdf8427d8451da23 /_etcd/machines/0acdd9bf38194ea5ad1611ff9a4236f1 /_etcd/machines/f2558aaa231044f3abbe01510ac2b1d8
查看其中一个具体节点的信息。
core@core-01 ~ $ etcdctl get /_etcd/machines/f260afd8224c4854bdf8427d8451da23 etcd=http%3A%2F%2F172.17.8.102%3A4001&raft=http%3A%2F%2F172.17.8.102%3A7001这里用到的etcdctl命令是Etcd提供的用于查询和操做其存储内容的命令行工具,下面来看看它的其余用法。
Etcd自己提供了基于HTTP的Restful API,可是为了方便运维人员的平常使用,etcdctl实现了这套API中的许多功能,熟练的使用它可以简化很多运维的工做量。
在上一节中已经用到了的etcdctl ls 和 etcdctl get 命令是最常用到的两个基本命令。Etcd的键值能够进行分层和嵌套,Etcd中的目录能够存放多个键以及其余的目录,同时每一个具体的目录和键都有本身的“访问路径”,这种作法与文件管理中的普通文件和目录颇为类似。.而etcdctl ls 的做用是查看特定路径下的键或目录列表。例如列出根目录下面的内容:
core@core-01 ~ $ etcdctl ls / /coreos.com
能够经过 --recursive 参数一次性列出指定目录及子目录下全部的内容。
core@core-01 ~ $ etcdctl ls / --recursive /coreos.com /coreos.com/updateengine /coreos.com/updateengine/rebootlock /coreos.com/updateengine/rebootlock/semaphore
而 etcdctl get 命令用于得到指定的键所存储的值,例如:
core@core-01 ~ $ etcdctl get /coreos.com/updateengine/rebootlock/semaphore {"semaphore":1,"max":1,"holders":[]}
对一个目录使用 etcdctl get 命令会获得一个提示信息,告诉使用者这是一个目录。
core@core-01 ~ $ etcdctl get /coreos.com /coreos.com: is a directory
能够经过etcdctl的 -o 参数指定输出内容的格式,例如 -o extended 参数会打印这个键的更详细信息。
core@core-01 ~ $ etcdctl -o extended get /coreos.com/updateengine/rebootlock/semaphore Key: /coreos.com/updateengine/rebootlock/semaphore Created-Index: 6 Modified-Index: 76156 TTL: 0 Etcd-Index: 104439 Raft-Index: 413950 Raft-Term: 12 {"semaphore":1,"max":1,"holders":[]}
建立一个新的目录和键分别使用 etcdctl mkdir 和 etcdctl mk 命令。
core@core-01 ~ $ etcdctl mkdir /demo core@core-02 ~ $ etcdctl get /demo /demo: is a directory core@core-01 ~ $ etcdctl mk /demo/hello "Hello Etcd" # 实际状况中这里会回显输出“Hello Etcd”,省略 core@core-02 ~ $ etcdctl get /demo/hello Hello Etcd
实际状况中每次使用 mk 之后,屏幕会回显写入值的内容。因为以后咱们都会再次用 get 取出这个值,以验证内容确实写入了Etcd记录中,因此统一省略回显的输出,后面的update和set的例子也是。注意在上面的例子中,咱们在core-01节点建立了新的键和目录,而后在core-02节点检查Etcd记录的内容。这样作是为了说明在Etcd服务组成的集群里,每个节点上获取的数据都是实时同步的。固然,要是在core-01上检查记录内容也会获得相同的结果。
使用 etcdctl mk命令时,若是建立键的路径不存在,会自动建立相应的目录结构,例如:
core@core-01 ~ $ etcdctl mk /path/to/the/new/key "Text Message" core@core-02 ~ $ etcdctl get /path/to/the/new/key Text Message
尝试重复建立一个已经存在的键会产生一个错误。
core@core-01 ~ $ etcdctl mk /demo/hello "Something Else" Error: 105: Key already exists (/demo/hello) [130187]
更新键的记录内容能够经过etcdctl update(或 etcdctl set)命令完成。
core@core-01 ~ $ etcdctl update /demo/hello "Hello CoreOS" core@core-02 ~ $ etcdctl get /demo/hello Hello CoreOS
使用etcdctl update与etcdctl set的区别在于尝试update不存在的键时,会产生一个错误。
core@core-01 ~ $ etcdctl update /demo/xx "Message" Error: 100: Key not found (/demo/xx) [129853]下面这个表格比较了etcdctl mk、etcdctl update和etcdctl set的异同。从结果能够看出etcdctl set至关于前二者的功能的合并。在实际状况中应该根据实际的应用场景选择相应的命令。
命令/操做 |
etcdctl mk |
etcdctl update |
etcdctl set |
目标键不存在 |
建立此键并赋值 |
出错:Key not found |
建立此键并赋值 |
目标键已经存在 |
出错:Key already exists |
更新键的内容 |
更新键的内容 |
删除键和目录的命令分别为etcdctl rm和etcdctl rmdir:
core@core-01 ~ $ etcdctl rm /demo/hello core@core-01 ~ $ etcdctl get /demo/hello Error: 100: Key not found (/demo/hello) [131166] core@core-01 ~ $ etcdctl rmdir /demo core@core-01 ~ $ etcdctl get /demo Error: 100: Key not found (/demo) [131189]
注意,etcdctl rmdir只能删除空的目录,这一点和Linux的rmdir命令很类似。试图删除还有其余键或子目录的目录会产生一个错误。这种状况可使用etcdctl rm配合--recursive参数递归删除目录下的全部子目录和键。
core@core-01 ~ $ etcdctl rmdir /path Error: 108: Directory not empty (/path) [131209] core@core-01 ~ $ etcdctl rm /path --recursive
Etcd的键值系统有一个对应用配置颇有帮助的特性,能够给每个键或目录指定一个存活时限(TTL,即Time To Life)。TTL的单位是秒,当指定的秒数过去之后,若是相应的键或目录没有获得更新,就会被自动从Etcd记录中移除。
用于给键或目录添加TTL的参数是--ttl,这个命令对几个建立和更新的命令都适用。
core@core-01 ~ $ etcdctl mkdir /path/to/demo --ttl 120 # 给目录添加TTL时间 core@core-01 ~ $ etcdctl mk /path/to/demo/title “Message Title” --ttl 120 # 给键添加TTL时间
经过 update 和 updatedir 的 --ttl 参数可以将键和目录的剩余存活周期重置为指定的新值。这个功能有点像用于确保仪器正确运行的“看门狗”程序,一旦发现程序必定时间内都没有更新相应的Etcd记录,这条记录就会被认为是过时的而直接被移除。在设定了TTL以后,可使用带 -o extended 参数的ectdctl get命令来检查数据键或目录剩余的存活时间。
core@core-01 ~ $ etcdctl updatedir /path/to/demo --ttl 500 # 更新目录的TTL时间 core@core-01 ~ $ etcdctl update /path/to/demo/titl “Message Title” e--ttl 400 # 更新键的TTL时间
这个功能使得局部的系统出现节点或服务失效时,系统的其他部分可以及时发现这一状况,并做出调整。具体的应用场景咱们在之后的进阶篇内容中会举例说明。
若是Etcd的功能仅仅局限于数据的存储和分发,它与普通的NoSQL数据库就没有特别的差异了。事实上Etcd所作的远不止这些,做为一个用于集群配置共享的服务,除了TTL这种典型特性外,另外一个重要的功能即是数据变动的订阅/通知(Subscript/Public)。
集群中的应用程序为了保持正确的行为,须要时刻依据所需的配置信息进行相应的调整,一般有两种方式能够实现。一种是按期去检查集群配置的内容,即定时轮询(Polling)。另外一种是订阅特定的事件,由集群配置服务(Etcd)在相应事件发生的时候直接通知应用程序作出处理。显然从响应的及时性和对应用程序效率的影响来讲,后者要更加适用一些。
其实Etcd自己并无提供一套直接的订阅/通知服务机制,但经过它提供的监控变化API以及HTTP long-polling的办法,是能够实现相同的功能的。与此相关的命令是etcdctl watch和etcdctl exec-watch,前者用于等待指定的键发生变化,后者在前者的基础上提供了变化发生后,自动触发另外一段用户指定的命令的能力。
core@core-01 ~ $ etcdctl watch /path/to/demo core@core-02 ~ $ etcdctl update /path/to/demo “new-value”
上面这段例子使用了在core-01节点监听路径/path/to/demo,当etcdctl watch执行后,程序就开始进入等待状态。而后在core-02节点对这个路径上的键进行了更新,此时等待在core-01节点上的etcdctl进程收到了这个变化随即退出。
这个命令能够接收一个参数 --recursive 用于递归监听指定路径下全部子路径的变化。
core@core-01 ~ $ etcdctl watch --recursive /path
单独使用etcdctl watch命令没有太大的意义,由于它每次监听到指定的变化就退出了,什么也作不了。通常会将监听到记录变化之后须要执行的命令写在这行命令的后面,这样当事件发生之后,就会立刻执行特定的操做了。在一些后台的脚本中,这种监听功能十分有用。下面的这段脚本实现了自动监听/path/to/demo路径的变化,一旦有变化发生,就将这个键的值前面加上一个“Hello”。
KEY=/path/to/demo while true; do etcdctl watch ${KEY} sleep 1s etcdctl update ${KEY} “Hello $(etcdctl get ${KEY})” done
须要注意这段脚本中的“sleep 1s”这一行,若是去掉这一行,在后面当即使用etcdctl get获得依然是变化发生以前这个键的内容。即etcdctl在接收到变化信号时候,若是想获取变化后的内容,须要等待一点点时间。已经将这个问题提交到了GitHub,有兴趣的同窗能够跟一下后续的回复。
除了将须要执行的命令放到etcdctl watch命令以后,etcdctl也提供了一条可以一步到位的命令:exec-watch。可是在实际使用中发现这个命令还存在不少问题,在监视同一个键时,修改了键的内容后,使用watch命令老是能当即返回,可是exec-watch命令常常平白无故的接收不到;此外exec-watch在执行了指定的命令后会存在命令没法正确退出的问题。鉴于它所完成的工做使用etcdctl watch已经可以作到,这里暂时略去对这个命令的介绍。
在这一篇中,咱们主要从Etcd服务的做用和它提供的数据操做工具etcdctl介绍了CoreOS的核心分布式数据服务的概貌及用法。然而在这些工具表象的背后,是一套支撑着Etcd业务扩展的API系统。在系列的下一篇中,会继续探讨Etcd服务的Restful API使用、Etcd的配置参数以及其余一些使用者须要知道的Etcd特性。敬请期待。(做者/林帆 责编/周小璐)