Published on

AWS EBS Architect Dive Deep

Authors
  • avatar
    Name
    Guming
    Twitter

EBS Dive Deep

基于早期架构分析

1.0 系统架构

EBS(弹性块存储)系统旨在为云环境中的计算实例提供高性能、高可用、可扩展的持久化块级存储服务。其整体架构遵循分层与组件化的核心设计理念,通过将控制平面与数据平面分离,实现了服务的模块化和高内聚。这种设计不仅优化了系统性能和可靠性,也为未来的功能扩展与维护奠定了坚实的基础。本章节将深入剖析构成 EBS 系统的服务器端与客户端(Droplet)的软件堆栈,揭示其内部协同工作的机制。

1.1 核心服务组件

EBS 系统的功能由四个核心后台服务协同提供,每个服务都承担着明确且独立的职责:

  • Xino: 作为系统的API网关和请求入口,Xino负责接收和验证来自客户的所有请求(如创建卷、删除卷等),确保请求的合法性与合规性,并将处理过的请求转发给下游服务。
  • Cloud Manager (CM): 云管理器,主要负责卷挂载(Attachment)生命周期的管理。它处理卷与计算实例之间的关联逻辑,并与存储管理器协作创建和管理访问租约(Lease)。
  • Storage Manager (SM): 存储管理器,是整个EBS系统的“大脑”和控制中心。它负责卷的整个生命周期管理(创建、删除、状态变更)、EBS 存储节点(EBSHost)的调度与健康监控、以及租约的颁发与撤销,是系统实现高可用与故障恢复的关键。
  • Dropletman (DM): 部署在计算节点宿主机上的代理服务,负责执行来自 CM 的指令,管理卷设备在宿主机(Dom0)与客户实例(DomU)之间的挂载与卸载操作。

1.2 Server Stack

EBS 的数据平面采用了Master-Slave,确保每个数据卷在物理上至少存在两个同步副本,从而杜绝单点故障导致的数据丢失。

数据卷的主副本(Master)是处理所有读写 I/O 请求的唯一入口。通过 gnbd 协议,客户端与 Master 节点建立连接。Master 节点内部包含一个复杂而高效的软件堆栈,用于处理数据请求、缓存、持久化以及向从节点(Slave)的同步复制。

下表详细解析了 Master 节点的核心组件及其功能:

组件名称功能解析
gnbd网络块设备协议入口,负责接收和解析来自客户端的 I/O 请求。
Mirror Backend镜像后端,是 I/O 处理的核心。它接收来自 gnbd 的请求,并负责将所有写操作通过 mirror 通道同步复制到 Slave 节点,确保数据冗余。
Cache Backend缓存后端,通常是基于内存的 Page Cache。它缓存热点数据,显著降低读操作的延迟,并优化写操作的性能。
Async IO Manager异步 I/O 管理器,负责将缓存中的脏数据高效、异步地刷写到后端持久化存储中。
S3 Backing StoreS3 后端存储接口,负责将数据最终持久化到成本更低、可靠性极高的对象存储(如 S3)中,实现数据的长期保存。
Diagnostics & Utilities一系列后台工具,如 ebs-diagnostics, soft-fail, log rotate, disk verification, stall detector 等,用于系统的监控、诊断和日常维护。

当一个写请求到达 Master 节点时,Mirror Backend 会通过专用的 slave channel 将该请求同步转发给 Slave 节点。只有在 Slave 节点确认数据写入成功后,Master 节点才会向客户端确认写操作完成。这种同步镜像机制是系统数据高可靠性的基石。

1.3 Client Stack

部署在承载客户计算实例(Droplet)的物理宿主机上,并基于 Xen Hypervisor 虚拟化环境进行构建。其结构分为三个层次:

  1. Xen Hypervisor: 底层的虚拟化管理器,负责硬件资源的隔离与调度。
  2. Dom0: 宿主机操作系统,拥有管理客户虚拟机的特权。EBS 的客户端核心组件部署于此。
  3. DomU: 客户虚拟机实例,运行客户的应用程序。它们通过虚拟化的块设备使用 EBS 服务。

