本文经授权转载自DockOne,原文作者李响,译者高洪涛。另外,李响将会在CNUTCon 2016上分享etcd相关的技术实践,欢迎关注。
在过去的几个月中,CoreOS致力于完善etcd3 API的beta版,经过对系统的严格测试并且在一些用户的共同协作下,etcd得到了长足的进步。今天etcd v3.0.0,这个由CoreOS开发的分布式键值对存储引擎,正式发布了。
在工程实践中,etcd3已经被集成到大型分布式系统Kubernetes之中。并且我们已经实现了分布式协作原语,包括:分布式锁、选举和软件事务内存(software transactional memory),以保证etcd3的API对广大的应用是足够灵活的。现在我们可以很骄傲地宣布etcd3已经准备好推广给大众了。
“etcd的3.0版本在效率、可靠性和可扩展性等方面都获得长足的进步。它将帮助Kubernetes管理超过数万台机器,甚至更多”,Google产品经理,David Aronchick这样说:“Google容器引擎(GKE)团队非常感谢CoreOS团队能对Kubernetes项目作出持续的贡献。对于云原生社区来说,这使Kubernetes成为一个产品级的容器管理系统”
etcd 3.0是etcd3 API和其数据模型的第一个稳定版本。升级是非常容易的,因为etcd2的JSON外部协议和集群内部协议在etcd3中同样支持。但是,etcd3是通过搜集了etcd2用户的反馈和实际扩展etcd2的经验基础上全新设计了API的产品。这篇文章将会点明etcd3在效率,可靠性和并发控制上改进的闪光点。
“见证了etcd在技术和社区的持续进步并成长为Kubernetes项目的一部分是非常让人激动的。etcd3的正式发布将会使这样的势头得以持续,我们期待将来有更多的功能和特性被引入到Red Hat OpenShift容器应用平台产品中,”Red Hat首席软件工程师Timothy St. Clair如是说。
etcd最早是被设计用来解决CoreOS升级时机器的协调问题的。现在它被用于分布式网络、服务发现、配置管理、任务调度和负载均衡等服务中。原始设计的部分内容被证明是成功的:etcd已经成长为键值对存储引擎,包括JSON外部协议,持续键值更新观察者和有生存时间的键等特性。不幸的是,其他的一些功能被频繁吐槽,包括客户端空闲时会把仲裁的压力释放到集群中,对于老的键存在不可预测的垃圾回收。虽然etcd2可以满足协调机器的任务,但当今微服务的趋势使得etcd需要具备数以万计的客户端去操作数百万键的能力。
etcd3吸取了etcd2的教训。基础服务接口使用gRPC代替了JSON以增加效率。JSON外部协议可以通过gRPC gateway得到支持。有生命周期的键的实现被替换为轻型流式租约存活(streaming lease keepalive)模型。观察者也被重新设计,使用流式多路复用事件替代老式的事件模型。v3的数据模型去除了显示键继承和不稳定的观察窗口,取而代之的是扁平二进制键空间,并使用多版本并发控制的事务语义。
“etcd在我们的Elastic Services Infrastructure(ESI)中作为生产级中心配置管理存储器而使用,”NTT创新研究院的首席架构师Ichiro Fukuda这样介绍道:“我们对etcd v3的发布是非常兴奋的,并希望通过扩大使用它来增强我们的服务。”
我们邀请您加入我们一起庆祝etcd在性能和扩展性的长足进步使etcd的生产级v3版本成为云原生,分布式系统的基石。像华为、NTT和PingCAP这样的公司在它们的产品和项目中是非常依赖这个快速的键值对存储引擎的。
“祝贺CoreOS发布了etcd的v3.0版本。etcd在华为PaaS平台被作为关键组件应用在分布式数据协调和数据改变观察等架构中。随着etcd的3.0版本的发布,我们希望它能帮助我们的平台在扩展性,性能和稳定性这些方面得到显著的提升。我们很高兴过去能与CoreOS团队和技术社区一起工作,并希望持续与CoreOS协作将这项技术和其子系统推到一个更高的层面”——华为PaaS平台首席架构师Ying Xiong这样说。
原生的etcd3客户端使用gRPC协议进行通信。协议消息使用protobuf进行定义,它简化了PRC客户端存根代码的生成并且使协议易于管理。比较来看,即使etcd2客户端经过解析优化后与etcd3的消息处理性能仍然有2倍的差距。并且,gRPC在处理链接方面优势明显,因为gRPC使用单一链接的HTTP2多路复用流式RPC调用,而JSON客户端必须为每个请求建立一个链接。
etcd2的键失效是通过存活时间(TTL)机制实现的。对于每个有存活时间的键,客户端必须周期性的刷新该键以保证到期时它不会被自动删除掉。每个刷新行为都会建立一个新连接并提交一个一致性仲裁给etcd去更新键。为了保证所有的TTL键存活,一个空闲的集群必须支持保持一个最低限度的TTL的键除以平均过期时间的吞吐量。
etcd3中的租约替代了早期TTL的实现。租约减少了保持活动的请求并限制了平稳状态一致性更新。与一个键一个TTL不同,一个租约包含一个TTL,租约被分配给一个键。当租约生命到期了,它所关联的所有键全被删除。这个模型在很多键有相同的租约时减少了保持活性的通信量。保持活性的链接也不会像在etcd2中那样被回收,而是使用多路复用的gPRC流的方式来保持活性。同样的,保持活性现在由领导者来处理,避免了在空闲集群提交一致性协议占用资源过多的情况。
观察者在etcd中持续观测键值的变化。与ZooKeeper或Consul等返回观察事件不同,etcd可以持续观察从当前版本开始的变化。在etcd2中,这些流式的观察室通过long polling模式的HTTP请求实现的,它使etcd2的服务器极其浪费的为每个观察者建立一个TCP链接。当应用有数千个客户端观察数千个键时,这种机制会很快耗尽etcd2服务器的套接字和内存资源。
etcd3的多路复用观察者使用一个链接。与每次都打开一个新连接不同,客户端会在一个双向gRPC流中注册一个观察者。流会发送一个带有观察者注册ID的事件。多个观察流甚至可以共享相同的TCP链接。多路复用和流式链接共享减少了至少一个数量级的etcd3的内存占用。
跟所有键值对存储引擎相同,etcd的数据模型是键映射到值上。etcd2的模型仅仅保持最近的键值映射是可用的;老版本是被废掉的。但是,跟踪所有键变化或者扫描整个键空间的应用需要一个可靠的事件流去持续重构过去的键状态。为了避免过早的删除事件以保证这些应用在短暂掉线后仍能正常工作,etcd2维护一个短小的全局滑动事件窗口。但是,如果一个观察者从一个窗口划过的修订开始观察,那么观察者将错过废弃的事件。
etcd3去掉了这个不可预计的窗口,取而代之的是通过新的多版本并发控制模型去维护键的历史版本。历史版本保留策略可以为了细粒度存储管理的需要由集群管理员进行配置。通常etcd3通过一个定时器作废老版本的键更改。一个典型的etcd3集群会保存几个小时的作废键。为了稳定处理客户端过长时间离线,不仅仅是透明的网络中断,观察者可以从最后一次观察的历史修订中恢复过来。为了从存储中读取特定时间的数据,键读取请求需要使用一个修订版来标记,这样就会返回键在这个特定版本的值。
另外为了保存历史数据,etcd3用扁平二进制键空间替代了etcd2的继承键结构。事件上,应用更倾向于获取单独的键或者迭代获取一个目录下所有的键。使用继承模式的场景并不常见。etcd3额外提供了在一个区间中键按范围搜索的功能。这个区间模型支持前缀匹配查询和一个键命名习惯模式,这种模式像从一个目录里来列出键一样。
当多个客户端并发读取或修改一个或一组键时,为了阻止损害应用状态的数据竞争,同步原子操作变得格外重要。为了这个目的,etcd2提供了load-link/store-conditional和compare-and-swap操作;一个客户端确定一个先前的版本索引或者值去做匹配,看是否可以更新键。虽然对于简单的信号量和有限的原子更新这些操作是足够的,但是对于一个像分布式锁和事务级内存这种更加要求成熟度的序列化数据访问方案来说,这就明显不适合了。
etcd3可以序列化多个操作为一个条件性的迷你事务。每个事务包含一组条件守护的组合(例如,对于键版本,键值,已经修改的版本和创建版本的检查),当所有提交满足时一组操作被执行(例如,Get、Put、Delete操作),并且任何条件不满足时另外一组操作被执行。事务可以保证分布式锁的安全。在etcd3中,因为访问是否被条件性取决于是否客户端持有它的锁。这意味着如果一个客户端因为时钟倾斜或者错过失效事件而失去它获得的锁,那么etcd3会毫不留情的拒绝客户端失效的请求。
etcd3相对于etcd模型有了质的飞跃。新的etcd3的API是更加有效率和可扩展的。它不仅满足了当今使用etcd的应用的需求,同时满足未来的超大规模的集群。
“我们在我们的大规模分布式存储产品——TiKV中广泛使用etcd。我们将etcd的raft库嵌入到我们存储层去支持一致性复制,并且我们在TiKV集群中使用etcd去协调存储节点,”PingCAP的CEO刘琦介绍说。“我们非常高兴与etcd团队合作,并乐于见到他们新版本的发布!”
有了etcd3,通过多版本历史键数据技术,数据传播会更加可靠。同时etcd3的事务原语使原子性更新多个键变得容易,同步多个进程变的安全。
etcd目前还在活跃开发中去继续在集群同步软件中成为最好的。项目遵循稳定发布模式,这样可以快速开发新功能而不用担心牺牲稳定性。未来,etcd项目计划增加支持更好扩容性的智能代理,自定义etcd个性化协议网关,和更多的测试,更好的保证整个系统。