CoreOS实践指南(六):分布式数据存储Etcd(下)

注:本文首发于CSDN,转载请标明出处。

【编者按】在“漫步云端:CoreOS实践指南”系列第五篇: 分布式数据存储Etcd(上)中,ThoughtWorks的软件工程师林帆从系统运维工作者的角度介绍了Etcd的操作和API的使用。本文为分布式数据存储Etcd的下篇。Etcd是CoreOS生态系统中处于连接各个节点通信和支撑集群服务协同运作的核心地位的模块,这篇文章将主要介绍Etcd的RESTful API。如果说Etcd数据存储服务是CoreOS分布式架构的基石,那么Etcd的RESTful API就是架在这基石上的顶梁立柱。

作者简介:

林帆,生在80后尾巴的IT攻城狮,ThoughtWorks成都办公室CloudOps小组成员,平时喜欢在业余时间研究DevOps相关的应用,目前在备考AWS认证和推广Docker相关技术。

如果说Etcd数据存储服务是CoreOS分布式架构的基石,那么Etcd的RESTful API就是架在这基石上的顶梁立柱。由于CoreOS中的许多分布式应用都会使用Etcd作为其存储配置的地方,对于一个普通的运维人员,熟练的使用etcdctl工具已经可以完成很多系统配置的任务。为什么还要单独的一篇来介绍Etcd的API体系呢?一方面来说,ectdctl实现功能的只是Etcd API的一个子集(例如不支持指定监控事件的起始时间),因此Etcd API的内容可以看做是前一篇的锦上添花。另一方面,etcdctl是Etcd API的CLI(Command Line Interface)实现,比较适合通过脚本和配置管理工具运行,却不适合在一般的编程语言当中直接使用。相比ZooKeeper提供特定语言Library扩展支持,Etcd API采用的是通用的HTTP协议和Json数据格式,几乎没有编程语言的限制,也使得基于Etcd的二次开发应用更加容易调试,甚至只需简单的curl命令行工具便能测试这些API的返回结果。

额外说明:正所谓计划赶不上变化,正在写作这篇文章的同时,Etcd官方的Github版本库恰好也已经将v2.0的文档合并到了 主干分支上,距离Etcd的V2.0版本发布又近了一步。但截止目前,即便在Alpha发布通道上Etcd V2.0尚不可用,出于示例的真实性考虑,本篇的内容将依然采用V0.4的API为例。Etcd V2.0(原V0.5)API与此前的V0.4 API基本兼容,其中最大的却别在于其规范了端口号码的使用(已经写入 IANA组织的标准端口记录,感谢百度段兵指出这个出处)。提供给外部客户端的端口变为2379,而用于Etcd服务间通信的端口变为2380(在V0.4版本中分别为4001和7001)。以下例子中API与V2.0版本不兼容的地方将单独指出。

初识Etcd RESTful API

RESTful API是基于HTTP协议和Json格式的无状态应用程序接口,比如在一个运行了CoreOS的节点上,使用curl(或浏览器)访问地址http://127.0.0.1:4001/version,就能得到当前节点运行的Etcd服务版本。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/version

etcd 0.4.6

这个curl命令中的 -L 参数表示如果遇到重定向,应该跟随到重定向后的地址访问。需要指出的是,Etcd的文档中所有curl访问的例子都没有使用 -L 参数,但在实际的测试中发现,有些API(特别是PUT和POST操作的那些)如果不使用 -L 时是不能生效的,这一点可能是Etcd文档的错误。

增删改查

注意到没有?刚刚获得Etcd版本的调用返回值是版本号的字符串,没有使用Json格式,因此它是一个特殊的API。一般来说,Etcd API路径的一部分始终是API的版本号,对于V0.1以后的Etcd版本,包括V0.4和V2.0使用的都是第2版的API,因此这个根路径是 /v2/。第二级路径是API的分类,对于键值和目录的API,这个分类路径是 /keys/,即完整的路径为 /v2/keys/。而对数据的增、删、改、查操作是通过 HTTP 的访问方式和参数区别。

例如通过 HTTP GET 方式访问 /v2/keys/ 路径将返回Etcd数据存储结构中根路径上的所有键和目录。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/

{

"action":"get",

"node":{

  "key":"/",

  "dir":true,

  "nodes":[{

  "key":"/coreos.com",

  "dir":true,

  "modifiedIndex":6,

  "createdIndex":6

  }]

}

}