在 Dom0 内部,EBS 客户端堆栈进一步分为用户空间和内核空间两部分:

  • 用户空间 (Userspace):
    • xebs_client: 核心客户端进程,负责与 EBS Master 建立和维持长连接,处理 I/O 请求的收发。
    • xebs_client_control: 一个控制脚本或程序,用于管理 xebs_client 的生命周期,并与 Dropletman (DM) 和 EBS Connection Manager (ECM) 等管理组件交互。
  • 内核空间 (Kernel space):
    • xenbd 内核线程: 一组持久化的内核线程(xenbd_recv, xenbd_send),通过 xgnbd kmod 内核模块实现。它们提供了高效的内核态数据通路,负责将用户空间的 I/O 请求传递给网卡,并将网络返回的数据包传递给上层,同时在 Dom0 中暴露一个块设备接口,如 /dev/xgnbd0。

这些组件协同工作,将远程的 EBS 卷映射为 Dom0 上的一个本地块设备。随后,该 Dom0 设备通过 Xen 的块设备后端驱动(blkbk)机制附加给客户实例(DomU),在 DomU 内部呈现为标准的块设备(如 /dev/sdk 或 /dev/sdp1),客户可以像使用本地磁盘一样对其进行分区、格式化和读写。

通过对系统架构的剖析,我们了解了各个组件的定位与职责。接下来,我们将深入探讨这些组件在处理核心操作时如何进行复杂的交互。

2.0 核心操作流程

理解 I/O 请求在系统中的完整路径以及卷的生命周期管理流程,对于系统性能调优、故障排查和高效运维至关重要。本章节将通过对关键操作流程的逐步解析,揭示系统内部各组件之间复杂的交互细节。

2.1 I/O 请求生命周期分析

读请求流程

为了实现最低的I/O延迟,所有的读请求均由 Master 节点独立处理,无需与 Slave 节点交互。

  1. 发起请求: 客户实例(DomU)中的应用程序发起一个 read() 系统调用。
  2. 虚拟化穿透: 请求经过 Xen mangling 过程,从 DomU 传递到宿主机操作系统 Dom0。
  3. 客户端转发: Dom0 中的客户端堆栈接收到 read() 请求,并将其通过网络转发给对应的 EBS Master 节点。
  4. 服务端处理: Master 节点解码请求,并直接从其块设备(优先访问 Cache Backend)中读取数据。
  5. 返回数据: 读取到的数据沿着原路返回:从 Master 节点传回 Dom0,Dom0 确认 read() 完成。
  6. 完成请求: 数据再次经过 Xen mangling,最终返回给 DomU 中的应用程序,完成整个读操作。

写请求流程

写请求为了保证数据的一致性和持久性,必须同步地在 Master 和 Slave 节点上完成,流程更为复杂。

  1. 发起请求: DomU 中的应用程序发起一个 write() 系统调用。
  2. 虚拟化穿透: 请求通过 Xen mangling 传递到 Dom0。
  3. 客户端转发: Dom0 中的客户端堆栈将 write() 请求发送至 EBS Master 节点。
  4. 同步镜像: Master 节点的 Mirror Backend 组件解码请求后,并不立即写入本地,而是首先通过 mirror 通道将该写请求 同步转发 给 Slave 节点。
  5. 从节点写入: Slave 节点接收并解码请求,执行 Page Cache write() 操作,将数据写入其本地 缓存后端 (Cache Backend)。完成后,向 Master 发送确认消息。
  6. 主节点写入: Master 节点在收到 Slave 的确认后,才执行本地的 Page Cache write() 操作,将数据写入自身的 缓存后端 (Cache Backend)。
  7. 返回确认: Master 节点向 Dom0 的客户端返回写操作成功的确认。
  8. 完成请求: 确认消息最终通过 Xen mangling 返回给 DomU 中的应用程序,标志着写操作完成。

这种“Master-Slave同步写”机制确保了任何被确认的写操作都已在两个独立的物理节点上留有副本,是系统高可用性的核心保障。

2.2 卷生命周期管理

