自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

英國《衛(wèi)報》是如何不停機從MongoDB遷移到Postgres?

數(shù)據(jù)庫 其他數(shù)據(jù)庫 MongoDB
這篇文章介紹了英國《衛(wèi)報Guardian》為什么和如何從Mongo遷移到Postgres,英國衛(wèi)報大部分內(nèi)容 - 包括文章,實時博客,畫廊和視頻內(nèi)容 - 都是內(nèi)部CMS工具Composer中制作的。直到最近一直得到了在AWS上運行的Mongo DB數(shù)據(jù)庫的支持。

[[254518]]

這篇文章介紹了英國《衛(wèi)報Guardian》為什么和如何從Mongo遷移到Postgres,英國衛(wèi)報大部分內(nèi)容 - 包括文章,實時博客,畫廊和視頻內(nèi)容 - 都是內(nèi)部CMS工具Composer中制作的。直到最近一直得到了在AWS上運行的Mongo DB數(shù)據(jù)庫的支持。這個Mongo DB數(shù)據(jù)庫是Guardian所有在線發(fā)布內(nèi)容的“真實來源” - 大約230萬內(nèi)容項。

當初為了遷移到AWS,決定購買OpsManager- Mongo的數(shù)據(jù)庫管理軟件,使用OpsManager來管理備份,處理編排并為我們的數(shù)據(jù)庫集群提供監(jiān)控。

因為Mongo沒有提供任何工具來輕松在AWS上進行設置 - 我們需要手工編寫cloudformation來定義所有基礎架構,最重要的是我們編寫了數(shù)百行ruby腳本來處理安裝監(jiān)視/自動化代理和新數(shù)據(jù)庫實例的編排。

自從遷移到AWS 以來,由于Mongo DB數(shù)據(jù)庫問題,我們發(fā)生了兩次嚴重的中斷,每次在theguardian.com上被阻止發(fā)布內(nèi)容至少一個小時。在這兩種情況下,OpsManager和Mongo的支持服務人員都沒有能夠幫助我們,我們最終解決了這個問題 :

  • 時鐘非常重要 - 不要將VPC鎖定到NTP停止工作的程度。
  • 在應用程序啟動時自動生成數(shù)據(jù)庫索引可能是一個壞主意。
  • 數(shù)據(jù)庫管理很重要而且很難 - 我們寧愿不自己做。

OpsManager并沒有真正兌現(xiàn)其無障礙數(shù)據(jù)庫管理的承諾。例如,實際管理OpsManager本身 - 特別是從OpsManager 1升級到2 - 非常耗時,并且需要有關我們的OpsManager設置的專業(yè)知識。

由于不同版本的Mongo DB之間的身份驗證架構發(fā)生了變化,它也沒有實現(xiàn)其“一鍵升級”承諾。我們每年至少花費兩個月的工程時間來完成這項數(shù)據(jù)庫管理工作。

所有這些問題,加上我們?yōu)橹С趾贤蚈psManager支付的高額年費,讓我們尋找替代數(shù)據(jù)庫選項,具有以下要求:

  • 需要最少的數(shù)據(jù)庫管理。
  • 支持靜態(tài)加密。
  • Mongo的可行遷移路徑。

由于我們所有其他服務都在AWS中運行,因此顯而易見的選擇是DynamoDB - 亞馬遜的NoSQL數(shù)據(jù)庫產(chǎn)品。不幸的是,當時Dynamo不支持靜態(tài)加密。在等待大約9個月后才添加此功能,我們最終放棄并尋找其他東西,最終選擇在AWS RDS上使用Postgres。

“但是postgres不是文件存儲!”我聽到你哭了。嗯,不,它不是,但它確實有一個JSONB列類型,支持JSON blob中字段的索引。

我們希望通過使用JSONB類型,我們可以將Mongo遷移到Postgres,只需對我們的數(shù)據(jù)模型進行最小的更改。此外,如果我們希望將來轉(zhuǎn)向更多的關系模型,我們就有了這個選擇。關于Postgres的另一個好處是它有多成熟:在大多數(shù)情況下,我們想要提出的每個問題都已經(jīng)在Stack Overflow上得到了解答。