我们访问的路径 /v2/keys/ 表示的是Etcd根目录,相应的 /v2/keys/coreos.com/则表示Etcd中的/coreos.com目录。输出结果中的node.nodes是一个目录中所有键和子目录的列表。

上面的Json结果使用了缩进格式排版,实际的输出格式是压缩过的Json文本。关于控制台Json输出的格式化会在篇末的部分介绍。

这里API路径最后的那个反斜杠号表示访问Etcd的根目录,不能省略,否则会出现404 Not Found错误。对于访问的是非根目录的时候,最后的反斜杠则可有可无。可以通过参数来获得不一样的结果,下面的例子可以递归打印所有目录和子目录内容。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/coreos.com?recursive=true

{

  "action": "get",

  "node": {

  "key": "/coreos.com",

  "dir": true,

  "nodes": [{

  "key": "/coreos.com/updateengine",

  "dir": true,

   "nodes": [{

  "key": "/coreos.com/updateengine/rebootlock",

  "dir": true,

  "nodes": [{

  "key": "/coreos.com/updateengine/rebootlock/semaphore",

  "value": "{\"semaphore\":0,\"max\":1,\"holders\":[\"0acdd9bf38194ea5ad1611ff9a4236f1\"]}",

  "modifiedIndex": 6,

  "createdIndex": 6  }],

  "modifiedIndex": 6,

  "createdIndex": 6  }],

  "modifiedIndex": 6,

  "createdIndex": 6  }]

  }

}

对于API调用需要传递附加的参数,需要根据当前使用的HTTP操作类型选择传参的方法。对于GET和DELETE操作可以通过HTTP参数的方式传递,例如上面在GET获取列表时通过参数 recursive=true 来递归列出指定节点下包括子孙节点在内的所有目录和键。对于PUT和POST操作则需要将参数通过HTTP正文发送,在使用curl的时候就是通过 -d或--data来附加参数内容,在后面会使用到具体例子的时候再来说明。

如果通过HTTP GET访问的是一个键而不是目录,就会获得这个键的内容。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/coreos.com/updateengine/rebootlock/semaphore

{

  "action": "get",

  "node": {

   "key": "/coreos.com/updateengine/rebootlock/semaphore",

  "value": "{\"semaphore\":0,\"max\":1,\"holders\":[\"0acdd9bf38194ea5ad1611ff9a4236f1\"]}",

  "modifiedIndex": 6,

  "createdIndex": 6

  }

}

可以看到node.value部分的输出就是这个键存储的内容。

如果使用了PUT或POST方法操作目录所对应的API路径,则可以创建和更新目录或键。但两者有很大的区别。对于大多数的情况,我们应该使用PUT方法,例如新建一个Etcd的键。

core@core-01 ~ $ curl -L http://localhost:4001/v2/keys/path/demo1-XPUT -d value="Hey"

{

  "action": "set",

  "node": {

  "key": "/path/demo1",

  "value": "Hey",

  "modifiedIndex": 248530,

  "createdIndex": 248530

  }

}

注意键的内容是通过HTTP正文的方式传入的(curl的-d或 --data参数),这种参数传递方法适用于所有PUT和POST的操作。创建目录同样通过参数的方法指定,所使用的参数是 dir=true。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/path/demo2-XPUT -d dir=true

{

  "action": "set",

  "node": {

  "key": "/path/demo2",

  "dir": true,

  "modifiedIndex": 248955,

  "createdIndex": 248955

  }

}

其实一直到这里,我们有意的回避未提在输出结果中两个反复出现的数据单元modifiedIndex和createdIndex。这两个数据分别表示键或目录的最后修改时间和创建时间。但它们的变化规律可能和许多人直观感觉的不太一样(并且文档中对于这部分内容阐述得并不十分清晰),下面是关于这两个值的一些特点。

首先,Etcd记录的每一个目录或键都有这两个属性,它们都是只增不减的整型数字; 其次,其值与目录或键的创建时间和修改时间正相关,同时被创建的目录或键可能会有相同的modifiedIndex和createdIndex(只会在父子目录出现这种相同的情况); 细心的用户也许还会发现,这两个值并不是在集群全局一致的,在同一个集群的不同节点上查看同一个键或目录获得的值并不相同; 同样两个键或目录的Index值之间相减的差始终是一样的,也就是说,顺序和相对位置始终是一致的; 最后,对同一个键进行多次PUT操作,它的modifiedIndex和createdIndex值会同时增加,并保持相等,而不仅仅是想直觉认为的只增加modifiedIndex的数值。

