实录|王渊命谈etcd如何“少踩坑”

12月06日周二晚8点30分,GitChat团队迎来了QingCloud 容器平台负责人,前新浪微博架构师王渊命的《etcd v2/v3 架构与实现解析》的话题交流。以下是主持人小媛子整理的问题精华,记录下了讲师和读者之间分布式系统问题的精彩片段。

问:业务使用的etcd v2 升级到 v3 会有什么问题呢,如何平滑过渡?

答:v2的大多数功能,用v3都能实现,比如用prefix模拟原来的目录结构,用txn模拟CAS,一般不会有什么问题。但因为v2和v3的数据是互相隔离的,所以迁移起来略麻烦。建议先在业务中封装一层,将etcd v2,v3的差异封装起来,然后通过开关切换。

问:Etcd,Zookeeper,Consul选型建议?

答:etcd和zookeeper的选型,建议选择和自己的开发栈比较接近并且团队成员比较熟悉的。二者大多数情况下可以互相替代。如果想使用consul的高级功能,比如DNS、服务注册以及监控,则可以试试consul。

问:怎么用etcd选主?

答:这个其实比较简单,就是通过etcd的CAS机制,多节点同时创建一个key,然后创建成功的节点当leader。

问:etcd可以用来做任务队列么?

答:这个也是可以的,比如kubernetes就是用etcd来做任务分发的,但这种任务的数量不能太大,否则会遇到etcd的写瓶颈。

问:metad的watch是怎么实现的?

答:metad的watch实现的比较简单,因为metad的watch返回的不是变更事件,而是最新的结果。所以metad只维护了一个全局版本号,只要发现客户端watch的版本小于等于全局版本号,就直接返回最新结果。

问:etcd和zk都是作为分布式配置管理的组件。均提供了watch功能,选主。作为初使用者,这两个之间的选取该如何?

答:etcd和zk二者大多数情况下可以互相替代,都是通用的分布式一致性kv存储。二者之间选择建议选择自己的开发栈比较接近并且团队成员比较熟悉的,比如一种是按语言选择,go语言的项目用etcd,java的用zk,出问题要看源码也容易些。

如果是新项目,纠结于二者,那可以分装一层lib,类似于docker/libkv,同时支持两种,有需要可以切换。

问:etcd和eureka、consul 的异同,以及各自的适用场景,以及选型原则。这个问题其实可以把zk也包括进来,这些都有相同之处。

答:先回答选型的问题,etcd和zk的选型前面讲到了,二者的定位都是通用的一致性kv存储,而eureka和consul的定位则是专做服务注册和发现。前二者的优势当然是通用性,应用广泛,部署运维的时候容易和已有的服务一起共用,而同时缺点也是太通用了,每个应用的服务注册都有自己的一套元数据格式,互相整合起来就比较麻烦了,比如想做个通用的api gateway就会遇到元数据格式兼容问题。

这也成为后二者的优势。同时因为后二者的目标比较具体,所以可以做一些更高级的功能,比如consul的DNS支持,consul-template工具,eureka的事件订阅过滤机制。Eureka本身的实现是一个AP系统,也就是说牺牲了一致性,它认为在服务发现和配置中心这个场景下,可用性和分区容错比一致性更重要。

我个人其实更期待后二者的这种专门的解决方案,要是能形成服务注册标准,那以后应用之间互相交互就容易了。但也有个可能是这种标准由集群调度系统来形成事实标准。

后二者我了解的也不深入,感觉可以另起一篇文章了。

问:老师好,请问您为什么选择了etcd,而不是zk、eureka、consul?

答:一方面是比较轻量(相对zk),另外一方面在etcd和consul之间选型有纠结了一阵,但最终还是选择了一个相对通用一些的。consul-template的功能我们通过confd来实现了。

问:我问一下 接上面,etcd和zk各自都有哪些坑可能会被踩到,都有多坑。掉进去了如何爬起来?