從性能的角度來看,我們有信心Postgres可以應對 - 而Composer是一個寫作繁重的工具(每次記者停止打字時都會寫入數(shù)據(jù)庫) - 通常只有幾百個并發(fā)用戶 - 而不是高性能計算!

第二部分 - 二十年的內(nèi)容遷移,沒有停機時間

以下是我們遷移數(shù)據(jù)庫的步驟:

  • 創(chuàng)建新數(shù)據(jù)庫。
  • 創(chuàng)建一種寫入新數(shù)據(jù)庫的方法(新API)。
  • 創(chuàng)建一個代理,使用舊的數(shù)據(jù)庫作為主數(shù)據(jù)庫,將流量發(fā)送到舊數(shù)據(jù)庫和新數(shù)據(jù)庫。
  • 將記錄從舊數(shù)據(jù)庫遷移到新數(shù)據(jù)庫。
  • 使新數(shù)據(jù)庫成為主數(shù)據(jù)庫。
  • 刪除舊數(shù)據(jù)庫。

鑒于我們正在遷移的數(shù)據(jù)庫為我們的CMS提供支持,因此遷移對我們的記者造成的影響很小。畢竟,新聞永遠不會停止。

新API:

2017年7月底,新的Postgres驅(qū)動的API開始工作。所以我們的旅程開始了。

我們簡化的CMS架構是這樣的:數(shù)據(jù)庫,API和與之交互的幾個應用程序(例如Web前端)。堆棧是,現(xiàn)在仍然是使用Scala,Scalatra Framework和Angular.js構建的,它大約有四年的歷史。

經(jīng)過一些調(diào)查,我們得出結論,在我們可以遷移現(xiàn)有內(nèi)容之前,我們需要一種方法來與新的PostgreSQL數(shù)據(jù)庫進行對話,并且仍然像往常一樣運行舊的API。畢竟,Mongo數(shù)據(jù)庫是我們的真相來源。它在試驗新API時為我們提供了安全毯。

這是為什么在舊API之上構建不是一個選項的原因之一。在原始API中幾乎沒有關注點分離,甚至在控制器級別也可以找到MongoDB細節(jié)。因此,在現(xiàn)有API中添加另一個數(shù)據(jù)庫類型的任務風險太大。

我們采用了不同的路線,并復制了舊的API。這就是APIV2誕生的方式。它或多或少是Mongo的復制品,包含相同的端點和功能。我們使用doobie,一個用于Scala的純功能JDBC層,添加了Docker以便在本地運行和測試,并改進了日志記錄和關注點分離。APIV2將成為一個快速而現(xiàn)代的API。

到2017年8月底,我們部署了一個使用PostgreSQL作為其數(shù)據(jù)庫的新API。但這只是一個開始。Mongo數(shù)據(jù)庫中的文章最初是在二十年前創(chuàng)建的,所有這些文章都需要移到Postgres數(shù)據(jù)庫中。

遷移

我們需要能夠編輯網(wǎng)站上的任何文章,無論它們何時發(fā)布,因此所有文章都作為單一的“事實來源”存在于我們的數(shù)據(jù)庫中。

雖然所有文章都存在于Guardian的內(nèi)容API(CAPI)中,它為應用程序和網(wǎng)站提供支持,但正確的遷移是關鍵,因為我們的數(shù)據(jù)庫是“真相的來源”。如果CAPI的Elasticsearch集群發(fā)生任何事情,那么我們將從Composer的數(shù)據(jù)庫中重新索引它。

因此,在關閉Mongo之前,我們必須確信在Postgres驅(qū)動的API和Mongo驅(qū)動的API上的相同請求將返回相同的響應。

為此,我們需要將所有內(nèi)容復制到新的Postgres數(shù)據(jù)庫。這是使用直接與新舊API對話的腳本完成的。這樣做的好處是,API已經(jīng)提供了一個經(jīng)過良好測試的界面,用于從數(shù)據(jù)庫讀取和寫入文章,而不是直接編寫訪問相關數(shù)據(jù)庫的內(nèi)容。