EBS 卷在其生命周期中会经历一系列明确定义的状态转换。理解这些状态是管理和使用卷的基础。

  • creating: 卷正在被创建和初始化的中间状态。
  • available: 卷已创建成功,处于空闲状态,可以被挂载。
  • in-use: 卷已被挂载到一个实例,正在被使用。
  • deleting: 卷正在被删除的中间状态。
  • deleted: 卷已被成功删除,资源已回收。
  • error: 卷在操作过程中遇到错误,需要人工干预。

创建卷 (Create Volume)

创建一个新卷需要控制平面的多个组件协同完成,并内建了多层重试机制以保证创建的成功率。

  1. 客户通过 Xino 发起 create volume 请求。
  2. Xino 验证请求(如权限、配额等)后,将其转发给 Storage Manager (SM)。
  3. SM 将卷的初始状态设置为 creating。
  4. 控制平面重试: SM 随机选择一个健康的 EBSHost (如 EBSHostA) 作为 Master 节点候选,并向其发送创建指令。如果 EBSHostA 因空间不足等原因失败,SM 会自动选择另一个随机的 EBSHost (如 EBSHostC) 重试。
  5. 数据平面重试: 成为 Master 的 EBSHostC 负责为卷寻找一个 Slave 节点。它会随机选择一个对等节点 (如 EBSHostB)。为提高容错能力,选择策略会优先确保 Slave 与 Master 不在同一个机架 (rack),甚至不在同一个机房 (room)。如果 EBSHostB 因空间不足等原因无法建立连接,EBSHostC 会自动选择另一个对等节点 (如 EBSHostD) 再次尝试。
  6. EBSHostC 与 EBSHostD 成功建立对等连接 (peer connection),完成镜像通道的初始化。
  7. 初始化成功后,两个节点分别向 SM 回报 bless okay,确认卷已创建。
  8. SM 在收到双方确认后,将卷的状态更新为 available,并通过 Xino 向客户返回创建成功的消息。

挂载卷 (Attach Volume)

挂载卷是一个两阶段过程,首先由控制平面创建访问授权(租约),然后由客户端使用该授权与数据平面建立连接。

阶段一:创建租约 (Lease Creation)

  1. 客户向 Xino 发起 attach volume 请求。
  2. Xino 将请求转发给 Cloud Manager (CM)。
  3. CM 请求 Storage Manager (SM) 为该卷 create lease。
  4. SM 将卷的状态从 available 更新为 in-use,并将租约状态设置为 active。随后,它将生成的 lease-id 返回给 CM。
  5. CM 将挂载任务的状态更新为 attaching,并携带 lease-id 指示 Dropletman (DM) 执行具体的挂载操作。

阶段二:建立连接 (Device Connection)

  1. DM 指示其管理的 EBS Connection Manager (ECM) 使用 lease-id 进行连接。
  2. ECM 向 SM redeem lease(兑换租约)。SM 验证租约的有效性,并返回 Master 和 Slave 节点的地址信息。
  3. 客户端控制脚本 xgnbd_client_control.sh 启动 xebs_client 进程。
  4. xebs_client 首先向 SM 发送 bless 请求,注册其连接意图。SM 验证通过后返回 bless okay,正式授权该客户端连接到数据平面。
  5. 获得授权后,xebs_client 与 EBSMaster 建立长连接套接字 (socket)。此连接通过一个阻塞式的 do_it_ioctl 调用来维持。这种设计在架构上极为高效:它允许一个用户空间进程 (xebs_client) 直接管理一个内核态的连接,而该调用的返回(尤其是带错误码的返回)成为一个简单而强大的信号,能立即通知整个客户端堆栈连接已断开,从而触发后续的故障恢复逻辑。
  6. 连接建立后,CM 被通知,并将挂载状态更新为 attached。
  7. 最后,DM 将 Dom0 中出现的 xgnbd 设备附加给目标客户实例(DomU)。

卸载卷 (Detach Volume)

卸载卷是挂载的逆过程,同样包括正常流程和异常处理机制。

