etcd 3.2带来规模化监视程序与分布式锁等全新特性


Etcd团队刚刚兴奋地公布了etcd 3.x系列中的最新成员——etcd 3.2.0。这套新版本迎来了代理改进、强化后端并发性、分布式协调服务、更为轻的Go客户端以及JWT身份验证等新的功能与特性。

在今天的文章中,我们将展示etcd 3.2.0当中的几项全新功能,同时以此为基础展开讨论。首先要提到的是多租户机制:即新的命名空间客户与能够提供完全隔离密钥空间的代理。在此之后则要聊聊规模伸缩——gRPC代理现在能够每秒支持上百万个事件。最后,为了更好地协调各类系统,本次新版本引入了新的并发RPC服务,且其中内置有分布式锁及对应选项,可供任意gRPC客户进行轻松访问。

命名空间

对于共享同一套etcd集群的应用程序,为了避免彼此之间的相互干扰,其需要保有仅属于自己的密钥。虽然etcd的验证机制能够利用权限对个别用户的密钥加以保护,但却无法阻止各密钥之间因使用同一名称而引发潜在冲突。相反,应用程序通常会采用前缀参数,而后将此前缀添加到所有应用程序的密钥名称当中以实现密钥的有效命名。然而需要强调的是,这种前缀的正确实现流程往往枯燥且极易出错,因此etcd现在将客户命名空间与代理命名空间皆作为现成的可复用组件向用户交付。

各命名空间负责将etcd密钥组织为各自完全独立的密钥空间。一个命名空间属于一套包含全部密钥且具备一条前缀的视图。一条命名空间内密钥名称在由基础etcd密钥空间进行查看时包含该前缀,但在通过命名空间查看时则不包含前缀。同样的,任意接入命名空间内etcd代理的etcd客户端(可选择将该代理伪装为独立集群以避免暴露核心端点)则只能访问该代理命名空间前缀之下的各条密钥。举例来说,在以下etcd拓扑图当中,某一客户通过代理“/app-A/”访问etcd,而该代理会将指向密钥“abc”的请求翻译为“/app-A/abc”。同样的,“app-B”只能通过“/app-B/abc”访问其“abc”;其中命名空间“/app-A/”与“/app-B/”彼此相互隔离。
keyspaces.png

包含两套命名空间代理的etcd拓扑示例图

命名空间在配置方面亦非常简单易行。我们可以利用以下命令对上述示例中的拓扑结构进行测试:
# start etcd
$ etcd &

launch namespace proxies for /app-A/ and /app-B/

$ etcd grpc-proxy start --endpoints=http://localhost:2379 \
--listen-addr=127.0.0.1:23790 \
--namespace=/app-A/ &
$ etcd grpc-proxy start --endpoints=http://localhost:2379 \
--listen-addr=127.0.0.1:23791 \
--namespace=/app-B/ &
# write to /app-A/ and /app-B/
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23790 put abc a
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23791 put abc z

confirm the different keys were written

$ ETCDCTL_API=3 etcdctl --endpoints=http://locahost:2379 --prefix /app

每秒百万个事件

对于密钥的任何更改都会触发etcd向一切对其加以关注的监视程序进行事件广播。对于一套大规模etcd部署体系,其中往往包含成千上万个并发监视程序。遗憾的是,这些监视程序的存在本身亦会带来对应成本。

数量过于庞大的监视程序往往会引发etcd超载。以下图表所示为一套小型etcd实例在面向大量监视程序进行单一共享密钥持续更新时的超载现象。这里的etcd服务器为一台较为孱弱的n1-standard-1(1vCPU + 3.75 GB内存)机器,而客户端则使用更为强大的n1-standard-32(32vCPU + 120 GB内存)机器,后者能够实现每秒500次写入并通过监视程序占满服务器资源。这意味着继续添加监视程序将最终导致写入速率下降,事件发生率亦将因此低于理想水平。
etcd_watcher_scaling.png

指向单一密钥之监视程序过多时引发的etcd服务器超载

etcd的gRPC代理能够将来自某一服务器监视程序的事件重播至多个其它客户端监视程序。每个代理都能够将相关输入客户端监视程序合并为单一etcd服务器监视程序。具体来讲,该代理能够为多个客户端提供经过合并的监视事件。这些客户端将共享同一服务器监视程序;而代理则将有效帮助核心集中摆脱资源占用压力。

通过添加代理,etcd能够实现每秒高达百万个事件,具体如下图所示。使用与此前示例相同的测试平台,我们为添加到该集群中的每个代理附加100个新的监视程序。每个代理皆与etcd实例一样运行有自己的n1-standard-1实例。当代理数量达到20个时,监视程序活动强度将线性提升至每秒1000个事件;与此同时,etcd集群的每秒写入事件仍低于500次——即未出现任何超载现象。
scaling_with_proxies.png

通过增加监视程序代理提升事件容纳通量

大家可以使用etcd基准测试工具在本地执行类似的实验。以下为利用3个代理对100条分布连接进行测试所带来的延迟变化结果:
$ etcd & $ etcd grpc-proxy start --endpoints=http://localhost:2379 --listen-addr=127.0.0.1:23790 & $ etcd grpc-proxy start --endpoints=http://localhost:2379 --listen-addr=127.0.0.1:23791 & $ etcd grpc-proxy start --endpoints=http://localhost:2379 --listen-addr=127.0.0.1:23792 & $ benchmark watch-latency \     --clients=100 --conns=100     --endpoints=http://localhost:23790,http://localhost:23791,http://localhost:23792 

分布式协调服务

Etcd这类一致性分布式键-值存储方案的一大优势,在于其能够对各分布式系统进行协调与同步。这种协调的一般性原则通常包括分布式共享锁以及领导节点选举。在etcd 3.2当中,分布式共享锁与选举皆被导出为RPC服务,这不仅极大简化了分布式协调流程,同时亦提高了其在高延迟环境下的性能表现。

Etcd v3 API的早期开发工作涉及到编写分布式方法以进行“初步检查”。根据相关经验,行之有效的协调算法实际上非常罕见;我们无法指望每个etcd语言绑定皆拥有良好的锁定协议。一般来讲,拥有分布式锁的第三方etcd绑定只能支持单一简单定制化锁,其负责实现自动等待(即利用调用休眠以限制请求数量),从而避免大量请求对于传输路径的争夺。而由于etcd的gRPC协议能够带来出色的移植易行性,我们自然有理由借此实现更具实效的“锁即服务”尝试。

采用服务器端锁的最大优势在于能够避免网络条件不稳定引发的种种问题。对于客户端锁而言,其必须打开一个监视程序并等待响应以了解该锁是否已经被其它进程所占用。而在另一方面,锁RPC只需要进行一次传输往返,因此由网络延迟带来的影响将得到有效控制;如果指向的锁已经被占用,etcd则会以内部方式分配监视程序,并在该RPC获取锁后再将其返回。下图所示为网络延迟对客户端锁及RPC锁的具体影响。RPC锁的平均延迟更低,而随着网络传输往返用时的下降,二者的性能表现则逐步趋同。
average_lock_latency_round_trips.png

争用与延迟水平提升给锁延迟带来的影响(越低越好)。

各RPC复用etcd的客户端协调代码以实现交叉兼容。要将etcd服务器连接到自身以同时作为客户端,各服务可利用一套新的嵌入式客户端将etcd服务器内部映射为一套etcd客户端。对于负责声明客户端会话的服务器,新版本亦带来一项新的会话恢复功能,用于根据原有租约构建会话。通过这样的代码复用方式,服务器端RPC锁与选举机制皆能够在行为上与etcd的客户端锁及客户端选举机制保持一致。

另外,etcdctl命令行工具还能够帮助用户快速使用etcd锁。以下简单示例将在shell当中对文件f进行持续增量;而锁定机制则确保全部增量都切实得到叠加:
$ echo 0 >f
$ for `seq 1 100`; do
ETCDCTL_API=3 etcdctl lock mylock -- bash -c 'expr 1 + $(cat f) > f' &
pids="$pids $!"
done
$ wait $pids
$ cat f

该锁服务端点通过etcd的grpc网关接收JSON请求。以下示例展示了如何利用JSON获取一套锁并等待该锁的实际释放:
$ lid=$(ETCDCTL_API=3 etcdctl -w fields lease grant 5 | grep '"ID"' | awk ' { print $3 } ')

acquire lock by name "mylock" (base64 encoded) using JSON

$ curl localhost:2379/v3alpha/lock/lock -XPOST -d"{\"name\":\"bXlsb2Nr\", \"lease\" : $lid }" >/dev/null
$ date

lease expires in five seconds, unlocking "mylock" and letting etcdctl acquire the lock

$ ETCDCTL_API=3 etcdctl lock mylock date

了解更多

感兴趣的朋友可以点击此处了解etcd项目最新最重要的发展成果。该项目亦托管着3.2.0版本的签名二进制库以及etcd发布页面上的各历史版本。此GitHub版本也包含与etcd集群操作以及etcd应用程序开发相关的最新说明文档。

与以往一样,etcd团队致力于打造出最出色的分布式一致性键-值存储方案;如果您发现了任何bug、希望提出问题或者相关建议,请点击此处访问etcd问题追踪页面。

此次发布的etcd版本亦将被包含在未来的Tectonic版本当中。如果大家有兴趣了解基于etcd、Kubernetes以及CoreOS的其它分布式计算解决方案,我们亦邀请您点击此处免费体验Tectonic。

原文链接:etcd 3.2 now with massive watch scaling and easy locks(翻译:崔婧雯)

0 个评论

要回复文章请先登录注册