遷移的基本流程是:

  • 從Mongo獲取內(nèi)容。
  • 將內(nèi)容發(fā)布到Postgres。
  • 從Postgres獲取內(nèi)容。
  • 檢查一個和三個的響應是否相同

如果您的最終用戶完全沒有意識到它已經(jīng)發(fā)生并且一個好的遷移腳本始終是這個的重要組成部分,那么數(shù)據(jù)庫遷移真的很順利。

考慮到這一點,我們需要一個腳本,可以:

  • 發(fā)出HTTP請求。
  • 確保在遷移一段內(nèi)容后,兩個API的響應都匹配。
  • 如果出現(xiàn)錯誤則停止。
  • 生成詳細日志以幫助診斷問題。
  • 發(fā)生錯誤后從正確的點重新啟動。

我們開始使用Ammonite,Ammonite允許您在Scala中編寫腳本,Scala是我們團隊的主要語言。這是一個很好的機會,可以嘗試我們之前沒有用過的東西,看看它對我們是否有用。雖然Ammonite允許我們使用熟悉的語言,但也有缺點。雖然Intellij現(xiàn)在支持Ammonite,但當時它沒有,這意味著我們失去了自動完成和自動導入。也不可能長時間運行Ammonite腳本。

最終,Ammonite不是正確的工具,我們使用sbt項目來執(zhí)行遷移。我們采用的方法允許我們使用我們自信的語言工作并執(zhí)行多次“測試遷移”,直到我們有信心在生產(chǎn)中運行它。

快進到2017年1月,是時候在我們的預生產(chǎn)環(huán)境CODE中測試完整的遷移了。

與我們的大多數(shù)系統(tǒng)類似,CODE和PROD之間***的相似之處是它們運行的​​應用程序的版本。支持CODE環(huán)境的AWS基礎架構遠沒有PROD那么強大,因為它的使用率要低得多。

在CODE上運行遷移將有助于我們:

  • 估計PROD上的遷移需要多長時間。
  • 評估遷移對性能的影響(如果有的話)。

為了準確衡量這些指標,我們必須匹配這兩個環(huán)境。這包括將PROD mongo數(shù)據(jù)庫的備份還原到CODE并更新AWS支持的基礎架構。

遷移超過200萬項內(nèi)容需要很長時間,當然比辦公時間更長。所以我們一夜之間在屏幕上運行腳本。

為了衡量遷移的進度,我們將結構化日志(使用標記)發(fā)送到ELK堆棧。從這里,我們可以創(chuàng)建詳細的儀表板,跟蹤成功遷移的文章數(shù)量,失敗次數(shù)和總體進度。此外,這些顯示在團隊附近的大屏幕上,以提供更大的可見性。

遷移完成后,我們采用相同的技術檢查Postgres匹配的Mongo中的每個文檔。

第三部分 - 代理和生產(chǎn)中的運行

現(xiàn)在,新的Postgres驅(qū)動的API正在運行,我們需要使用實際流量和數(shù)據(jù)訪問模式對其進行測試,以確保其可靠和穩(wěn)定。有兩種可能的方法可以實現(xiàn)這一點:更新與Mongo API通信的每個客戶端以與兩個API通信; 或運行代理,這樣做。我們使用Akka Streams在Scala中編寫了一個代理。

代理操作相當簡單:

  • 接受來自負載均衡器的流量。
  • 將流量轉(zhuǎn)發(fā)到主api并返回。
  • 異步將相同的流量轉(zhuǎn)發(fā)到輔助api。
  • 計算兩個響應之間的任何差異并記錄它們。

一開始,代理在兩個API的響應之間記錄了很多差異,在需要修復的API中出現(xiàn)了一些非常微妙但重要的行為差異。

結構化日志記錄

