1. 程式人生 > >因Kubernetes漏洞導致銀行應用癱瘓1小時,事件細節回顧和分析

因Kubernetes漏洞導致銀行應用癱瘓1小時,事件細節回顧和分析

Kubernetes

大家好,我是Monzo的技術主管,應我週五承諾,我準備分享關於這次事故的更多的內部資訊。因為這個問題的本質牽涉技術問題,這篇文章也會略顯技術性。

值得注意的是,我們上週有兩個大的事故,而且很多使用者受到影響(再次抱歉)。第一個事故持續將近一週時間,隻影響了我們的預付費產品-ie,Monzo Alpha和Beta卡。第二個事故在週五上午持續影響1.5個小時,這次不僅影響了我們的預付費產品,而且還影響了我們的現金賬戶。這篇文章主要介紹的是後者。

通過我去年釋出的這些部落格(https://monzo.com/blog/2016/09/19/building-a-modern-bank-backend/),你可以更深入的瞭解下我們整體的後臺架構設計,但是更重要的是要了解下我們技術棧中下面幾個元件扮演的角色,這樣才能更深度的瞭解這篇文章。

  • Kubernetes是我們所有基礎架構的管理部署系統。Monzo的後端是上百個微服務、已經打包的Docker容器。Kubernetes是這些容器的管理者,確保他們能夠正常執行在我們的AWS節點。
  • etcd是一個分散式資料庫,儲存有關部署哪些服務,它們在哪裡執行以及它們處於什麼狀態的資訊,這些資料提供給Kubernetes。Kubernetes需要穩定的連線到etcd才能正常的工作。如果etcd停止執行,我們所有的服務雖然繼續可以執行,但是他們無法升級或者縮容、擴容等。
  • linkrd是我們用來管理後端服務通訊連線的一個軟體。在像我們的系統中,每秒鐘發生數以千計的網路連線,linkrd扮演了這些連線任務的路由和負載的角色。為了知道路由從何處發起,他還依賴於能夠收到Kubernetes更新服務位於何處。

時間軸

兩週前:平臺研發團隊對我們的etcd叢集做了變更,升級了一個新的版本,對叢集做了擴容。以前,這個叢集只包含了三個節點(每一個zone中一個節點),這次我們提升為9個節點(每個zone中三個節點分佈)。因為etcd的依賴能夠達到分散式的Quorum ,這意味著在這個設定我們可以容忍同時失去整個zone再加上另一個zone中的單個node。

這次是按計劃升級,並沒有設計安排任何停機時間。我們可以確認這個叢集是正確的,但是很重要的是這裡觸發了另一個系統的錯誤。

一天前:我們的一個團隊對於目前的賬戶開發了一個新的功能,在生產環境部署了一個新的介面,但是注意到他正在經歷的問題。作為防範措施,他們將服務縮減為沒有執行副本,但Kubernetes services仍然存在。

14:10:工程師部署變更服務需要處理當前的支付賬戶。 這種做法並非罕見,我們的工程師也經常做這樣的事情:為了儘量減少變化的風險,我們以更小的粒度,更頻繁的節奏,使用一個可重複的,明確定義的過程來發布這些功能。 但是當服務部署完成時,所有對它的請求都開始失敗。 此時開始我們站點當前的賬戶開始支付失敗。 而與此同時,預付卡不受影響,因為它不使用失敗的的服務。

14:12:我們回滾了釋出的應用。這也是釋出失敗標準的操作流程,當介面被改變,他們應該通過回滾操作確保向前相容。然而,這種情況下即使是回滾操作,錯誤依舊存在,支付仍然不能成功。

14:16:我們立即宣佈內部故障。團隊成員開始召集,以確定問題的影響,並開始除錯。

14:18:工程師確定linkerd似乎處於不健康的狀態,並試圖使用一個內部工具來識別出現問題的單個節點並重新啟動它們。如前所述,linkerd是一個我們用來管理後端服務之間通訊的系統。 要知道傳送特定請求的位置,需要從請求中獲取一個邏輯名稱,如service.foo,並將其轉換為IP地址/埠。 在這種情況下,linkerd沒有收到Kubernetes關於新的pods13執行在網路上的更新。 因此,它試圖將請求路由到不再對應於正在執行的程序的IP地址。

14:26:我們認為,最好的方法是重啟後端的所有linkerd例項,其中有幾百個,假設它們都遇到同樣的問題。 當我們遇到問題時,許多工程師正試圖通過啟用旨在提供備份的內部流程來最小化客戶對付款或接收銀行轉帳的影響。 這意味著儘管持續不穩定,大多數客戶仍然能夠成功使用他們的卡。

14:37:替換linkerd無法啟動,因為執行在我們每個節點上的Kubelet無法從Kubernetes apiservers檢索適當的配置。 此時,我們懷疑Kubernetes或etcd有其他問題,並重新啟動三個apiserver程序。 完成後,替換linkerd例項將能夠成功啟動。

15:13:所有linkerd pod都重新啟動,但每秒處理數千個請求的服務現在沒有收到任何流量。 此時,客戶完全無法重新整理其Monzo應用程式中的Feed或餘額,而我們的內部COps(“客戶操作”)工具停止工作。 現在這個問題已經升級為全面的平臺停機,沒有任何服務能夠滿足要求。 正如您可以想像的,幾乎所有的自動警報都已經觸發。

15:27:我們注意到,linkerd在嘗試解析來自Kubernetes apiserver的服務發現響應時正在記錄NullPointerException(http://t.cn/Rl086mW)。 我們發現這是我們正在執行的Kubernetes和Linkerd版本之間的不相容,特別是沒有解析空服務。因為linkerd更新版本已經在我們的暫存環境中測試了幾個星期,其中包含了對不相容性的修復,平臺團隊的工程師開始部署新版本的linkerd以試圖向前滾動。

15:31:在檢查程式碼更改之後,工程師意識到他們可以通過刪除不包含endpoints的Kubernetes服務(即前面提到的服務作為預防措施縮小到0個副本)來防止解析錯誤。他們刪除違規服務和連結器 能夠成功載入服務發現資訊。 此時,平臺恢復正常,流量開始在服務之間正常轉移,付款開始重新開始工作。 事件結束!

根源

在這一點上,雖然我們把系統重新恢復上線,但我們還不瞭解問題的根本原因。 由於部署頻率和對節點和應用程式故障的自動響應,網路在後端非常動態,所以能夠信任我們的部署和請求路由子系統是非常重要的。之後,我們在Kubernetes和etcd客戶端中發現了一個bug(https://github.com/kubernetes/kubernetes/issues/47131),在我們之前一週執行的那種叢集重新配置之後,會導致請求超時。 由於這些超時,部署服務時,linkerd無法從Kubernetes接收關於網路上可以找到的更新。 雖然是善意的,重啟所有linkerd例項是一個不幸和糟糕的決定,加劇了中斷的影響,因為它暴露了我們部署的軟體版本之間不同的不相容性(https://github.com/linkerd/linkerd/issues/1219)。

總結

分散式系統中的大規模失敗可能是很難理解的,善意的人為行為有時會使問題複雜化,就像這裡所發生的那樣。 當這樣的事情發生時,我們希望儘可能地從事件中學習,以確保它不會重新出現。 我們已經確定了我們將在短期內採取的幾個步驟:

  1. 修復Kubernetes中的錯誤,可以在叢集重新配置後觸發超時。
  2. 推出修復解析錯誤的新版本的linkerd。
  3. 為受影響的元件建立更好的健康檢查,儀表板和警報,以更清晰地顯示有關錯誤的訊號,並防止出現人為錯誤。
  4. 改進我們的程式,以確保我們儘可能清楚,快速地在內部和外部傳達中斷。

我想向大家保證,我們非常認真地對待這件事。 這是我們歷史上發生的最嚴重的技術事件之一,我們的目標是經營一家客戶可以依賴的銀行。 真的很抱歉,我們知道我們讓您失望。 我希望這個事件分析能清楚地說明發生了什麼,以及我們正在做什麼來確保未來不會再發生。 我會確保我們釋出類似的事情來處理這種嚴重的事件:如果我是一個客戶,我想知道。而且我個人覺得這個帖子很有趣,可以作為對生產系統的深入瞭解。 如果您有任何問題,請告訴我。

原文連結:https://community.monzo.com/t/resolved-current-account-payments-may-fail-major-outage/26296/95

原文來自微信公眾號:Docker