两次错误等于多次学习
错误 #1
学习 #1
错误 #2
学习 #2
第三次魅力
学习 #3
最后的想法
如果您上周二晚上(2020年1月7日)在 DEV 上闲逛,可能在浏览网页时遇到了一些小故障😬。希望这种情况不会发生太多,因为我们使用 Fastly 在边缘进行了大量缓存,这可以大大降低意外宕机时造成的影响……
错误 #1
一切始于周二下午早些时候,当时我和 Ben 决定推出一个分支,该分支将切换到使用新的 SiteConfig 设置,而不是 ENV 变量,以便我们能够更灵活地配置一堆值。我们决定在当天下午 4:45 左右推出这个分支。
新代码一上线,我就尝试打开控制台检查一下。结果让我沮丧的是,控制台根本启动不了,因为这个错误导致应用崩溃了。You cannot use settings before Rails initialize.
我立刻意识到我们需要回滚,于是 Ben 和我迅速向 Heroku 发出了回滚命令,等待所有服务器回滚。应用恢复后,我们开始着手查找问题所在。我们首先查看了错误回溯,最终发现问题出在某个模型验证中的 SiteConfig 值上。
validates :slug,
...
exclusion: { in: ReservedWords.all,
message: "%<value>s is a reserved word. Contact #{SiteConfig.default_site_email} for help registering your organization." }
这引出了我们今晚的第一项学习内容。
学习 #1
Rails 在启动时只会加载一次验证,并且优先于其他所有验证。因此, Rails 文档附带了以下警告:
请注意,验证器在整个应用程序生命周期中只会初始化一次,而不是在每次验证运行时初始化,因此请小心在其中使用实例变量。
毋庸置疑,SiteConfig 在验证过程中无法正常工作,因为在评估验证时 Rails 尚未完全加载。测试中没有显示该错误的原因在于启用了 Rails 的预加载功能。由于我们在开发或测试环境中不使用预加载,因此很容易忽略这一点,稍后会详细介绍。
我和 Ben 对设置 gem 做了一些调查,找到了我们遇到的错误以及相应的修复方法。修复方法是SiteConfig.default_site_email
根据 gem 文档将其设置为只读。我们在开发环境中启用了预先加载,并测试了修复方法。虽然应用启动没有问题,但我们很快意识到,这将导致我们无法更新任何 SiteConfig 值。
错误 #2
鉴于此,Ben 建议我们选择简单的方案,从组织模型验证中移除 SiteConfig。这似乎很容易,而且我们急于完成这项工作,所以我从验证中移除了 SiteConfig,并合并了新的代码。
代码再次投入生产,我们尝试打开 Heroku 控制台,确保一切正常。然而,和之前一样,控制台无法启动,因为 Rails 不断崩溃,错误信息几乎和我们之前遇到的一样。

我们再次立即回滚代码,并开始查看错误,它与之前基本相同,只是这次错误来自我们的一个邮件程序,该程序试图使用 SiteConfig 设置默认:from
字段。这引出了我们的第二个学习点。
学习 #2
即使是最简单的修复也要测试。当我们尝试修复验证问题时,我们测试了将 SiteConfig 设置为只读后会发生什么。修复成功,一切正常。然而,在最后一刻,我们发现只读修复阻止我们更新配置变量,于是我们转向了一个自认为简单的解决方案。由于该解决方案简单易用,我们俩都没有想到在从验证中移除 SiteConfig 后,尝试使用预加载重新加载应用程序。
很多时候,当你在努力修复一个问题时,你会感受到快速修复的压力。而且这种压力甚至可能不是来自别人。当我们试图修复这个问题时,已经快到晚上 7 点了,我和 Ben 只想结束工作,然后下线休息一晚。这种急于完成的压力,加上修复方案的简单性,让我们直接跳过了至关重要的测试步骤。无论你的修复方案多么简单,或者你面临什么样的修复压力,你都必须测试它。虽然这意味着现在会多花一点时间,但如果你的修复方案确实有误,它很可能会为你节省 30 分钟的部署和回滚时间。好吧,继续讲故事!
第三次魅力
在再次执行回滚操作后,我们发现又有 3 处 Rails 尝试在初始化 Rails 之前使用 SiteConfig 变量。我们这次选择的修复方案是恢复使用旧的 ENV 变量,然后在早上再制定长期解决方案。可以肯定的是,在第二轮关闭网站之后,我和 Ben 这次都在测试这个更改。
确认启用 eager_loading 后,我们双方都确认代码能够正常工作,然后合并更改,焦急地等待。最终,这次新代码顺利发布。之后,我们花了几分钟时间总结经验教训,并讨论如何防止此类事件再次发生。
配置问题很容易被忽略,尤其是在运行生产环境应用时,其配置与开发和测试环境略有不同。我立即开始尝试,看看是否有办法在 Heroku 完成部署之前,在生产环境中启动该应用。
学习 #3
进入Heroku 的发布阶段!Heroku 在应用部署之前有一个发布阶段,允许您运行任何您想要的脚本。如果该脚本成功,部署就完成了。如果该脚本失败,Heroku 将暂停部署并提醒您失败。
我们已经在发布阶段运行迁移了,所以我想为什么不用它来“测试启动”应用程序呢?我最终写了一个简单的 bash 脚本来运行迁移,然后发出一个简单的rails runner
命令。
#!/bin/bash
STATEMENT_TIMEOUT=180000 bundle exec rails db:migrate && rails runner "puts 'app load success'"
Rails 运行器必须加载整个 Rails 应用程序才能执行命令,因此它是测试配置问题不会导致无法启动的应用程序被推送到生产环境的完美方法。
最后的想法
在我和 Ben 下班之前,我们为团队的其他成员写了一份简短的总结,并将其放在 Slack 中,以便第二天早上大家能够了解发生了什么,以及为什么站点监控警报会响起。
总而言之,这绝对不是我们最自豪的时刻之一,但事后我还是忍不住感到非常满意。尽管经历了停机,但现在我们已经建立了一套流程,可以防止此类问题再次发生。作为一名 SRE,事故发生后最理想的结果就是找到一种方法来防止它再次发生。在这方面,我们新的预部署脚本是一个巨大的胜利。
这件事也提醒我们要放慢速度。当你到处匆匆忙忙地修改代码时,很容易变得过于放松。这样的事件提醒我们,无论你发布的代码看起来多么无害,都要保持谨慎,并先进行测试!
对于周二晚上被打扰的朋友们,我深感抱歉。但我写这篇文章是想让大家知道,我们没有浪费这次机会,反而从中学到了很多!
鏂囩珷鏉ユ簮锛�https://dev.to/molly/2-wrongs-equal-a-lot-of-learnings-791