答:再回答坑的问题,这个坑的概念比较太广泛了,更详细的可以翻bug列表。但使用中的大多数坑一般有几种:

  1. 误用导致的坑。要先认识清楚etcd,zk的定位,它需要保存的是整个集群共享的信息,不能当存储用。比如有人在某个zk的某个数据节点下创建了大量的子节点,然后获取,导致zk报错,zk的buffer有个4mb的限制,超过就会报错。
  2. 运维方面的坑。etcd,zk这种服务,一般都比较稳定,搭建好后都不用管,但万一某些节点出问题了,要增加节点恢复系统的时候,可能没有预案或者操作经验,导致弄坏集群。
  3. 网络分区以及可用性设计的坑。设计系统的时候,要想清楚如果etcd或zk整个挂了,或者出现网络分区,应用的一部分节点只能连接到少数派的etcd/zk(少数派不可用)的时候,应用会有什么表现。这种情况下,应用的正确表现应该是服务正常运作,但不支持变更,等etcd/zk集群恢复后就自动恢复了。但如果设计不当,有自动化的一些行为,可能带来的故障就大了。

想要少踩坑,一个办法就是我文中提到的,知其然同时知其所以然,另外就是多演练。

问:王老师,您好!请问您是否已经在生产环境使用了etcd?如果有,目前基于etcd构建的系统规模大致是个什么样的量级?

答:已经在生产系统中使用了,正在上线。我们是提供给用户的应用,规模取决于用户的使用。

问:etcd和zk,一旦出现脑裂,再合并会有什么坑?

答:脑裂再合并,这个一致性算法都考虑到了,其实没有啥坑,如果不误操作的话。etcd和zk的集群节点都是固定的,如果不手动删除,少数派一直是不可用的状态。但如果误操作把少数派从集群中踢出,另外组成了一个新的集群,可能就会有问题。

问:etcd的节点越多越好吗?是否会影响性能?多少个节点是性能与高可用的最佳平衡点?

答:肯定不是越多越好,因为它的所有节点的数据都是一致的,增加节点只是增加了容灾能力,并不能提升存储空间以及读写性能,节点越多,raft操作涉及的节点也越多,性能会有下降。除非读请求不通过raft协议,直接读取本地,这样可以提升读请求的容量。

问:如果etcd脑裂,分成三个相等的区,合并后会发生什么情况?是否必然会丢失两个分区的数据提交?

答:如果这种情况下的脑裂,应该每个区都是不可用的,因为每个区都选不出leader。

问:这是否是etcd的一个缺陷?

答:不算缺陷吧,这是为了一致性做出的牺牲。

问:王老师,您好!前面您提到了etcd和zk的存储信息,保存的是整个集群共享的信息,不能当存储用。那么具体共享信息都有哪些呢?有时候为了方便起见,我们也会投机的存储一些比较轻量的数据。这个度该如何把握比较合适呢?

答:共享的信息比如集群的信息,集群中每个节点的信息和配置,另外也可以存需要分发的任务。

