有时候,failure 报告会化身 SLOW_OPS,并获得永生。
Ceph 集群里面,OSD 和 OSD 会定时互相发送心跳。如果有人很长时间不回应,那么另外一方就会打个小报告给 monitor。告诉 monitor,对方很可能已经停摆了。当然,这只是一家之言,monitor 不会因此就断定那个不回消息的人失联了。但是如果收到多个不同来源的报告,都指向同一个 OSD,那么 monitor 就会认为该 OSD 的确是有问题的。而且,本着认真负责的精神,monitor 把所有的小报告的原件都保存了起来。
-
monitor 这边
-
如果 osdmap 已经把失联的 OSD 标记成了
down
,它会逐个联系打报告的 OSD,给他们发送最新版的 osdmap。告诉相关人士,"众卿这下可以放心了"。如果自己变成了 peon,也会把这些小报告重新处理一遍,小报告会被转发给新的 leader,让它定夺。 -
如果打小报告的 OSD 不在 osdmap 里面了,它会直接把这个 OSD 发送的报告全部删除。
-
-
报告人
-
如果打小报告的 OSD 准备重启,或者因为网络故障打算停止运行,它也会主动发消息,让 monitor 取消自己的报告。
-
另外,报告人一旦和失联 OSD 取得了联系,它也会取消自己的报告。
-
或者它不再和失联 OSD 互发心跳了,那么它同样会把之前的报告撤销。
-
-
失联的人
-
如果失联的人其实并没有脱离组织,但是它发现 monitor 已经把自己从 osdmap 除名。那么它会向 monitor 发消息澄清。monitor 收到它的请求,会把所有关于它的报告作废。
-
另外一方面,为了跟踪集群里面的性能问题,Ceph 设计了一个机制,叫
TrackedOp
。我们认为每个请求都有它的生命周期。如果集群出现问题的话,可能会导致请求有很长时间的延迟。这就是我们常说"hang"。最极端的情况就是一个请求永远得不到相应。这里所说的请求包罗万象,从客户端发来的
IO 请求,到 ceph
命令行发到 monitor
的查询,甚至刚才提到的"小报告"都是所谓的
TrackedOp
。每个对自己服务质量有要求的 daemnon
都有个注册表叫做 OpTracker
。每收到一个
TrackedOp
请求,都会把它记录在案。在
OpTracker
的析构函数里面,则会把自己从注册表里面注销掉。所以这个表里面保存着当前所有的
TrackedOp
的引用,如果一个
TrackedOp
收到之后很快就销毁了,那么就认为这个 op
的处理是及时的。但是如果有请求躺在注册表里很长时间,都没有把自己注销,那么很可能它就"hang"住了。处理这个请求的服务在定期汇报健康状况的时候,会把这个问题也一起报告给
monitor。monitor
会进一步汇总,把这类问题一起报告给管理员。这种问题叫做
SLOW_OPS
。
所以,只要可能失联的 OSD 仍然在 osdmap 里面,那么 leader monitor 就会把针对它的小报告一直保留下来。直到 leader 收集到足够的证据,或者举报的人主动撤销或者自己出局。
但是也有可能证据一直不够充分,那么 leader
无法做出判断。而这些报告经
prepare_failure()
处理后,就一直放在
OpTracker
里面,变成了
SLOW_OPS
的一员。对于系统管理员来说,它们成了很碍眼的报警信息。
这里其实有两个问题:
-
prepare_failure()
处理过了小报告,是不是就可以说,monitor 完成了这个请求?从而把它从OpTracker
里面注销? -
monitor、报告人和疑似失联者三方都尽心尽力对报告负责。但是如果报告人因为异常重启没有撤销报告,那么这个它发送的报告就永远无法撤销了。
解决这个问题可能有下面几个方案,他们相对独立:
-
处理完毕失联报告,就直接销毁对应的请求,让
OpTracker
不再跟踪它。这样即使这个报告在短时间之内没有举报成功也不会出现在SLOW_OP
里面。但是问题在于-
如果失联报告暂时没有下文,我们能说处理完毕了吗?换言之,如果一个疑似失联的人没有被及时澄清,或者没有踢出 osdmap,那么这个集群的设计很可能是有问题的。它表示这个集群很可能有严重的网络分裂问题,导致一部分 OSD 之间无法互相通信,但是另外一部分 OSD 之间的网络是联通的,导致疑似失联的 OSD 没有办法在短时间之内获得足够数量的报告。所以把这个问题以
SLOW_OPS
的方式暴露出来也是一个办法,让管理员觉察到问题,虽然这个错误信息比较隐晦。 -
报告人这里对报告也有周密的跟踪机制,它把还未发出去的报告保存在
failure_queue
里面,已经发出去的报告则保存在failure_pending
里面。 如果发出的报告没有下文,也就是说如果它没有撤销,而新版的 osdmap 也没有把失联的 OSD 标记成 "down",那么这些报告会一直保存在 OSD 里面。
-
-
在 monitor 这边加入衰退的机制,如果失联报告很长时间没有其他方的证据坐实这个问题,那么就取消报告。但是在大规模的集群里面,如果集群有突发情况,比如产生大规模的网络分裂,那么短时间之内
failure_info
中会出现大量积压的 failure 报告,如果在持有Monitor::lock
大锁的时候遍历数据它,可能会加重 monitor 的负担,提高 monitor 处理消息的延迟。请注意,Monitor::timer
是一个SafeTimer
,而这个 "safe" 是由Monitor::lock
保障的。而Monitor::ms_dispatch()
也是在Monitor::lock
里面处理消息的。为了缓解这个问题,可以引入一个int
型的 "指针",每次在tick()
里面仅仅处理一定数量的 failure,如果超过这个数量,就把工作留到下次tick()
再做。这个 "指针" 就用来保存下次开始的位置。如果在某次处理部分报告的时候,正好错过了某些刚加入的信息也没关系,以后就会轮到的。除非failure_info
增长的速度大大超过每次tick()
处理的速度。 -
因为 OSD 会定期发送
osd_beacon
给 monitor,所以如果有疑似的失联 OSD 发送 beacon 给 monitor,那么是不是也能说明这个 OSD 并不是真正的脱离了组织呢?但是,如果因为网络的问题,这个 OSD 只是不能和举报的 OSD 联系,那么我们也需要把它踢出 osdmap。但是因为osd_beacon
的发送周期非常长,达 300 秒之久。所以经过这么长的时间,monitor 都无法做出裁决。大概率也能说明集群很难有效地在指定时间内有效地标记失联 OSD 了,至于原因可能是当初发送报告的 OSD 重启,或者干脆是严重的网络分裂问题。但是如果是 OSD 重启的话,我们不应该继续保留SLOW_OPS
了,它逗留的时间已经够久了。