关于上面的最后一点,实际的原因是,直接PUT一个已经存在的键,默认的操作是覆写(而不是更新)原本的键。也就是说Etcd会新建一个键放到指定的位置上替代原来的那个,因此代表创建时间的createdIndex值也相应的变化了。在大多数情况下,使用者不会去关心这点差别,但任然要指出的是,如果用户确实希望原地更新这个键的内容,需要在PUT时加上 prevExist=true参数。

core@core-01 ~ $ curl -L http://localhost:4001/v2/keys/path/demo1-XPUT -d value="New" -d prevExist=true

{

  "action": "update",

  "node": {

  "key": "/path/demo1",

  "value": "New",

"modifiedIndex": 248675,

"createdIndex": 248530

  },

  "prevNode": {

  "key": "/path/demo1",

  "value": "Hey",

  "modifiedIndex": 248530,

  "createdIndex": 248530

  }

}

POST操作的作用是创建一组以有序数值为键的序列,说起来比较抽象,举个例子。

curl -L http://127.0.0.1:4001/v2/keys/path/demo-XPOST -d value="Val1"

curl -L http://127.0.0.1:4001/v2/keys/path/demo-XPOST -d value="Val2"

curl -L http://127.0.0.1:4001/v2/keys/path/demo-XPOST -d value="Val3"

curl -L http://127.0.0.1:4001/v2/keys/path/demo

{

  "action": "get",

  "node": {

  "key": "/path/demo",

  "dir": true,

  "nodes": [{

  "key": "/path/demo/206981",

  "value": "Val3",

  "modifiedIndex": 206981,

  "createdIndex": 206981

  }, {

  "key": "/path/demo/206975",

  "value": "Val1",

  "modifiedIndex": 206975,

  "createdIndex": 206975

  }, {

  "key": "/path/demo/206978",

  "value": "Val2",

  "modifiedIndex": 206978,

  "createdIndex": 206978

  }],

  "modifiedIndex": 206975,

  "createdIndex": 206975

  }

}

可以看到在指定的 /path/demo 目录下创建了三个以相应的 createdIndex 同名的键,而键的值是POST操作时设置的内容。这样做的好处是确保了生成存放内容的键依照创建顺序命名,只有在一些对内容顺序敏感的应用场景,这个功能才能够发挥实际的价值。

删除Etcd键和目录的方法是使用HTTP DELETE操作访问相应的URL。对于目录的删除需要加上 dir=true 参数,而删除非空的目录还需要再加上 recursive=true参数。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/path/demo?dir=true\&recursive=true-XDELETE

{

  "action": "delete",

  "node": {

  "key": "/path/demo",

  "dir": true,

  "modifiedIndex": 207070,

  "createdIndex": 206975

  },

  "prevNode": {

  "key": "/path/demo",

  "dir": true,

  "modifiedIndex": 206975,

  "createdIndex": 206975

  }

}

注意,在 Shell 中输入GET或DELETE操作的多个参数时,连接参数的 & 符号需要转义,即写成 \&,见上面命令的例子。

拥抱变化

Etcd API对数据节点操作的其他高级功能上在etcdctl工具中的大部分都有对应的命令,比如监视数据节点变化、设置TTL、原子读写等,没有特别新鲜的新货,大家可直接查询Etcd文档,不在这里枉添篇幅。然而其中有一点依然值得提出与君共赏,那就是Etcd API中的监控变化功能中,提供了个etcdctl里没有的东西:指定监控的时间起点。

试想这样一种情况,用户编写的一个程序通过etcdctl watch命令的方式在循环中等待指定数据节点的变化,当变化发生之后,这个程序开始执行另一端代码处理这个变化。然而,在这个部分的处理还未完成之前,一个新的变化到来了,等到程序完成处理后继续回到下一次 etcdctl watch时,它完全不知道自己刚刚错过了一次数据变化的时间。而指定监控变化的时间起点就能够解决这个问题。

在GET获取数据的时候,加上参数wait=true就能够等待在特定的值上,直到变化发生才返回监控变化后的内容。例如在core-01节点上监控/path/demo1键的变化。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/path/demo1?wait=true

然后在core-02节点上对/path/demo1键进行更新。

core@core-02 ~ $ curl -L http://127.0.0.1:4001/v2/keys/path/demo1-XPUT -d value=”New”

此时在core-01监视的操作会立即返回,curl会在屏幕上打印出此次变化的内容。