正常卸载流程

  1. 客户发起 detach volume 请求,经由 Xino 到达 CM,CM 将挂载状态更新为 detaching。
  2. DM 收到指令后,首先解除 xgnbd 设备到客户实例的附加。
  3. 客户端各组件(ECM, xebs_client 等)收到 disconnect 指令,安全地断开与 EBSMaster 的连接,xebs_client 进程终止。
  4. 客户端随后向 SM 发送 revoke-lease 请求。
  5. SM 将租约状态置为 revoked,并将卷状态从 in-use 回归到 available,卸载完成。

强制卸载流程 (Force Detach)

在客户端无响应或卸载失败的情况下,系统提供强制卸载机制。

  1. CM 发送 revoke-lease with force 指令给 SM。
  2. SM 将租约状态置为 revoking,并直接命令 EBSMaster drop connection,强制断开与客户端的连接。
  3. 客户端的 do_it_ioctl 调用因连接中断而返回,导致 xebs_client 进程终止。
  4. SM 确认客户端连接已断开后,将租约状态更新为 revoked,完成强制卸载。

删除卷 (Delete Volume)

删除卷操作会永久销毁卷及其所有数据,因此设计了审慎的流程和延迟机制。

  1. 客户通过 Xino 发起 delete volume 请求。
  2. 请求最终到达 SM,SM 首先检查卷是否处于 available 状态。只有空闲的卷才能被删除。
  3. 验证通过后,SM 向该卷的 EBSMaster 和 EBSSlave 同时发送删除指令。
  4. 收到确认后,SM 将卷的状态更新为 deleting。
  5. 系统引入了 “收割者延迟 (grim reaper delay)” 机制。在一段可配置的延迟时间(如1、5或15分钟)后,SM 的后台进程才会执行最终的“收割(reaps)”操作,清理所有相关元数据和后端存储资源。
  6. 清理完成后,卷的最终状态被置为 deleted。这个延迟设计为意外删除提供了一个短暂的恢复窗口。

这些精心设计的流程确保了系统操作的原子性、一致性和安全性。而保障这些流程在各种异常情况下仍能稳定运行的,是系统内置的高可用与故障容错机制。

3.0 高可用性与故障容错机制

对于一个企业级存储系统而言,高可用性与故障容错是其核心价值所在。EBS 系统通过在客户端和服务端设计多层次的冗余和自愈机制,能够有效应对硬件故障、网络分区等多种异常情况,保障业务的连续性。

3.1 主从同步复制

系统的核心数据冗余机制是 主从同步复制 (Master-Slave Synchronous Replication)。如前文所述,每一个写操作都必须在 Master 和 Slave 两个独立的物理节点上成功写入后,才向客户端返回确认。这意味着在任何时间点,数据都存在两份完全一致的副本。当 Master 节点所在的服务器发生硬件故障(如磁盘损坏、主板故障等)导致数据无法访问时,Slave 节点上存有最新的、一致的数据副本,从而可以立即接管服务,避免数据丢失。

3.2 客户端故障切换 (Client-Side Failover)

EBS 客户端具备自动检测连接中断并进行故障切换的能力,对上层应用实现了故障的透明化。

当 Master 节点(EBSMasterA)突发硬件故障时,切换流程如下:

  1. 连接中断感知: 客户端 xebs_client 与 EBSMasterA 之间维持的长连接因服务器宕机而中断。这个中断使得阻塞的 do_it_ioctl 系统调用立即返回一个错误。这个返回机制是整个故障感知的关键,它提供了一个即时且明确的失败信号。
  2. 客户端进程终止: xebs_client 进程捕获到这个致命错误后会自行终止。
  3. 租约重新验证: 上层的控制脚本 (xgnbd_client_control.sh) 检测到 xebs_client 进程的退出,立即向 Storage Manager (SM) 发起 revalidate-lease 请求,以重新验证当前租约并获取最新的主节点信息。
  4. 获取新主节点: SM 通过其心跳检测机制已经发现 EBSMasterA 失联。它会自动将原有的 Slave 节点(EBSMasterB)提升为新的 Master,并将 EBSMasterB 的地址信息返回给客户端。
  5. 自动重连: 客户端控制脚本使用获取到的新 Master 地址,重新启动 xebs_client 进程,并建立到 EBSMasterB 的新连接。
  6. 服务恢复: 连接一旦建立,I/O 请求便可以继续发送到新的 Master 节点,整个切换过程对 DomU 中的应用程序是无感的,仅表现为短暂的 I/O 停顿。

