(EDT)5月19日凌晨,quay.io坠毁。灾难影响了quay.io消费者和使用quay.io作为构建和分发软件平台的开源项目。红帽重视双方的信任。
SRE工程师团队立即加入进来,并试图尽快稳定Quay服务。但是,当他们这样做时,客户失去了推送新映像的能力,只有偶尔他们能够提取现有映像。由于某些未知原因,在服务扩展到最大容量后,quay.io数据库被锁定。
“发生了什么变化?“-这是在这种情况下通常会问到的第一个问题。我们注意到,在问题发布不久之前,OpenShift专用集群(正在运行quay.io的集群)开始更新到版本4.3.19。由于quay.io由Red Hat OpenShift Dedicated(OSD)提供支持,因此定期更新是司空见惯的,绝不会引起问题。此外,在过去的六个月中,我们多次更新了Quay群集,而没有任何服务中断。
在我们尝试恢复服务时,其他工程师开始使用以前版本的软件准备新的OSD群集,以便在需要时在其上部署所有内容。
根本原因分析
崩溃的主要症状是成千上万的数据库连接雪崩,使MySQL实例被有效禁用。这使得难以诊断问题。我们对客户的最大连接数设置了限制,以帮助SRE团队评估问题。我们没有注意到到数据库的任何异常流量:实际上,大多数请求是读取的,只有少数请求是写入的。
我们还尝试确定数据库流量中可能导致这种雪崩的模式。但是,无法在日志中找到任何模式。在等待带有OSD 4.3.18的新群集准备就绪时,我们一直尝试启动quay.io容器。每次群集满负荷时,数据库都会冻结。这意味着除了所有quay.io容器外,还必须重新启动RDS实例。
到了晚上,我们将服务稳定在只读模式,并禁用了最大数量的非必需功能(例如,命名空间中的垃圾回收),以减少数据库的负载。冻结停止了,但是没有找到原因。新的OSD集群已准备就绪,我们迁移了服务,连接的流量并继续进行监视。
Quay.io在新的OSD群集上稳定运行,因此我们返回了数据库日志,但是找不到解释锁的相关性。OpenShift工程师与我们一起研究了Red Hat OpenShift 4.3.19中的更改是否可能导致了Quay问题。但是,什么也没发现,这个问题在实验室里无法重现。
第二次失败
5月28日(美国东部时间中午)之前不久,quay.io再次因相同的症状而崩溃:数据库被阻止。我们再次全力以赴进行调查。首先,必须还原服务。但是,这次重新启动RDS和重新启动quay.io pod并没有导致任何结果:大量的连接席卷了整个基础。但为什么?
Quay用Python编写,每个pod都作为一个整体容器运行。许多并行任务在容器运行时中同时运行。我们使用的是库
gevent
下gunicorn
处理Web请求。当Quay收到请求(通过我们自己的API或通过Docker API)时,会为其分配一个gevent worker。通常,该工作人员需要与数据库进行通信。第一次失败后,我们发现gevent worker正在使用默认设置连接到数据库。
考虑到大量的Quay Pod和每秒数千个入站请求,理论上大量的数据库连接可能会使MySQL实例不堪重负。通过监视,已知Quay平均每秒处理5000个请求。与数据库的连接数大致相同。 RDS实例的功能可容纳5,000个连接(大约不能说成千上万个)。由于某种原因,连接数量出现了意外的峰值,但是我们没有注意到与传入请求的任何关联。
这次,我们决心找到并解决问题的根源,而不仅仅是重启。已对Quay代码库进行了更改,以限制每个gevent worker的数据库连接数。该数字成为配置中的一个参数:可以“即时”更改它,而无需构建新的容器映像。为了找出实际要处理的连接数量,我们在一个暂存环境中运行了多个测试,这些暂存环境设置了不同的值,以了解这将如何影响负载测试场景。结果,事实证明当连接数超过10K时,Quay开始引发502错误。
我们立即将此新版本部署到生产环境,并开始监视数据库连接时间表。过去,基地在大约20分钟后被封锁。经过30分钟无故障的通话后,我们有了希望,一个小时后,我们有了信心。我们将流量恢复到网站上的帖子,并开始进行事后分析。
在设法解决导致阻塞的问题之后,我们没有找到其真正原因。可以确定的是,它与OpenShift 4.3.19中的任何更改都没有关系,就像版本4.3.18中发生的更改一样,该版本以前可以与Quay一起使用而没有任何问题。
显然,集群中还潜藏着其他东西。
详细研究
Quay.io使用默认设置连接数据库已有六年了,没有任何问题。发生了什么变化?显然,一直到quay.io的流量一直在稳定增长。在我们的例子中,一切看起来都好像达到了某个阈值,这触发了雪崩的连接。第二次崩溃后,我们继续检查数据库日志,但未发现任何模式或明显的关系。
同时,SRE团队一直在努力改善Quay的查询可观察性和整体服务运行状况。已经部署了新的指标和仪表板,以显示客户最需要Quay的哪些部分。
Quay.io在6月9日之前运行良好。早晨(通过EDT),我们再次见证了数据库连接数量的显着增加。这次没有停机时间,因为新参数限制了它们的数量,并且不允许超出MySQL带宽。但是,在大约半小时内,许多用户注意到quay.io运行缓慢。我们使用添加的监视工具迅速收集了所有可能的数据。突然出现了一种模式。
在连接跃迁之前,大量请求已发送到App Registry API... App Registry是quay.io的鲜为人知的功能。它允许您存储诸如Helm图表和富含元数据的容器之类的东西。大多数quay.io用户不使用此功能,但是Red Hat OpenShift正在积极使用它。 OpenShift中的OperatorHub将所有运算符存储在App Registry中。这些运营商构成了OpenShift工作负载生态系统和以合作伙伴为中心的运营模型(在第2天运营下)的基础。
每个OpenShift 4群集都使用内置的OperatorHub中的运算符来发布可用于安装的运算符目录,并为已安装的运算符提供更新。随着OpenShift 4的日益普及,世界各地的集群数量都在增加。这些群集中的每个群集都使用quay.io中的App Registry作为后端来加载操作员内容以运行内置的OperatorHub。在寻找问题的根源时,我们错过了以下事实:随着OpenShift的普及逐渐增加,很少使用的quay.io函数之一的负载也增加了。
我们对App Registry请求流量进行了一些分析,并研究了注册表代码。立即发现了缺陷,由于这些缺陷导致对数据库的查询不够理想。它们在轻负载下不会造成任何麻烦,但是当负载增加时,它们就会成为问题的根源。事实证明,App Registry有两个问题端点,无法对负载增加做出很好的响应:第一个给出了存储库中所有程序包的列表,第二个返回了程序包的所有Blob。
消除原因
在接下来的一周中,我们一直在优化App Registry本身及其环境的代码。显然,低效率的SQL查询被重新处理,消除了对该命令的不必要调用
tar
(每次获取Blob时都会运行该命令),并尽可能添加缓存。然后,我们进行了广泛的性能测试,并比较了更改前后App Registry的性能。
过去耗时半分钟的API请求现在已在毫秒内完成。我们在下周推出了对生产的更改,并且quay.io从那时起一直保持稳定。在此期间,App Registry终结点的流量出现了一些峰值,但是所做的改进已防止数据库中断。
我们学到了什么?
显然,任何服务都试图避免停机。就我们而言,我们认为最近的崩溃使quay.io变得更好。对于我们自己,我们已经学习了几个主要的经验教训,我们希望与大家分享:
- 有关谁在使用您的服务以及如何使用的信息不是多余的。因为Quay“工作正常”,所以我们不必花费时间优化流量和管理负载。所有这些都造成了一种错误的安全感,即该服务可以无限扩展。
- , — . Quay , . , — , .
- 评估每个服务功能的影响。客户很少使用App Registry,因此这不是我们团队的优先事项。当产品的某些功能很少使用时,它们的错误很少弹出,并且开发人员停止监视代码。容易误以为这是应该的方式,直到突然这一功能成为大规模事件的中心。
下一步是什么?
确保服务稳定性的工作从未停止,我们正在不断改进它。quay.io上的流量持续增长,我们认识到我们有责任尽一切努力以赢得客户的信任。因此,我们目前正在执行以下任务:
- , RDS.
- RDS. . , ( ); .
- . , .
- firewall’ - (WAF), , quay.io.
- , Red Hat OpenShift App Registry (Operator Catalogs), , quay.io.
- 对开放容器倡议(OCI)工件规范的支持可以长期替代App Registry。当前,它已作为本机Quay功能实现,并且在规范本身最终确定后将对用户可用。
当我们从一个像创业公司这样的小型团队迁移到由SRE驱动的成熟平台时,上述所有内容都是Red Hat对quay.io进行的持续投资的一部分。我们知道,我们的许多客户都依赖quay.io来进行日常工作(包括Red Hat!),并尝试对最近的中断和不断努力进行改进保持开放。
译者的PS
另请参阅我们的博客: