「etcd」- 概念与架构

问题描述

2013 年,CoreOS 团队需要一个协调服务来存储服务配置信息、提供分布式锁等能力;

需求概述
该服务需要满足五个目标:可用性角度:高可用;数据一致性角度:提供读取“最新”数据的机制;容量角度:低容量、仅存储关键元数据配置;功能:增删改查,监听数据变化的机制(数据同步);运维复杂度:可维护性;

现有方案
高可用性、数据一致性、功能:从这三个角度来说,ZooKeeper 是满足 CoreOS 诉求的;
配置繁琐:但是,当时的 ZooKeeper 不支持通过 API 安全地变更成员,需要人工修改一个个节点的配置,并重启进程;
容易故障:如果操作错误,则 ZooKeeper 有可能出现脑裂等严重故障;
维护成本:适配云环境、可平滑调整集群规模、在线变更运行时配置是 CoreOS 的期望目标,而 ZooKeeper 在这块的可维护成本相对较高;
资源占用:ZooKeeper 是用 Java 编写的,占用较多的内存资源;
部署繁琐:
专有接口:同时 ZooKeeper RPC 的序列化机制用的是 Jute,自己实现的 RPC API,其无法使用 curl 等常用工具来操作,CoreOS 期望使用比较简单的 HTTP + JSON;

解决方案

CoreOS 决定开始开发协调服务(具体原因我们不得而知,总之 CoreOS 开始着手 etcd 开发);

原理简述

概览架构

详细架构

Client Layer

该层层包括:
1)Client API v2 和 Client API v3 两个版本,提供简洁易用的 API,
2)同时支持负载均衡、节点间故障自动转移,可极大降低业务使用 etcd 复杂度,提升开发效率、服务可用性;

API Layer

该层包括 client 访问 server 和 server 节点之间的通信协议;
1)Client 访问 etcd 的 API v2 和 API v3 两个版本:API v2 使用 HTTP/1.x 协议;API v3 使用 gRPC 协议,且 API v3 通过 etcd grpc-gateway 组件也支持 HTTP/1.x 协议,便于各种语言的服务调用;
2)Server 间通信协议(是指节点间通过 Raft 算法实现数据复制和 Leader 选举等功能时使用的协议),其为 HTTP 协议;

Raft Layer

Raft 算法层实现 Leader 选举、日志复制、ReadIndex 等核心算法特性,用于保障 etcd 多个节点间的数据一致性、提升服务可用性等;

功能逻辑层

etcd 核心特性实现层:
1)典型的 KV-Server 模块、Auth-Server 模块、Lease 租约模块、Compactor 压缩模块等,
2)MVCC 模块:主要由 treeIndex 模块和 boltdb 模块组成;

Storage Layer(存储层)

存储层包含:
1)快照 (Snapshot) 模块、
2)boltdb 模块:boltdb 则保存集群元数据和用户写入的数据;
3)预写日志 (WAL) 模块:其中 WAL 可保障 etcd crash 后数据不丢失;

特性特征

etcd 协调服务使用 Go 语言编写,无依赖,部署简单

2013-08, etcd v0.1, API v1

简单的 HTTP Get/Set/Delete/Watch API,但读数据一致性无法保证;

2013-12, etcd v0.2, API v2

支持通过指定 consistent 模式,从 Leader 读取数据(保证数据强一致性)
发布新的 API v2,这就是大家熟悉的 etcd v2 版本,第一个非 stable 版本;

2014-06,Kubernetes v0.4 发布,使用使用 etcd v0.2 版本

2014-02, etcd v0.3, API v2

Discovery API
并将 Test And Set 机制修正为 CAS(Compare And Swap),解决原子更新的问题

2015-01, etcd v2.0, API v2

Raft 网络、存储抽象、插件化、支持 Quorum Read(等到大多数节点返回数据,则成功读取)

2015/07,Kubernetes v1.0.1 发布,使用 etcd v2.0 版本;

2015-07, etcd v2.1, API v2

实现鉴权和授权 Auth API

2015-09, etcd v2.2, API v2

改进 Client 库,提升错误处理能力;

2016-06, etcd v3

随着 Kubernetes 项目不断发展,v2 版本的瓶颈和缺陷逐渐暴露,遇到若干性能和稳定性问题;

首先是功能局限性问题。它主要是指 etcd v2 不支持范围和分页查询、不支持多 key 事务;

第一,etcd v2 不支持范围查询和分页。分页对于数据较多的场景是必不可少的。在 Kubernetes 中,在集群规模增大后,Pod、Event 等资源可能会出现数千个以上,但是 etcd v2 不支持分页,不支持范围查询,大包等 expensive request 会导致严重的性能乃至雪崩问题;

第二,etcd v2 不支持多 key 事务。在实际转账等业务场景中,往往我们需要在一个事务中同时更新多个 key;

然后是 Watch 机制可靠性问题。Kubernetes 项目严重依赖 etcd Watch 机制,然而 etcd v2 是内存型、不支持保存 key 历史版本的数据库,只在内存中使用滑动窗口保存最近的 1000 条变更事件,当 etcd server 写请求较多、网络波动时等场景,很容易出现事件丢失问题,进而又触发 client 数据全量拉取,产生大量 expensive request,甚至导致 etcd 雪崩;

其次是性能瓶颈问题。etcd v2 早期使用简单、易调试的 HTTP/1.x API,但是随着 Kubernetes 支撑的集群规模越来越大,HTTP/1.x 协议的瓶颈逐渐暴露出来。比如集群规模大时,由于 HTTP/1.x 协议没有压缩机制,批量拉取较多 Pod 时容易导致 APIServer 和 etcd 出现 CPU 高负载、OOM、丢包等问题;

另外 etcd v2 client 会通过 HTTP 长连接轮询 Watch 事件,当 watcher 较多的时候,因 HTTP/1.x 不支持多路复用(每个watcher都需要建立一条连接),会创建大量的连接,消耗 server 端过多的 socket 和内存资源。同时 etcd v2 支持为每个 key 设置 TTL 过期时间,client 为防止 key 的 TTL 过期后被删除,需要周期性刷新 key 的 TTL;

实际业务中很有可能若干 key 拥有相同的 TTL,可是在 etcd v2 中,即使大量 key TTL 一样,你也需要分别为每个 key 发起续期操作,当 key 较多的时候,这会显著增加集群负载、导致集群性能显著下降;

最后是内存开销问题。etcd v2 在内存维护一颗树来保存所有节点 key 及 value。在数据量场景略大的场景,如配置项较多、存储大量 Kubernetes Events, 它会导致较大的内存开销,同时 etcd 需要定时把全量内存树持久化到磁盘。这会消耗大量的 CPU 和磁盘 I/O 资源,对系统的稳定性造成一定影响;

etcd 聆听社区的声音并积极改进,解决社区的痛点。etcd v3 就是为解决以上稳定性、扩展性、性能问题而诞生的;

在内存开销、Watch 事件可靠性、功能局限上,它通过引入 B-tree、boltdb 实现一个 MVCC 数据库,数据模型从层次型目录结构改成扁平的 key-value,提供稳定可靠的事件通知,实现事务,支持多 key 原子更新,同时基于 boltdb 的持久化存储,显著降低 etcd 的内存占用、避免 etcd v2 定期生成快照时的昂贵的资源开销;

性能上,首先 etcd v3 使用 gRPC API,使用 protobuf 定义消息,消息编解码性能相比 JSON 超过 2 倍以上,并通过 HTTP/2.0 多路复用机制,减少大量 watcher 等场景下的连接数;

其次使用 Lease 优化 TTL 机制,每个 Lease 具有一个 TTL,相同的 TTL 的 key 关联一个 Lease,Lease 过期的时候自动删除相关联的所有 key,不再需要为每个 key 单独续期;

最后是 etcd v3 支持范围、分页查询,可避免大包等 expensive request;

2017-01, etcd v3.1

提升线性读性能

2017-03 Kubernetes 1.6 发布,使用 etcd v3,支撑 5000 个节点;

2017-06, etcd v3.2

提升 etcd 并发读取性能;

2018-02, etcd v3.3

boltdb 等稳定性提升,增加数据毁坏检测功能;

2019-08, etcd v3.4

支持全并发读、Learner

2021-06, etcd v3.5

支持集群降级特性;

应用场景

WIP

参考文献

极客时间 /etcd 实战课(唐聪,腾讯云资深工程师,etcd 活跃贡献者)
《etcd 工作笔记:架构分析、优化与最佳实践》
etcd/Documentation versions