3.3 服务器端再镜像 (Server-Side Re-mirroring)

除了应对节点完全宕机,系统还能处理 Master 和 Slave 之间网络连接中断的场景,并通过自愈机制自动恢复冗余状态。

当 Master (EBSHostA) 和 Slave (EBSHostB) 之间的镜像通道因网络问题断开时:

  1. 连接中断: EBSHostA 发现无法连接到 EBSHostB。
  2. 存活仲裁 (防止脑裂): EBSHostA 会向 SM 发起一个 "can I die?" 的问询。这是一个关键的防脑裂机制。当一个节点与它的伙伴失联时,它必须通过中央权威(SM)来确认自己是否被网络分区隔离。SM 拥有全局视图,如果它能与 EBSHostA 通信,就会回复 "no",授权 EBSHostA 继续作为 Master 服务。而被隔离的 EBSHostB 若也发起问询,SM 会指示其下线 ("yes"),从而避免两个 Master 同时存在的危险情况。
  3. 自主选择新从节点: 获得存活授权后,Master EBSHostA 会自主地在健康的对等节点中 picks a random new peer (如 EBSHostC) 作为新的 Slave。此设计体现了数据平面在一定程度上的自治能力。
  4. 请求授权新关系: EBSHostA 与新的 Slave EBSHostC 建立连接,并共同向 SM 请求 bless(祝福)这个新的镜像关系。SM 的角色是作为权威对这个新组合进行认证,而不是直接参与调度。
  5. 触发再镜像: 在得到 SM 的 blessed 确认后,EBSHostA 启动 remirror volume 过程,将自己的全部数据完整地复制到 EBSHostC。这个过程在后台进行,期间 EBSHostA 已经可以对外提供服务(此时处于单副本无冗余状态)。
  6. 恢复冗余: 当数据完全同步到 EBSHostC 后,系统的双副本冗余状态得到恢复,高可用性得到保障。

这些自动化的故障检测与恢复机制共同构成了 EBS 系统坚固的可靠性基础,为上层业务提供了稳定可靠的存储服务。

4.0 结论

EBS 系统通过其精心设计的分布式架构,成功地为云环境提供了一个高性能、高可用且易于扩展的块存储解决方案。其核心价值在于对数据可靠性和业务连续性的坚定承诺,这体现在系统设计的每一个层面。

EBS 系统的成功实践,源于以下几个关键的设计哲学:

  • 控制平面与数据平面的分离: 系统将负责资源管理和调度的 Storage Manager (SM) / Cloud Manager (CM) 等组件(控制平面)与负责处理 I/O 请求的 EBSMaster/Slave 节点(数据平面)完全解耦。这种分离使得两个平面可以独立扩展和演进,极大地提升了系统的可管理性和灵活性。
  • 无状态客户端与有状态服务端: 客户端(Droplet Stack)的设计相对“无状态”,其核心任务是维持与服务端的连接。所有关于卷状态、主节点位置等关键信息都由 SM 集中管理。这种模式极大地简化了客户端的故障切换逻辑——当连接中断时,客户端只需向 SM 查询最新的状态即可,而无需维护复杂的集群视图。
  • 基于租约的访问控制: 租约(Lease)机制是系统数据一致性的核心保障。它通过 SM 的集中授权,确保在任何时刻一个卷只能被一个客户实例“持有(in-use)”,从根本上杜绝了多个客户端同时写入同一卷而导致的“脑裂”和数据损坏问题。
  • 自动化故障检测与恢复: 系统内置了全面的自动化故障处理能力。无论是客户端感知的硬件故障切换,还是服务器端因网络分区而触发的再镜像自愈,都无需人工干预。这种主动的、自动化的容错设计,是系统鲁棒性的具体体现。

综上所述,EBS 系统的架构不仅解决了当前云存储的核心需求,其模块化、高内聚的设计也为未来的功能演进提供了坚实的基础。无论是引入更先进的缓存策略以优化性能,还是集成更精细的成本控制模型,这套架构都展现了卓越的适应性和扩展潜力。