我們在Guardian上進行日志記錄的方式是使用ELK堆棧。使用Kibana使我們能夠靈活地以對我們最有用的方式顯示日志。Kibana使用相當容易學習的lucene查詢語法。但我們很快意識到,在當前設置中無法過濾掉日志或?qū)ζ溥M行分組。例如,我們無法過濾掉因GET請求而發(fā)送的日志。

我們的解決方案是向Kibana發(fā)送更多結構化日志,而不是僅發(fā)送消息。一個日志條目包含多個字段,例如時間戳,發(fā)送日志或堆棧的應用程序的名稱。以編程方式添加新字段非常簡單。這些結構化字段稱為標記,可以使用logstash-logback-encoder庫實現(xiàn)。對于每個請求,我們提取了有用的信息(例如路徑,方法,狀態(tài)代碼),并創(chuàng)建了一個包含我們記錄所需的附加信息的地圖??纯聪旅娴睦?。 

  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) 

我們?nèi)罩局械母郊咏Y構允許我們構建有用的儀表板并在我們的差異中添加更多上下文,這有助于我們識別API之間的一些較小的不一致。

復制流量和代理重構:

將內(nèi)容遷移到CODE數(shù)據(jù)庫后,我們最終得到了幾乎完全相同的PROD數(shù)據(jù)庫副本。主要區(qū)別是CODE沒有流量。為了將實際流量復制到CODE環(huán)境中,我們使用了一個名為GoReplay(gor)的開源工具。它的設置非常簡單,可根據(jù)您的要求進行定制。

由于進入我們的API的所有流量首先達到了代理,因此在代理服務器上安裝gor是有意義的。請參閱下文,了解如何在您的盒子上下載gor以及如何開始捕獲端口80上的流量并將其發(fā)送到另一臺服務器。 

  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 

一切都運行良好一段時間,但很快我們的代理幾分鐘時就遇到了生產(chǎn)中斷。經(jīng)過調(diào)查,我們發(fā)現(xiàn)代理運行的所有三個盒子同時循環(huán)。我們懷疑gor使用了太多資源并導致代理失敗。在進一步調(diào)查中,我們在AWS控制臺中發(fā)現(xiàn)這些盒子已經(jīng)定期循環(huán),但不是在同一時間。

在深入研究之前,我們試圖找到一種方法來繼續(xù)運行gor,但這次沒有對代理施加任何壓力。解決方案來自Composer的二級堆棧。該堆棧僅用于緊急情況,并且我們的生產(chǎn)監(jiān)控工具會不斷對其進行測試。將流量從此堆棧重新映射到CODE,速度加倍,這次沒有任何問題。

新發(fā)現(xiàn)提出了很多問題。該代理的構建可能沒有像其他應用程序那樣精心設計。此外,它是使用Akka Http構建的,之前沒有任何團隊成員使用過。代碼很混亂,并且快速修復。我們決定開始一項重大的重構工作,以提高可讀性,包括使用理解而不是我們之前增長的嵌套邏輯,并添加更多的日志記錄標記。

我們希望通過花時間了解一切是如何運作的,并通過簡化邏輯,我們能夠阻止騎行。但這沒效果。經(jīng)過大約兩個星期的嘗試使代理更可靠,我們開始覺得我們越來越深入兔子洞了。必須作出決定。我們同意冒險并留下風險,因為花在實際遷移上的時間比試圖修復一個月內(nèi)將會消失的軟件更好。我們通過再經(jīng)歷兩次生產(chǎn)中斷來驗證這個決定,每次中斷持續(xù)大約兩分鐘,但總體來說這是正確的做法。

快進到2017年3月,我們現(xiàn)在已經(jīng)完成了遷移CODE,對API的性能或CMS中的用戶體驗沒有任何不利影響。我們現(xiàn)在可以開始考慮在CODE中停用代理。

***階段是改變API的優(yōu)先級,以便代理首先與Postgres交談。如前所述,這是基于配置的。然而,有一個復雜性。