{

  "action": "set",

  "node": {

  "key": "/path/demo1",

  "value": "Hey",

  "modifiedIndex": 248640,

  "createdIndex": 248640

  },

  "prevNode": {

  "key": "/path/demo1",

  "value": "Hey",

  "modifiedIndex": 248530,

  "createdIndex": 248530

  }

}

用户通过Etcd API获得的内容比etcdctl 工具多了一些内容,其中包含数据节点的modifiedIndex和createdIndex。因此除了简单的监控,直接使用API还可以指定一个监控变化的起始时间。通过 waitIndex=<参考时间>参数传入。一般来说会使用前一次获得数据节点的 modifiedIndex 值加1作为参考时间的值,即当这个数据节点的 modifiedIndex 大于或等于其原本多1时(即说明发生了变化),就立即返回。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/keys/path/demo1?wait=true\&waitIndex=248640

这样即便在两次监听的间隔区发生了数据变化,应用程序任然可以正确的获得通知消息。

集群的统计信息

除了对数据节点进行操作,通过Etcd API还能够获得一些有用的集群信息。这些信息的API都在 /v2/stats/ 路径下面。例如访问 /v2/stats/leader路径可以获得集群通过 Raft 选举的Leader节点、Follower节点的ID及网络延时等信息。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/stats/leader

{

  "leader": "0acdd9bf38194ea5ad1611ff9a4236f1",

  "followers": {

  "f2558aaa231044f3abbe01510ac2b1d8": {

  ... ...

  },

  "f260afd8224c4854bdf8427d8451da23": {

  ... ...

  }

  }

}

而访问 /v2/stats/self路径将得到一些关于当前所在节点与集群有关的信息。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/stats/self

{

  "name": "f2558aaa231044f3abbe01510ac2b1d8",

  "state": "follower",

  "startTime": "2015-01-17T16:22:02.814304197Z",

  "leaderInfo": {

  "leader": "0acdd9bf38194ea5ad1611ff9a4236f1",

  "uptime": "69h22m13.913201673s",

  "startTime": "2015-01-19T16:20:29.297796915Z"

  },

  "recvAppendRequestCnt": 2457288,

  "recvPkgRate": 20.100779810395967,

  "recvBandwidthRate": 1410.8737348916932,

  "sendAppendRequestCnt": 562007

}

其中的 recvBandwidthRate / recvPkgRate这个节点与 Leader 通信的速度,单位分别是每秒的字节数和每秒的请求数。如果当前节点是Leader节点,则看到的是sendBandwidthRate / sendPkgRate,但含义基本相同。这些数据对于排查集群中的一些问题具有参考作用。

路径 /v2/stats/store 可以获得整个集群的所有Etcd API请求次数的统计数据,这个数据对于普通用户没有太多的价值,而一般是用于评价和分析集群的健康度时提供一些有用的数据。

core@core-01 ~ $ curl -L http://127.0.0.1:4001/v2/stats/store

{

  "getsSuccess": 547387,

  "getsFail": 17829,

  "setsSuccess": 69650,

 "setsFail": 6,

  ... ...

  "expireCount": 139,

  "watchers": 0

}

时间: 2024-09-15 02:17:28

CoreOS实践指南(六):分布式数据存储Etcd(下)的相关文章

CoreOS实践指南(八):Unit文件详解

注:本文首发于CSDN,转载请标明出处. [编者按]在"漫步云端:CoreOS实践指南"系列的前几篇文章中,ThoughtWorks的软件工程师林帆主要介绍了CoreOS及其相关组件和使用,其中已经提到了使用 Unit 文件配置 Systemd 管理的系统服务的方式,本文将详细讲解 Unit 文件具体的格式和可用的参数. 作者简介: 林帆,生在80后尾巴的IT攻城狮,ThoughtWorks成都办公室CloudOps小组成员,平时喜欢在业余时间研究DevOps相关的应用,目前在备考AW

CoreOS实践指南(七):Docker容器管理服务

注:本文首发于CSDN,转载请标明出处. [编者按]在"漫步云端:CoreOS实践指南"系列的前几篇文章中,ThoughtWorks的软件工程师林帆主要介绍了CoreOS及其相关组件和使用.说到CoreOS,不得不提Docker.当Docker还名不见经传的时候,CoreOS创始人Alex就凭着敏锐直觉,预见了这个项目的价值,将Docker做为了这个系统支持的第一套应用程序隔离方案.本文将主要介绍在具体的场景下,如何在CoreOS中恰当的管理Docker容器. 作者简介: 林帆,生在8

