加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

英国《卫报》是如何不停机从MongoDB迁移到Postgres?

发布时间:2019-01-18 19:38:45 所属栏目:编程 来源:佚名
导读:这篇文章介绍了英国《卫报Guardian》为什么和如何从Mongo迁移到Postgres,英国卫报大部分内容 - 包括文章,实时博客,画廊和视频内容 - 都是内部CMS工具Composer中制作的。直到最近一直得到了在AWS上运行的Mongo DB数据库的支持。这个Mongo DB数据库是Guar

考虑到这一点,我们需要一个脚本,可以:

  • 发出HTTP请求。
  • 确保在迁移一段内容后,两个API的响应都匹配。
  • 如果出现错误则停止。
  • 生成详细日志以帮助诊断问题。
  • 发生错误后从正确的点重新启动。

我们开始使用Ammonite,

Ammonite允许您在Scala中编写脚本,Scala是我们团队的主要语言。这是一个很好的机会,可以尝试我们之前没有用过的东西,看看它对我们是否有用。虽然Ammonite允许我们使用熟悉的语言,但也有缺点。虽然Intellij现在支持Ammonite,但当时它没有,这意味着我们失去了自动完成和自动导入。也不可能长时间运行Ammonite脚本。

最终,Ammonite不是正确的工具,我们使用sbt项目来执行迁移。我们采用的方法允许我们使用我们自信的语言工作并执行多次“测试迁移”,直到我们有信心在生产中运行它。

快进到2017年1月,是时候在我们的预生产环境CODE中测试完整的迁移了。

与我们的大多数系统类似,CODE和PROD之间唯一的相似之处是它们运行的​​应用程序的版本。支持CODE环境的AWS基础架构远没有PROD那么强大,因为它的使用率要低得多。

在CODE上运行迁移将有助于我们:

  • 估计PROD上的迁移需要多长时间。
  • 评估迁移对性能的影响(如果有的话)。

为了准确衡量这些指标,我们必须匹配这两个环境。这包括将PROD mongo数据库的备份还原到CODE并更新AWS支持的基础架构。

迁移超过200万项内容需要很长时间,当然比办公时间更长。所以我们一夜之间在屏幕上运行脚本。

为了衡量迁移的进度,我们将结构化日志(使用标记)发送到ELK堆栈。从这里,我们可以创建详细的仪表板,跟踪成功迁移的文章数量,失败次数和总体进度。此外,这些显示在团队附近的大屏幕上,以提供更大的可见性。

迁移完成后,我们采用相同的技术检查Postgres匹配的Mongo中的每个文档。

第三部分 - 代理和生产中的运行

现在,新的Postgres驱动的API正在运行,我们需要使用实际流量和数据访问模式对其进行测试,以确保其可靠和稳定。有两种可能的方法可以实现这一点:更新与Mongo API通信的每个客户端以与两个API通信; 或运行代理,这样做。我们使用Akka Streams在Scala中编写了一个代理。

代理操作相当简单:

  • 接受来自负载均衡器的流量。
  • 将流量转发到主api并返回。
  • 异步将相同的流量转发到辅助api。
  • 计算两个响应之间的任何差异并记录它们。

一开始,代理在两个API的响应之间记录了很多差异,在需要修复的API中出现了一些非常微妙但重要的行为差异。

结构化日志记录

我们在Guardian上进行日志记录的方式是使用ELK堆栈。使用Kibana使我们能够灵活地以对我们最有用的方式显示日志。Kibana使用相当容易学习的lucene查询语法。但我们很快意识到,在当前设置中无法过滤掉日志或对其进行分组。例如,我们无法过滤掉因GET请求而发送的日志。

我们的解决方案是向Kibana发送更多结构化日志,而不是仅发送消息。一个日志条目包含多个字段,例如时间戳,发送日志或堆栈的应用程序的名称。以编程方式添加新字段非常简单。这些结构化字段称为标记,可以使用logstash-logback-encoder库实现。对于每个请求,我们提取了有用的信息(例如路径,方法,状态代码),并创建了一个包含我们记录所需的附加信息的地图。看看下面的例子。

  1. object Logging { 
  2.  val rootLogger: LogbackLogger = LoggerFactory.getLogger(SLFLogger.ROOT_LOGGER_NAME).asInstanceOf[LogbackLogger] 
  3.  
  4.  private def setMarkers(request: HttpRequest) = { 
  5.    val markers = Map( 
  6.      "path" -> request.uri.path.toString(), 
  7.      "method" -> request.method.value 
  8.    ) 
  9.    Markers.appendEntries(markers.asJava) 
  10.  } 
  11.  
  12.  def infoWithMarkers(message: String, akkaRequest: HttpRequest) = 
  13.    rootLogger.info(setMarkers(akkaRequest), message) 

我们日志中的附加结构允许我们构建有用的仪表板并在我们的差异中添加更多上下文,这有助于我们识别API之间的一些较小的不一致。

复制流量和代理重构:

将内容迁移到CODE数据库后,我们最终得到了几乎完全相同的PROD数据库副本。主要区别是CODE没有流量。为了将实际流量复制到CODE环境中,我们使用了一个名为GoReplay(gor)的开源工具。它的设置非常简单,可根据您的要求进行定制。

由于进入我们的API的所有流量首先达到了代理,因此在代理服务器上安装gor是有意义的。请参阅下文,了解如何在您的盒子上下载gor以及如何开始捕获端口80上的流量并将其发送到另一台服务器。

  1. wget https://github.com/buger/goreplay/releases/download/v0.16.0.2/gor_0.16.0_x64.tar.gz  
  2. tar -xzf gor_0.16.0_x64.tar.gz gor  
  3. sudo gor --input-raw :80 --output-http http://apiv2.code.co.uk 

一切都运行良好一段时间,但很快我们的代理几分钟时就遇到了生产中断。经过调查,我们发现代理运行的所有三个盒子同时循环。我们怀疑gor使用了太多资源并导致代理失败。在进一步调查中,我们在AWS控制台中发现这些盒子已经定期循环,但不是在同一时间。

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读