本文讲述了 HDFS Router-based Federation 的架构和特性。
上期回顾: 漫谈Hbase Fil ter
Hadoop 社区为了解决 HDFS 横向扩展的问题,早前的版本中实现了基于 ViewFs 的 Federation 架构,而在最新的 Hadoop 版本中,社区又实现了基于 Router 的 Federatio n架构,而且在这个架构之上还实现了许多加强集群管理能力的特性。Router 将挂载表从 Client 中抽离了出来,解决了挂载表不一致的问题,本篇文章就会介绍 HDFS Router-based Federation 的架构和特性。node
在 HDFS 单集群的架构中,随着集群规模的扩大,Block Manager 和Namespace 会消耗掉 NameNode 愈来愈多的资源,最终致使NameNode 难以提供可靠的服务。因而就提出了 Federation 架构。缓存
Federation 架构是指由多个子集群联合构成一个 Federation 集群,一般的作法是这些子集群会共享 Datanode.而后由挂载表来维护Federation Namespace 到子集群 Namespace 间的映射关系,这个挂载表存在客户端本地的配置文件里,由客户端来解析从而访问到正确的子集群。在社区的实现中,用了一个新的协议 viewfs:// 来访问Federation Namespace.
安全
小米内部对 Federation 实现作了不少优化。为了提升用户易用性,让用户配合集群迁移,咱们尽量的对用户屏蔽细节,实现访问Federation 集群和普通集群不须要用户修改代码甚至配置,bash
对社区的 ViewFs 增长了一层封装,改用本来的 hdfs:// 协议架构
挂载表存储在 Zookeeper 集群中,客户端周期性的检查挂载表是否有更新异步
实现了 Federation 集群中不一样子集群间的 rename 操做oop
可是 Federation 架构也带来了一些问题,性能
挂载表是由客户端实现,修改代码逻辑须要考虑新老客户端的兼容性并分发新的客户端优化
在实现子集群 Namespace 的 Rebalance 时,难以保证全部客户端同步更新到新的挂载表spa
因而,社区提出了新的 Federation 架构:Router-based Federation
为了对用户屏蔽 Federation 的实现细节,将挂载表的配置和实现从客户端中剥离出来,一个天然的想法引入新的代理服务,客户端直接请求代理服务,再由其解析挂载表后将请求转发给正确的子集群。咱们将这个代理服务叫作 Router.
咱们首先来看 Router 是如何代理转发RPC的。
图中标识的是调用关系。Router 会启动 RouterRpcServer 服务,这个类和 NameNodeRpcServer 同样实现了 ClientProtocol,也就是说 Client 不须要改实现,就能够把 Router 看成 Namenode 来访问。固然,Router 也实现了其余的协议用以管理员来管理 Router 或集群状态。
Router 在经过 RouterRpcServe 收到 RPC 后,显示经过解析挂载表获得对应的子集群和其路径,再经过 ConnectionManager 构造出对应NameNode 的 RPC Client,利用 Client 转发这个 RPC.
ConnectionManager 维护了一组链接池,每一个 RPC 的UserGroupInformation,NameNode Address 和 Protocol 共同构成了链接池的 Key.链接池在构造时会建立必定数量的 RPC Client,随后对于每个过来的 RPC,在链接池里找一个空闲的 RPC Client 用以发送RPC.当空闲的 RPC Client 不够时,由后台的 Creator 线程异步的构造新的链接,同时有后台的 Cleaner 线程负责清理链接池。
Router 如何代理用户的信息将在后面的 Router Security 章节说明。
在 Router 中,每一条 Federation Namespace 到子集群 Namespace的映射对应一个 MountTable,全部的 MountTable 就是集群的挂载表。在 MountTableResolver 中,由类型为 TreeMap<String, MountTable>的成员来管理,Key 为 Federation Namespace 下的路径,Value 为对应的MountTable.在解析的时候,会从这个路径向它的父目录找最近的挂载点,也就是最长匹配,这一点和社区本来的 ViewFs 的实现不一样,Router 支持了嵌套挂载表。
社区还实现了一个支持把一个路径挂在多个集群下的 Resolver,它能够根据指定的规则例如一致性哈希来决定把子目录映射到哪一个子集群上。
挂载表由管理员经过命令来设定,可是为了让全部的 Router 都能读到最新的挂载表,以及 Router 重启后不须要从新设定挂载表,这个挂载表应该持久化存在哪里呢?
为了更方便的管理 Router 的配置和状态,咱们引入了 State Store,这是对于咱们存储 Router 状态的存储服务的一个抽象,目前社区有基于文件系统和基于 Zookeeper 的两种实现。
负责与 State Store 通讯的是 StateStoreDriver,定义了一些基本的GET/PUT 接口,由 StateStoreConnectionMonitorService 维护。StateStoreService 是 Router 管理 State Store 的服务,负责从 State Store 拉取数据,更新注册进来的 RecordStore 的缓存。在 State Store 上存储的叫作Record,目前只有基于 protobuf 的序列化实现。
举例来讲,上面咱们提到的挂载表就是一个 RecordStore 的实现,每条 Mount Table 就是一个 Record,他们被 protobuf 序列化后存储在State Store 上。
有了上面的架构,Router 就能够做为一个无状态的代理层来工做了。但是 Client 再也不直接与NameNode通讯,非 RBF 集群的安全认证方案就失效了,因此就有了 Router 层的安全认证方案。
HDFS实践中用到的认证方案有两个,Kerberos 和 Delegation Token,这两种是针对不一样应用同时在使用的。
咱们先来看 Kerberos 如何在 Router 层实现。
显然,咱们能够将 Router 做为 Service 注册到 Kerberos,由 Router来认证 Client.同时,Router 由做为 HDFS 的超级用户来代理 Client 的用户信息,在代码中能够这样简单的实现
UserGroupInformation routerUser = UserGroupInformation.getLoginUser();
connUGI = UserGroupInformation.createProxyUser(
ugi.getUserName(), routerUser);复制代码
Delegation Token 相对就没这么容易实现了。
按照社区如今的实现,是由 Router 来构造 Delegation Token,认证Client.为了让全部的 Router 能同步已构造的 Delegation Token,须要将其存到 State Store 来让 Router 间进行同步。这样作的好处实现简单,不须要和全部的 NameNode 进行通讯就能够在 Route r层完成认证。坏处是须要 Router 间进行同步,这可能会致使性能问题,以及因为 Zookeeper 并不是保证强一致性,Router 可能会读不到另外一个 Router构造的 Delegation Token,结果 Client 认证失败。
在这样的架构下,社区还实现了不少有趣的特性
挂载表上能够设置权限
聚合 Quota.因为一个挂载表下的子目录能够挂在不一样的子集群上,因此就须要聚合 Quota 来管理全部子集群上的 Quota.
Disabled Namespace.
社区的 RBF 架构图以下
在社区文档中提到的推荐实践是,在每个 NameNode 的机器上启动一个 Router 服务,State Store 是 Zookeeper 实现,挂载表的映射关系中 Federation Namespace 和子集群 Namespace 上的路径相同。
基于 Zookeeper 可能会致使 Delegation Token 认证失败,因此咱们须要调用同步接口,或者换用强一致性的存储,例如etcd.
Router 对 NameNode 隐藏了 Clien t的 ip.Router 应该将 RPC上下文也发给 NameNode,使其能在 Audit Log 里记录 Client 正确的信息。
Rebalance.社区没有实现跨子集群的迁移,可是小米内部实现了跨子集群的 Federation Rename,咱们能够在将来将这一特性合并到社区的 Rebalance 方案中。
本文首发于公众号“小米云技术”,转载请注明出处为公众号:小米云技术,ID:mi-cloud-tech。原文连接:HDFS Router-based Federation