问:一个实验性质的硬件集群项目。我们实现了基于Arm的分布式互联的硬件集群(方法参考的是https://edcashin.wordpress.com/2013/12/29/trying-etcd-on-android-mac-and-raspberry-pi/comment-page-1/将etcd跑在Arm开发板上),将Etcd当作一个分布式的数据库使用(但是Etcd本身运行在这些硬件之上),然后参考go-rpio(https://github.com/stianeikeland/go-rpio)实现基于etcd的key-value同步硬件的信息,控制某些GPIO。

问1:目前已知Etcd可以为别的服务提供服务发现,在这个场景下假设已经存在5个运行Etcd节点的硬件,当一个新的Etcd硬件节点被安装时,Etcd能否为自己提供服务发现服务,实现Etcd节点的自动发现与加入?

问2:随着硬件安装规模的增加,Etcd的极限是多少,raft是否会因为节点的变多,心跳包的往返而导致同步一次的等待时间变长?

问3:当规模足够大,发生网络分区时,是否分区较小的一批硬件之间的数据是无法完成同步的?

答:这个案例挺有意思,我一个一个回答。

  1. etcd本来是做服务发现的,如果etcd集群也需要服务发现,那就再来一个etcd集群。 :)可以自己搭建一个etcd cluster或者用etcd官方提供的 discovery.etcd.io。详细参看:[https://github.com/coreos/etcd/blob/master/Documentation/op-guide/clustering.md]
  2. etcd的机制是多节点一致的,所以它的极限有两部分,一是单机的容量限制,内存和磁盘。二是网络开销,每次raft操作需要所有节点参与,节点越多性能越低。所以扩展很多etcd节点是没有意义的,一般是 3,5,7,9。再多感觉就没意义了。如果你们不太在意一致性,建议读请求可以不通过一致性协议,直接读取节点本地数据。具体方式文中有说明。
  3. etcd网络分区时,少数派是不可用状态,不支持raft请求,但支持非一致性读请求。

回答完了,不过感觉这个案例有点特殊,对你们的场景还不是太明白。

问:我们再考虑用类似raft的方式解决在没有中心的情况下的一批硬件的数据的一致性,主要担忧节点过多之后对性能的影响?有一部分公共的配置信息需要,且非常小,可能不超过32个字节,其余数据都是可以丢弃的临时数据?

答:你们的数据需要同步到所有节点上么?那感觉用etcd有点浪费啊。

主要担心etcd的版本控制,如果旧的数据版本能很快过期并被删除则不用担心,否则嵌入式系统的RAM和flash空间可能被浪涌的数据弄爆,如果etcd仍然能游刃有余,可以同步更多的数据,这样可以有更大的意义,而且单个硬件损坏可以不用担心。etcd的历史版本可以通过compact机制清理。

问:etcd本身存储数据的机制是否是高可用的?某个节点所在的数据不慎被抹掉后是否还能恢复?

答:etcd v3 是多版本机制的,每个key的历史版本都能查询出来(没有compact的情况下)。

问:我们在zk的使用过程中,碰到过这样的问题。原先的Master结点并未实际crash,但zk节点发生网络分区。zk认为之前的master已经crash选举出了新的master节点。client也成功获取到了master结点发生切换的通知,其实是有时延,换句话说有的client连接到新的master,有的client连接到了老的master,并且这两个client对同一个数据发起了变更请求。导致数据不一致。我们尚无etcd的生产环境使用经验,etcd是否也有可能出现上述问题?

答:这个是有可能的,并且不可避免的。故障探测是有延迟的。但这个应该最后是能达成一致的吧?

如果更新的请数量比较大,其实会有问题,那这个问题需要压测分析下了。感觉那种情况应该就出现很短的一瞬间。

问:王老师能否给一下大致的拐点数据?

答:你说任务的数量么?具体的量级不好给,可以根据场景判断。一般集群的调度任务是没问题的,但如果是业务的任务,就有问题了。

问:王老师你好,在使用 etcd watch 过程中,有没有一些措施能帮助降低出现惊群(Herd Effect)?

答:能具体描述下场景么?你是不希望一个key的变更触发太多节点?还是不希望变更太频繁触发太多次变更?是想问 “使用 etcd watch 时 期望不希望一个key的变更在短时间触发太多的节点”。这个问题我也遇到了,但没发现太好的办法,除了在客户端做随机延迟啥的。

问:请教一下王老师,对于etcd2集群,我的一个服务同时watch了两个节点,如果在变更非常迅速的情况,比如在批量刷新配置。被watch的两个节点先后被更新,那么两个watch操作在服务端返回的先后顺序会有保证吗。更进一步,如果两个watch操作没有都请求同一个etcd节点,顺序能保证吗?

答:这个顺序肯定不能保证啊,这个里面的变量太多了,并发机制,goroutine调度,网络延迟等等。

问:那我继续追问一下,如果要保持这样的更新顺序我怎么做比较合适尼。比如通过watch一个根节点的变更来监听所有的变更这样靠谱吗。

答:watch根节点是可以的,只要根下的节点不要太多。但这个也很难保证顺序吧。但v2的watch有个bug,子目录删除的时候不会notify,记得有提bug,还不确定是否修正了,后来我们就用v3了,v3没这个问题。

问:如果跨机房部署服务,是部署两套ETCD吗?如果跨机房部署,如何部署及配置?

答:这个要看跨机房的场景。如果是完全无关联需要公网连接的两个机房,服务之间一般也不需要共享数据吧?部署两套互不相干的etcd,各用各的比较合适。但如果是类似于aws的可用区的概念,两个机房内网互通,为了避免机房故障,这个etcd当前没有太好的解决办法,建议的办法是跨可用区部署一个etcd cluster,调整心跳以及选举超时时间,这个办法如果有3个可用区机房,每个机房3个节点,挂任何一个机房都不影响整个集群,但两个机房就比较尴尬。

还有个办法是两个集群之间同步,这个etcdv3提供了一个mirror的工具,但还是不太完善,不过感觉用etcd的watch机制做一个同步工具也不难。这个机制consul倒是提供了,多数据中心的集群数据同步,互相不影响可用性。

相关推荐