本文翻译自 StreamNative 博客《Deep Dive into Data Placement Policies》。作者陈航,StreamNative 高级工程师,Apache Pulsar PMC 成员。译者钟颖群,微信大数据团队成员,从事大数据基础架构服务建设。
本文将深入研究 Apache Pulsar 中的数据放置策略。在此之前,我们首先需要了解 Pulsar 中的隔离策略。数据放置策略有助于我们隔离 Pulsar 中的数据并实现不同级别的容灾。
一个 Pulsar 集群实例可以为多个业务团队服务。在为多个团队分配资源时,我们往往需要制定适当的隔离策略,以避免不同团队、不同任务之间的资源竞争,从而提供高质量的消息服务。在这种场景下,需要考虑资源隔离并权衡该方案是否可以覆盖所需的应用场景。
Pulsar 同时支持 broker 和 BookKeeper 级别的隔离。如下图所示,对于 broker 级别的隔离,可以将多个 broker 划分为不同的群组并将群组和对应的命名空间关联起来。通过这种方式,我们可以将命名空间中的主题和对应关联群组中的 broker 绑定。更多细节可以参考 Apache Pulsar 隔离系列(四):单集群隔离策略。
Pulsar broker 不仅提供消息服务,同时也通过 BookKeeper 客户端提供消息存储隔离。对于消息服务,我们可以将主题和一组归属于它们的 broker 绑定。对于消息存储隔离,我们需要通过 BookKeeper 客户端配置相应的数据放置策略。
由于该话题涉及的内容较多,本文主要讨论 BookKeeper 的数据放置策略并就如何使用 pulsar-admin
命令来配置这些策略提供指导。
bookie 的数据隔离由 bookie 客户端来控制。在 Pulsar 中 bookie 客户端有两种方式来读写数据。一种是在 broker 端,Pulsar broker 通过 bookie 客户端来读写主题消息。另外一种是在 bookie autoRecovery 时,bookie auditor 会检查 ledger 副本是否满足预期的放置策略,replication worker 会基于所配置的放置策略将 ledger 副本复制到目标 bookie 上。
要开启 Pulsar 的放置策略,我们应该在 Pulsar broker 和 BookKeeper autoRecovery 侧都进行配置。为此,我们可以简单使用 bin/pulsar-admin bookies set-bookie-rack
命令,该命令会将放置策略写入 ZooKeeper,broker 和 autoRecovery 侧的 bookie 客户端将从 ZooKeeper 中读取并使用该放置策略。
BookKeeper 提供三种放置策略:
你可以在满足机架是区域子集的各类部署中使用 RackawareEnsemblePlacementPolicy
和 RegionAwareEnsemblePlacementPolicy
(前者包含在后者中)。在公有云基础设施上可以使用 ZoneawareEnsemblePlacementPolicy
,其中可用区(AZs)是指按照公有云服务所属的数据中心所在地域划分的隔离区域。
现在让我们更深入地了解 RackawareEnsemblePlacementPolicy 和 RegionAwareEnsemblePlacementPolicy 的运作原理。
RackAwareEnsemblePlacementPolicy 会强制将不同的数据副本放置在不同的机架上,以保证数据机架级别的容灾。数据中心中通常有很多机架,每个机架都有很多存储节点。在生产环境中,我们需要将数据副本放置在不同的机架上以实现机架级别的故障容灾。我们可以为每个 bookie 节点配置机架信息,从而通过 RackAwareEnsemblePlacementPolicy
避免机架级别的故障。
如果要使用 RackawareEnsemblePlacementPolicy
,你需要为 bookie 实例配置它们的机架信息。相关命令如下:
bin/pulsar-admin bookies set-bookie-rack --bookie bookie1:3181 --hostname bookie1.pulsar.com:3181 --group group1 --rack rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie2:3181 --hostname bookie2.pulsar.com:3181 --group group1 --rack rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie3:3181 --hostname bookie3.pulsar.com:3181 --group group1 --rack rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie4:3181 --hostname bookie4.pulsar.com:3181 --group group1 --rack rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie5:3181 --hostname bookie5.pulsar.com:3181 --group group1 --rack rack2
…
如图 2 所示,该 BookKeeper 集群有 4 个机架和 13 个 bookie 实例。如果一个主题配置为EnsembleSize = 3
、WriteQuorum=3
且 AckQuorum=2
,那么 bookie 客户端会从 4 个机架中选择 3 个机架,例如图中的 rack1、rack3 和 rack4,再从各机架上分别选择一个 bookie 实例来写入数据,例如图中的 bookie1、bookie8 和 bookie12。
如果机架 rack3 和 rack4 中所有的 bookie 实例都宕机,导致集群不满足 3 个可用机架的要求,客户端将基于 EnforceMinNumRacksPerWriteQuorum
和 MinNumRacksPerWriteQuorum=3
参数来选择用于创建新 Ledger 和恢复旧 Ledger 数据的 bookie。
EnforceMinNumRacksPerWriteQuorum=true
和MinNumRacksPerWriteQuorum=3
,那么 bookie 客户端无法选择待写入的 bookie 并会抛出 BKNotEnoughBookiesException,因为集群只有 2 个可用机架并且 MinNumRacksPerWriteQuorum=3
的条件未满足。这意味着此时既不能创建新 Ledger 也不能恢复旧 Ledger 数据。EnforceMinNumRacksPerWriteQuorum=true
和 MinNumRacksPerWriteQuorum=2
,在恢复旧 Ledger 数据时(例如,旧 Ledger 的 ensemble 是 <bookie1, bookie8, bookie12>),bookie 客户端会从 rack1 和 rack2 中选择两个 bookie,例如 bookie2 和 bookie7 来放置 2 个副本。对于新创建的 Ledger,bookie 客户端会从 rack1 和 rack2 中选择 2 个 bookie,例如 bookie1 和 bookie6。对于最后一个副本,它会随机选择一个 bookie 放置。EnforceMinNumRacksPerWriteQuorum=false
,在恢复旧 Ledger 数据时(例如,旧 Ledger 的 ensemble 是 <bookie1, bookie8, bookie12>),bookie 客户端会从 rack1 和 rack2 中选择 2 个 bookie,例如 bookie2 和 bookie7 来放置 2 个副本。对于新创建的 Ledger,bookie 客户端会从 rack1 和 rack2 中选择 2 个 bookie,例如如 bookie2 和 bookie5。对于最后一个副本,它会随机选择一个 bookie 放置。可以使用 RegionAwareEnsemblePlacementPolicy 来实现区域级容灾。该策略会强制将不同数据的副本放置在属于不同区域的不同机架上以实现区域级故障容灾。为了实现数据中心级容灾,我们通常需要将数据副本写入到不同的数据中心。我们可以为每个 bookie 节点配置区域和机架信息,从而通过 RegionAwareEnsemblePlacementPolicy
来帮助我们实现区域级别的故障容灾。
如果要使用 RegionAwareEnsemblePlacementPolicy
,你需要为 bookie 实例配置它们的区域/机架信息。
相关命令如下:
bin/pulsar-admin bookies set-bookie-rack --bookie bookie1:3181 --hostname bookie1.pulsar.com:3181 --group group1 --rack RegionA/rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie2:3181 --hostname bookie2.pulsar.com:3181 --group group1 --rack RegionA/rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie3:3181 --hostname bookie3.pulsar.com:3181 --group group1 --rack RegionA/rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie4:3181 --hostname bookie4.pulsar.com:3181 --group group1 --rack RegionA/rack1
bin/pulsar-admin bookies set-bookie-rack --bookie bookie5:3181 --hostname bookie5.pulsar.com:3181 --group group1 --rack RegionA/rack2
bin/pulsar-admin bookies set-bookie-rack --bookie bookie6:3181 --hostname bookie6.pulsar.com:3181 --group group1 --rack RegionA/rack2
bin/pulsar-admin bookies set-bookie-rack --bookie bookie7:3181 --hostname bookie7.pulsar.com:3181 --group group1 --rack RegionA/rack2
bin/pulsar-admin bookies set-bookie-rack --bookie bookie8:3181 --hostname bookie8.pulsar.com:3181 --group group1 --rack RegionB/rack3
…
如图 4 所示,BookKeeper 集群有 4 个区域,每个区域有若干机架,每个机架有属于自己的 bookie 实例。如果一个主题配置为 EnsembleSize = 3
、WriteQuorum=3
且 AckQuorum=2
,那么 bookie 客户端会从 4 个区域里面选择 3 个区域,例如图中的 Region A、Region C 和 Region D;再从各区域里分别选择一个机架,例如图中 Region A 的 rack2、Region C 的 rack6、Region D 的 rack8;最后从机架上各选择一个 bookie 实例来写入数据,例如图中的 bookie5、bookie17 和 bookie21。
如图 5 所示,如果 2 个区域宕机,在恢复旧 Ledger 数据时(例如,旧 Ledger 的 ensemble 是 <bookie5, bookie17, bookie21>),bookie 客户端会从 Region A 或者 Region D 中选择一个 bookie 实例来替换不可用的 bookie17。对于新创建的 Ledger,bookie 客户端会选择 Region A 或者 Region D 来写数据副本。在 Region A,将退化为 RackawareEnsemblePlacementPolicy 并从 rack1 和 rack2 上各选择 1 个 bookie。在Region D,将从 rack8 上选择 1 个 bookie。最后,它可能选择 bookie1、bookie6 以及 bookie23 来写入数据副本。
Bookie 群组隔离利用现有的 BookKeeper 机架感知放置策略来实现。“机架”的概念不固定(例如,可以是机架/区域/可用区等)。在本示例中,我们通过 bin/pulsar-admin bookies set-bookie-rack
命令来配置对应的隔离策略。
bin/pulsar-admin bookies set-bookie-rack
The following options are required: [-b | --bookie], [-r | --rack]
Then we need to update the rack placement information for a specific bookie in the cluster. Note that the bookie address format is `address:port`.
Usage: set-bookie-rack [options]
Options:
* -b, --bookie
Bookie address (format: `address:port`)
-g, --group
Bookie group name
Default: default
--hostname
Bookie host name
* -r, --rack
Bookie rack name
在该命令中,我们可以为每个 bookie 指定机架名称和群组名称。机架名称用于表示该 bookie 的所属区域或机架。你可以将群组名和特定的命名空间绑定从而实现命名空间级别的隔离。
bin/pulsar-admin bookies set-bookie-rack
命令会将配置的放置策略写入 ZooKeeper 中,bookie 客户端将从 ZooKeeper 获取该放置策略并在选择 Ledger ensemble 时使用该策略。
机架感知放置策略的基本逻辑如下:当构建 Ledger ensemble 或复制 Ledger 数据时,客户端从不同的机架中挑选 bookie 来写入以降低数据不可用的概率。如果来自不同机架的 bookie 不可用,则策略会回退为在剩余可用 bookie 中随机挑选。
与机架感知策略相比,区域感知放置策略的基本逻辑是,当构建 Ledger ensemble 或复制 Ledger 数据时,客户端从不同的区域中挑选 bookie。对于选定的区域,如果其中存在多个 ensemble,则还需要从不同的机架中选择 bookie。
另外一个建议是在选择 ensemble 时需要考虑当前的磁盘使用率权重。当 BookKeeper 集群长时间运行后,你可能会注意到不同 bookie 的 Ledger 磁盘使用率并不均衡,可以通过在 conf/broker.conf
中设置 DiskWeightBasedPlacementEnabled=true
来启用磁盘权重。开启后,bookie 客户端在选择 Ledger ensemble 时将会考虑磁盘使用情况。
如上面图 1 所示,如果要同时为 Pulsar broker 和 BookKeeper 启用隔离策略,我们需要在 3 个方面进行配置。详细操作请参考 Apache Pulsar 隔离系列(四):单集群隔离策略。
如果要启用放置策略,需要在 broker 和 bookie autoRecovery 端都配置该策略。以下是配置区域感知放置策略的示例。
在 conf/broker.conf
中配置以下参数:
bookkeeperClientRegionawarePolicyEnabled=true
配置以下参数来启用 MinNumRacksPerWriteQuorum:
bookkeeperClientMinNumRacksPerWriteQuorum=2
bookkeeperClientEnforceMinNumRacksPerWriteQuorum=true
配置以下参数来启用基于磁盘权重的放置策略:
bookkeeperDiskWeightBasedPlacementEnabled=true
在 conf/bookkeeper.conf
中配置以下参数:
ensemblePlacementPolicy=org.apache.bookkeeper.client.RegionAwareEnsemblePlacementPolicy
reppDnsResolverClass=org.apache.pulsar.zookeeper.ZkBookieRackAffinityMapping
配置以下参数来启用 MinNumRacksPerWriteQuorum:
minNumRacksPerWriteQuorum=2
enforceMinNumRacksPerWriteQuorum=true
配置以下参数来启用基于磁盘权重的放置策略:
diskWeightBasedPlacementEnabled=true
关于 broker 和 BookKeeper 放置策略的配置,请参考以上内容。
本文从 Pulsar broker 和 BookKeeper 两方面概述了 Pulsar 隔离策略。我们还研究了 Pulsar 和 BookKeeper 的隔离策略如何协同工作以实现命名空间隔离。对于 BookKeeper 隔离策略,我们分别解释了机架感知放置策略和区域感知放置策略的运作原理。这些数据放置策略提供了不同级别的容灾能力。