postmortem
□ 总结
在协调世界时10月9日11:15至23:47之间,CircleCI客户在管道,工作流程和工作之间经历了长达数小时的延误和故障,同时CircleCI UI,API和通知也出现了延误.
* 协调世界时11:15至下午1:00,管道已建立,但未处理,未发送外出网络呼和Slack及电子邮件通知。
* 协调世界时下午1:00至2:40,工作流程和工作开始延误,而我们却在工作积压。
* UTC下午15:15至20:48,工作流程和工作开始的延误越来越多,最多可达1小时左右,CircleCI UI和API相继落后了约2小时. 在此期间,一些工作未能启动,一些在建工作流程也未能成功.
*从协调世界时20:48到22:10,我们暂时暂停开始新的工作流程和工作,以便我们的系统能够恢复. 在此期间接受了新的管线并排队.
* UTC22:10至23:47,排队工作流程和工作恢复并成功处理. 其中一些人等待了大约两个小时。
* 至23:47,事件得到缓解.
这一事件的状态页可以在[10月9日-UI和API中出现数据延迟](https://status.circleci.com/incidents/6cfhlr4rmm0)上找到. 本周早些时候,在[10月6日——管道、工作流程、UI数据和通知的延误](https://status.circleci.com/incidents/z4hbq8h49j8c),客户也经历了类似的事件。
Multi CircleCI服务使用由Postgres支撑的Temperal作为可靠的排队机制,两起事件都有同样的原因:向Temperal's Postgres实例的写作速度快于Postgres的交易日志可以归档这些写作的速度. 在这两种情况下,这都造成处理管道和状态通知的多小时延误。
我们评估了一些缓解措施,然后暂停了新的工作流程的启动,以便系统能够安全恢复,并逐步提升。 然后我们暂停工作 直到我们的系统恢复正常
为了永久地解决瓶颈问题,我们把工作分成多个临时例子,每个例子都有自己的独立数据库和工人集群,我们把两者都加载平衡。 我们已经安排在10月12日开始的一周内进行基础设施工程,这将消除这一瓶颈,防止发生这些事件。 因此从10月9日19点11分开始, 我们的工程团队在几个小时内实施,测试, 并推出一个简单的版本 这些变化。 我们还增加了新的警报并调整了基础数据库,以便在高负荷下提高性能。
在此窗口中工作失败的客户可能会重新运行 。
□ 背景情况
为了管理客户工作,CircleCI执行了一系列步骤:我们接受客户管道;我们为工人分配这些管道的工作;我们为这些管道和工作提供最新状况;我们收取客户的信贷。 每一个步骤都由一个独立的服务执行,在两者之间有可靠的排队.
历史上,我们实施了这些可靠的队列,兔子MQ在可持久存储的上方,具有指数回放和死信队列的可靠性. 在过去的一年里,我们越来越多地在[时 (https://temporal.io/)上重排可靠的队列,这比一个更简单、更强大的方案拟订模式提供了更好的持久性和可靠性保障。 我们的建设服务\(将管道配置变成可执行计划\),我们的工作流程管弦乐服务\(将任务分配给工人\),我们的通知服务\(提供状态更新\),以及我们的充电服务\(向客户收取信贷\)现在都使用Temperal.
Timoral将状态用于工作流程和活动\(称为“历史”\)存储在数据库中,这是Timoral 实例中唯一同步和状态的组件。 因此,Timoral实例接受和执行工作流程的能力取决于其支持数据库的写出率。 我们还配置了Timeoral,以便在工作完成后三天从历史中删除已完成的工作流程和活动。
在事件发生时,我们用一个单一的组合式Timoral实例,以一个多AZ Postgres RDS实例作为所有这些可靠的队列的后盾。 虽然多 AZ RDS 实例对区域故障具有弹性,但它通过单一的写-ahead-log\(WAL\)保持数据库一致性. 当时我们已经配置了我们的 Postgres 实例, 将它的 WAL 存储在指定日志卷\ (DLV\) 上, 因此日志写入不会与数据写入相竞争 。 数据库将日志连续保存到备份存储,在日志存档后只能再利用卷上的空间. 如果数据库的日志写得快于其存档,则卷数填充而Postgres没有写出其WAL日志的空间. 然后数据库停止接受来信 完全只读。 RDS DLV仅限于1TB存储和3000 IOPS,而根据AWS,这些限制是硬码的,不能增加.
# 发生的一切 # # What happened #
(所有时间为世界协调时\)
造成"10月6日"事件的因素之一是,我们同时处理大量"Temperal"历史的删除,这反过来又给支撑数据库带来压力. 为了解决10月6日事件, 我们改变了Temperal最大的表格上的清理设置, 这些改动消除了交易日志的主要写作来源,但没有考虑从预定的删除中增加日志写作。 因为我们已经配置了Temperal,在完成3天后从历史中删除已完成的工作流程和活动,10月6日的历史定于10月9日被删除.
从10月9日08:00起,Temporal开始处理这些预定的删除,因此Postgres DLV上的空闲空间开始急剧下降,从08:00的0.8TB降至10:10的0.1TB. 我们的监测在10点28分发出警报 我们在10点31分宣布了事件 当我们的基础设施团队正在调查, 在11: 15 DLV 跑出空间 而Postgres的例子只读。 这停止了所有时间 处理在它的轨道。
11:42,我们重启了Postgres,将WAL日志从DLV移到数据卷上,并使用备用磁盘空间和在那里的IOPS. 这一策略缓解了10月6日的事件,我们知道大约需要60分钟。
在数据库重新启动的同时,我们开始在Temperal上游采用几种装入技术:
* 我们开始调查我们是否应该完全减轻即将到来的工作量,以使其余的工作继续前进,但最终决定这不会产生任何价值。
* 12:29,我们取消了信贷收费更新,这占临时活动的12%。 这让管道使用页 带有 stale 充电数据, 直到我们回填它, 但不会丢失数据 或阻止管道运行。
12:59,数据库已备份并开始从11:15起排队的处理工作,到14:40,这一积压工作已清理完毕并按时开始工作. 然而,到15:15时,由于数据库的路线清理工作已远远落后于用于跟踪排队和排期工作的表格。 这些桌子通常有数以万计被删除的行等待清理,但到了下午后期,它们却有上千万行. 每一次临时操作都要读出这些行,所以数据库在每一行都花费了更多的处理时间,而Temperal跑得更慢. 这导致客户在启动管道和更新状况方面继续拖延一小时。
在接下来的几个小时里,我们尝试了各种缓解措施,包括:
* 操纵时间配置值以减少数据库压力。 这效果甚微。
* 进行人工真空工作,以清理Postgres的死行。 这最终与询问本身争夺CPU,最终适得其反. 我们17点45分取消了它们 以减轻数据库负荷的压力
* 我们开始尝试将所有通知使用套件移到一个单独的临时案例。 这占临时活动的18%。 我们17: 07成功完成移动通知。
*17:16,我们成功地实现了管道状态代表制的优化,使管道状态减少了5%。 这反过来又减少了数据库的写作规模,从而降低了Postgres WAL日志的数据压力.
这些都没有完全缓解这一问题。
到了20:48,我们意识到 Postgres 现在正在打在数据库CPU, 不再瓶颈在日志写作。 我们进一步认识到,我们的上游系统既提交了新的工作,也重新尝试了以前提交和失败的工作。 认识到我们正处在一场经典的重试风暴中,我们暂停了所有入场和重试工作,让它排队,以便数据库能够完全完成在进程中的工作. 然后我们从22:07 慢慢地把排队工作拖了起来 使数据库保持可持续负荷。
这在全部排队工作开始后以23:47完全成功,我们于10月10日02:08结束事件.
□ 未来的预防和改进过程
· 多重时间实例
在减轻和解决事件的同时,从10月9日19点11分起,我们还开始站起多起磨损的暂时性事件,并落实它们之间装入平衡工作的能力。
多个Temperal实例中的每一个实例都维持自己的独立数据库和工人集群,因此与其他实例相隔离. 作为10月6日事件中的尸检行动项目,我们已经安排了基础设施工程,从10月12日开始,预计将需要两周时间,这将消除单一Temperal案和Postgres数据库的瓶颈。 10月9日,我们意识到这项工作根本无法等,所以我们的工程团队在几个小时内实施,测试,并推出一个更简单的计划修改版本.
这些更改于10月10日01:45顺利完成.
** 此外,我们正在改变储存和监测交易日志的方式。 ** 我们的Temperal数据库不再使用固定大小的日志量. 我们正在增加关于日志归档落后多远和数据库写日志的速度的警示,以便我们能够比10月9日早几个小时采取行动。
** 我们还在重新审查临时删除时间表。 ** Temperal 在运行几天后删除工作完成, 因此繁忙的一天在一周后作为数据库的负载返回 。 我们正在改变这些删除的时间和速度。
** 我们正在改变我们的系统在Temperal缓慢时如何重试。 ** 10月9日,我们的系统对Temperal进行了失败的测试,并增加了它的载荷. 正如我们在10月6日事件之后所做的那样,我们正在改变我们的系统,以等待Temperal恢复,所以工作不会被放弃。
□ 结论
客户经验是我们的最高优先事项,我们承诺不断提高我们的系统的可靠性,以适应客户对我们的信任。 我们感谢我们的客户在我们团队努力解决这些事件时表现出的耐心。
请联系我们的支援小组 任何问题或关注.