更新文檔后,Composer會在Kinesis流上發(fā)送消息。為了避免重復消息,只有一個API應該發(fā)送這些消息。API為此配置了一個標志; 對于Mongo支持的API,該值為true,對于Postgres支持的API,該值為false。簡單地更改代理與Postgres交談是不夠的,因為在請求到達Mongo之前,消息不會在Kinesis流上發(fā)送。這太遲了。

為了解決這個問題,我們創(chuàng)建了HTTP端點,以便瞬時更改負載均衡器中所有實例的內(nèi)存配置。這使我們能夠非??焖俚厍袚Q哪個API是主要的,而無需編輯配置文件并重新部署。此外,這可以編寫腳本,減少人為干預和錯誤。

現(xiàn)在所有的請求都是Postgres,而API2正在與Kinesis交談,可以通過配置和重新部署來***更改。

下一步是完全刪除代理,讓客戶單獨與Postgres API交談。由于有許多客戶,單獨更新每個客戶并不是真的可行。因此,我們將其推向了DNS。也就是說,我們在DNS中創(chuàng)建了一個CNAME,它首先指向代理的ELB,然后更改為指向API ELB。這允許進行單個更改,而不是更新API的每個單獨客戶端。

現(xiàn)在是遷移PROD的時候了。雖然有點可怕,因為它是生產(chǎn)。這個過程相對簡單,因為一切都基于配置。此外,當我們向日志添加舞臺標記時,還可以通過更新Kibana過濾器來重新調(diào)整先前構建的儀表板。

關閉代理和MongoDB

在10個月和240萬個遷移文章之后,我們終于可以關閉所有與Mongo相關的基礎設施。但首先,我們一直在等待的那一刻:殺死代理

這個小軟件給我們帶來了很多問題,我們迫不及待想要把它關掉!我們需要做的就是更新CNAME記錄以直接指向APIV2負載均衡器。

該團隊聚集在一臺電腦周圍。只需點擊一下即可切換開關。沒有人再呼吸了。完全沉默。點擊!而且改變了。什么都沒發(fā)生!我們都放松了。

出乎意料的是,刪除舊的MongoDB API是另一項挑戰(zhàn)。在瘋狂刪除舊代碼時,我們發(fā)現(xiàn)我們的集成測試從未更改為使用新API。一切都很快變紅了。幸運的是,大多數(shù)問題都與配置相關,因此很容易修復。但是測試捕獲的PostgreSQL查詢存在一些問題。為了避免這個錯誤,我們想到了我們可以做的事情,我們意識到,在開始大量工作時你也必須接受你會犯錯誤。

之后發(fā)生的一切都很順利。我們從OpsManager中分離了所有Mongo實例,然后終止它們。剩下要做的***事情就是慶祝。

責任編輯:龐桂玉 來源: JDON
相關推薦

2020-05-06 13:47:42

ZooKeeperKubernetes遷移

2018-03-14 09:49:35

數(shù)據(jù)庫遷移

2009-04-15 08:40:04

2017-10-20 08:45:15

數(shù)據(jù)庫MongoDBMySQL

2024-10-25 10:00:00

云服務計算

2010-07-20 09:48:33

2020-04-13 15:45:46

MySQL數(shù)據(jù)庫備份

2016-10-26 16:44:44

WatchfinderAWS云計算

2011-06-24 10:10:35

SVN

2020-09-16 14:56:11

MYSQL知識數(shù)據(jù)庫

2020-01-06 12:50:50

Windows 7遷移Windows 10

2009-04-16 17:23:37

OracleBasicFileSecureFile

2021-07-13 09:45:48

CentOSAlmaLinux命令

2010-09-29 11:06:21

活動目錄OpenLDAP

2020-12-08 10:01:48

DropboxNginxEnvoy

2013-06-21 13:49:08

MariaDB

2012-05-21 10:23:36

2022-10-08 09:33:00

平臺中間件

2015-06-17 13:26:41

FICOVMwareOpenStack

2020-04-20 08:08:23

MongoDBElasticsear數(shù)據(jù)庫
點贊
收藏

51CTO技術棧公眾號