漫步云端:CoreOS实践指南(一)

[编者按]Docker和CoreOS都是硅谷创业孵化器的优秀"毕业生",据说两家老板的私交很好,Docker做容器引擎,CoreOS做容器管理,合作得非常愉快,只是随着Rocket的发布逐步"分道扬镳".虽然Docker和CoreOS都在求"简",但是Docker的"简"是力求用户能达到最简便地使用,CoreOS的"简"是追求极致的轻量化,究竟哪个将是Container技术的未来,其实也很难说.今天开始,来

CoreOS实践指南(三):系统服务管家Systemd

[编者按]作为一个操作系统,CoreOS 采用了高度精简的系统内核及外围定制,将许多原本需要复杂人工操作或者第三方软件支持的功能在操作系统级别进行了实现,同时剔除了其他对于服务器系统非核心的软件,比如GUI和包管理器.来自ThoughtWorks的软件工程师林帆将带来"漫步云端:CoreOS实践指南"系列文章,带大家了解CoreOS的精华和推荐的实践方法.本文为基础第三篇:系统服务管家Systemd. 作者简介: 林帆,生在80后尾巴的IT攻城狮,ThoughtWorks成都办公室Cl

网站分析工具实践指南之提升转化诱导力(下)

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 3.促成转化的页面是哪些? 所谓促成转化的页面是指,完成最终转化的用户.或者进入转化阶段的用户,在此之前所浏览过的页面,把这些页面找出来,进行优化将有助于提高最终的转化率. ⇒ 例如在维析的"转化贡献"功能中,可以简单的得到哪些页面促成了用户转化. 分析促成转化的页面,可以分为以下3步: 1.找出促成转化的页面 2.优化促

Linux/centos下安装riak数据库步骤 高度可扩展的分布式数据存储服务

Riak是以 Erlang 编写的一个高度可扩展的分布式数据存储,Riak的实现是基于Amazon的Dynamo论文,Riak的设计目标之一就是高可用.Riak支持多节点构建的系统,每次读写请求不需要集群内所有节点参与也能胜任.提供一个灵活的 map/reduce 引擎,一个友好的 HTTP/JSON 查询接口. Riak 非常易于部署和扩展.可以无缝地向群集添加额外的节点.link walking 之类的特性以及对 Map/Reduce 的支持允许实现更加复杂的查询.除了 HTTP API 外

《术以载道——软件过程改进实践指南》目录—导读

内容提要 术以载道--软件过程改进实践指南 软件过程改进(Software Process Improvement,SPI)是指帮助软件企业建立过程管理.识别改进点.持续优化过程体系.CMMI表示Capabi lity Maturity Mode Integration(能力成热度集成模型),提供了一个指导企业实施过程改进的框架,CMMI是实现过程改进标的一种有效手段和方法. 本书是作者软件工程经验.过程改进经验与CMMI咨询经验的总结,从实践者的角度出发,涉及到了实施CMMI的方方面面,包括C

《数据整理实践指南》一第2章 是我的问题还是数据的问题

第2章 是我的问题还是数据的问题 数据整理实践指南 Kevin Fink 假设给你一份未知来源的数据集,如何确定数据是否有用呢? 这种情况并不少见,给了你一份数据集,却无法提供关于数据来源.如何收集.字段含义等方面的诸多信息.事实上,收到这样的数据可能再正常不过了.在很多情况下,收到的数据可能经过了很多人的处理和加工,和收集的原始数据已经差别甚大,确实也没有人知道这些数据是什么含义了.在本章中,我将一步步引导你如何理解数据.验证数据并最终把数据集转换成可用的信息.特别地,我将探讨洞察数据的特殊方

《写给程序员的数据挖掘实践指南》——1.4本书体例

1.4本书体例 本书秉承践行学习法.我建议大家用书中提供的Python代码来进行习题练习和实验,而不是被动地阅读本书.多多实验.深入代码并在多个不同数据集上尝试本书中的方法是真正理解技术的关键. 我努力做到如下两个方面之间的平衡:一方面,我详细介绍实用的数据挖掘Python代码以便读者可以使用并对其进行修改:另一方面,读者也能了解背后的数据挖掘技术.为防止读者阅读理论.数学及Python代码时大脑的思维停滞,我加入插图和图片来刺激大脑的另一部分. Google研究院院长彼得·诺维德(Peter