Oracle Quorum 简介
Oracle Quorum简介通过引入一种称为Oracle的故障检测器,提高了由2个ZooKeeper实例组成的集群的可用性。Oracle的设计目的是在双实例配置中,当另一个实例被故障检测器Oracle判定为故障时,向唯一存活的实例授予权限。
Oracle的实现
每个实例都应访问一个包含0或1的文件,以指示该实例是否已获得Oracle的授权。然而,由于故障检测算法各不相同,这一设计可以更改。因此,开发者可以重写QuorumOracleMaj中的askOracle()方法,以适应首选的Oracle消息解密方式。
部署上下文
Oracle的设计目的是提高由2个ZooKeeper实例组成的集群的可用性;因此,投票成员的数量为2。换句话说,Oracle解决了在两实例集群中可能出现故障实例的共识问题。
当投票成员数量超过2时,确保Oracle正常工作的预期方式是在识别出故障机器时重新配置集群规模。例如,在5个实例的配置下,当故障机器与Leader断开连接时,预期会向集群发送reconfig客户端请求,使集群重新配置为4个实例。因此,一旦投票成员数量等于2,该配置就会进入Oracle设计要解决的问题领域。
如何在zoo.cfg中部署Oracle
无论集群规模大小,oraclePath 必须像其他静态参数一样在初始化时配置。以下展示了正确指定并启用 Oracle 的方法。
oraclePath=/to/some/file
zoo.cfg 配置示例:
dataDir=/data
dataLogDir=/datalog
tickTime=2000
initLimit=5
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
oraclePath=/chassis/mastership
server.1=0.0.0.0:2888:3888;2181
server.2=hw1:2888:3888;2181
QuorumOracleMaj 旨在读取故障检测器的结果,该结果被写入一个文本文件,即oracle文件。
zoo.cfg中的配置如下所示:
oraclePath=/to/some/file
假设您已将故障检测器的结果写入/some/path/result.txt,那么正确的配置如下:
oraclePath=/some/path/result.txt
那么,提供的文件中正确内容应该是什么?可以通过终端执行以下命令创建一个示例文件:
$echo 1 > /some/path/result.txt
任何等效文件都适用于当前QuorumOracleMaj的实现。Oracle文件的数量应等于为启用Oracle而配置的ZooKeeper实例数量。换句话说,每个ZooKeeper实例都应拥有自己的oracle文件,且这些文件不应共享;否则将会出现下一节所述的问题。
启用Oracle后部署的不同之处
当QuorumPeerConfig读取到zoo.cfg包含oraclePath时,它将创建QuorumOracleMaj实例而非默认的QuorumVerifierQuorumMaj。QuorumOracleMaj继承自QuorumMaj,并通过重写containsQuorum()方法与父类区分。QuorumOracleMaj的设计目的是当Leader失去所有follower且无法维持法定人数时,执行其特有版本的containsQuorum。在其他情况下,QuorumOracleMaj将按照QuorumMaj的方式执行。
使用Oracle时需要注意的事项
我们考虑一个异步分布式系统,该系统由2个ZooKeeper实例和一个Oracle组成。
活跃性问题:
当我们考虑预言机满足由[CT]提出的以下属性时:
Strong Completeness: There is a time after which every process that crashes is permanently suspected by every correct processes
系统的活性由Oracle确保。然而,当引入的oracle无法维持该属性时,将如以下示例所示预期发生活性丢失。
假设我们有一个Leader和一个Follower,它们正在广播状态下运行,当出现以下情况时系统将失去活性:
- The Leader fails, but the Oracle does not detect the faulty Leader, which means the Oracle will not authorize the Follower to become a new Leader.
- 当Follower发生故障,但Oracle未检测到该故障Follower时,意味着Oracle将授权Leader推动系统继续运行。
安全问题:
进度丢失
当系统在不同时间发生多次故障时,进度可能会丢失,如下例所示,
假设我们有一个领导者(Ben)和一个跟随者(John)处于广播状态,
At T1 with zxid(0x1_1): L-Ben fails, and the F-John takes over the system under the authorization from the Oracle.
At T2 with zxid(0x2_1): The F-John becomes a new Leader, L-John, and starts a new epoch.
At T3 with zxid(0x2_A): L-John fails
At T4 with zxid(0x2_A): Ben recovers up and starts its leader election.
At T5 with zxid(0x3_1): Ben becomes the new leader, L-Ben, under the authorization from the Oracle.
在这种情况下,系统在L-Ben失败后丢失了进度。
然而,通过使Oracle能够引用最新的zxid,可以防止进度丢失。当Oracle能够引用最新的zxid时,
At T5 with zxid(0x2_A): Ben will not end his leader election because the Oracle would not authorize although John is down.
尽管如此,我们以牺牲活性为代价换取了安全性。
脑裂问题
我们认为Oracle满足[CT]提出的以下期望属性,
Accuracy: There is a time after which some correct processes is never suspected by any processes
然而,Oracle给出的决策应该是互斥的。
换句话说,
假设我们有一个领导者(Ben)和一个跟随者(John)处于广播状态,
- At any time, the Oracle will not authorize both Ben and John even though the failure detectors think each other is faulty. Or
- 在任何时刻,对于任意两个Oracle文件中的任意两个值,这两个值不会同时等于1。
当Oracle在领导者选举阶段无法维持这一属性时,预计会出现脑裂问题
- Start of the system
- 故障实例从故障中恢复。
故障检测器实现概念示例
需要考虑的是,故障检测器的输出结果是授权查询ZooKeeper实例,判断其是否有权在不等待故障实例的情况下推动系统前进,该故障实例由故障检测器识别。
硬件实现方案
假设有两台专用硬件设备hw1和hw2,分别可以托管ZooKeeper实例zk1和zk2,并组成一个集群。有一个硬件设备同时连接到这两台硬件上,能够判断硬件是否通电。因此,当hw1断电时,zk1无疑会出现故障。于是该硬件设备会将hw2上的oracle文件更新为1,这表示zk1已故障,并授权zk2推动系统继续运行。
软件实现
假设有两台专用硬件设备hw1和hw2,可以分别托管ZooKeeper实例zk1和zk2,并组成集群。我们可以在hw1和hw2上分别部署两个额外服务o1和o2。o1和o2的职责是检测另一台硬件是否存活。例如,o1可以持续ping hw2来判断hw2是否通电运行。当o1无法ping通hw2时,o1会判定hw2出现故障,随后将zk1的oracle文件更新为1,这个值表示zk2已故障,并授权zk1推动系统继续运行。
使用USB设备作为Oracle来维护进度
在macOS 10.15.7 (19H2)系统中,外部存储设备会挂载在/Volumes目录下。因此,我们可以插入包含所需信息的USB设备作为oracle。当设备连接时,oracle会授权领导者推动系统前进,这也意味着其他实例将失败。重现这个刺激需要六个步骤。
- 首先,插入一个名为
Oracle的USB设备,然后我们就可以访问/Volumes/Oracle了。 - 其次,我们在
/Volumes/Oracle目录下创建一个名为mastership的文件,内容为1。现在我们可以访问/Volumes/Oracle/mastership,zookeeper实例同样可以访问该文件来判断是否有权限推进系统。该文件可以通过以下命令轻松生成:$echo 1 > mastership
-
第三,您需要有一个类似以下示例的
zoo.cfg文件:dataDir=/data dataLogDir=/datalog tickTime=2000 initLimit=5 syncLimit=2 autopurge.snapRetainCount=3 autopurge.purgeInterval=0 maxClientCnxns=60 standaloneEnabled=true admin.enableServer=true oraclePath=/Volumes/Oracle/mastership server.1=0.0.0.0:2888:3888;2181 server.2=hw1:2888:3888;2181
(注意) 由于本次模拟中仅存在单一USB设备,因此不会出现脑裂问题。 此外,mastership不应由多个实例共享。 因此,仅配置了一个与Oracle连接的ZooKeeper实例。 更多详情请参阅安全议题章节。
- 第四,启动集群,预期它将正常形成法定人数。
- 第五,终止实例时可以不连接USB设备,或者
mastership包含0。预计会出现以下两种情况:
- 发生领导者故障,由于oracle的存在,剩余实例自行完成领导者选举。
- 由于oracle的存在,法定人数仍然得以维持。
- 最后,当USB设备被移除时,
/Volumes/Oracle/mastership将不可用。因此,根据当前实现,每当Leader查询oracle时,oracle会抛出异常并返回FALSE。重复第五步后,预计系统要么无法从leader故障中恢复,要么leader失去法定人数。无论哪种情况,服务都会中断。
通过这些步骤,我们可以轻松演示并实践oracle如何在双实例系统中运作。
参考文档
[CT] Tushar Deepak Chandra 和 Sam Toueg。1991年。《异步系统中的不可靠故障检测器(初步版本)》。发表于第十届ACM分布式计算原理年度研讨会论文集(PODC '91)。美国纽约州纽约市计算机协会,325-340页。DOI:https://doi.org/10.1145/112600.112627
