選單

Uber 是如何安全快速地進行全球化部署的

作者 | Mathias Schwarz

譯者 | 王強

策劃 | Tina

本文要點

優步的基礎設施平臺讓數千名工程師能夠在不犧牲穩定性的前提下並行更改系統。

我們擴充套件了原有的系統,並隨著業務增長逐漸將抽象級別從單個主機提升到跨多個區域的眾多地區。

從日常運維工作中抽象出物理主機和區域,讓我們大大減少了在優步執行一系列無狀態服務時產生的摩擦。

部署流程不僅運作起來簡單、輕鬆,而且其自動化特性是整個無狀態基礎設施實現大規模自動化的關鍵所在。

統一到一個單一的託管控制平面後,我們極大提升了優步跨多個可用地域高效管理無狀態負載的能力。

在 QCon Plus 上,優步的軟體工程師 Mathias Schwarz 展示了優步如何 在全球級規模上安全、快速地部署。優步是一家大型企業,擁有多種產品。在大多數情況下,它們會被部署到全球數十或數百個市場。我們最大的產品是 Uber Rides——優步叫車產品,只需點選一個按鍵,它就能帶你從城裡的這個地方到達那個去處。每天,優步會完成 1800 萬個出行訂單——這還只是 2020 年第一季度的數字。除了優步叫車平臺上的出行服務外,優步還有用於送餐的 Uber Eats——優食。

Uber 是如何安全快速地進行全球化部署的

為了運營所有這些產品,優步擁有大量後端服務。大約有 4000 個不同的微服務部署在優步多個數據中心的機器上。

Uber 是如何安全快速地進行全球化部署的

在優步,我們每週要做 58,000 次構建,每週對生產環境進行 5,000 次更改。換句話說,優步的每一項後端服務平均每週要在生產環境中部署一次以上。

Uber 是如何安全快速地進行全球化部署的

由於執行服務升級過程需要一段時間,這也意味著系統每時每刻都至少會對我們的一項後端服務進行一些升級。

區域和地域

對於優步的基礎設施,我們會將它拆分成許多層來考慮。最底層是各個伺服器。伺服器是單獨的機器,是執行每個程序的硬體。伺服器的物理實體會放置在某個區域(zone)內。區域可以是我們自己擁有的,比如說優步的資料中心,也可以是雲區域,其中機器屬於 GCP 公有云或 AWS 的一部分。

一個區域永遠不會跨越多個提供者——它始終只有一個提供者,且一組區域組成一個地區(region)。地區本質上是物理上彼此靠近的一些區域的集合,因此這些區域內的程序之間的呼叫延遲較低,這意味著你可以期望從一個區域到另一個區域的請求具有較低的延遲。這些地區共同構成了我們的全球基礎設施。因此,當你要將新構建部署到生產環境時,基本上就是將這些新構建全域性部署到優步基礎設施所有區域中的所有相關伺服器的過程。

早期:非結構化部署

當優步開始構建他們的部署策略和部署系統時,一開始的做法與其他大多數公司是類似的。優步的每個服務團隊都有一組特定主機,他們會在其中部署新構建。然後,每當他們想要釋出更改時,都會手動訪問這些伺服器,或者使用 Jenkins 指令碼將構建部署到伺服器,並確保他們已經升級了所有程序,然後再發布新構建。然而這種方法有幾個缺點。例如,當伺服器出現故障時,團隊需要手動清理。更糟糕的是,如果正在推出的更改中存在錯誤,就意味著團隊必須在將錯誤的更改從生產系統中移除後做一番清理,讓系統恢復到良好狀態。

Uber 是如何安全快速地進行全球化部署的

重要的部署系統特性

2014 年,我們退後一步,開始思考如何建立一個部署系統來將所有這些操作自動化,讓我們的工程師更容易保持高頻率部署節奏,同時確保安全性。我們提出了一系列希望系統能夠完成的要求。我們希望我們的構建保持一致,此外還有:

希望構建看起來都是一樣的,無論它們使用的是什麼語言、什麼框架,以及構建服務的是哪個團隊。構建應該與部署系統是一致的,這樣管理起來更方便。

此外,我們希望所有部署都具有零停機時間,這意味著當你想要部署你的構建時,你希望系統能夠自動管理伺服器的部署順序。我們希望系統在不干擾進入服務的流量的情況下,儘量不要停止更多的程序。

想要讓預防停機措施成為這個系統的一等公民。從本質上講,我們希望系統能夠在我們將新版本部署到生產環境時及時發現並響應可能存在的問題。

最後,我們希望系統能夠讓我們的後端恢復到良好狀態。總體而言,我們希望工程師能夠輕鬆部署新的更改,並能信任系統,讓系統來處理這些部署的安全性。

使用 uDeploy 進行結構化部署

基於這些需求,優步開始構建 Micro Deploy 系統。Micro Deploy 於 2014 年上線。在那一年,我們將所有後端服務轉移到了這個新平臺。在 Micro Deploy 中,我們將所有構建都設為 Docker 映象。我們還搭配使用了內部構建的,稱為 Makisu 的構建系統來做到這一點。從本質上講,這兩個系統結合在一起意味著我們所有的 Docker 映象看起來都是一樣的,並且在部署的系統中有著相同的表現,從而顯著簡化了部署管理。

Uber 是如何安全快速地進行全球化部署的

部署到區域中的叢集

在優步,我們還改變了工程師的抽象級別。我們告訴他們,他們無需擔心具體要部署到哪些伺服器上,而只要告訴我們他們需要哪些區域以及每個區域中他們想要的容量即可。也就是說,我們並不要求工程師找到特定的伺服器,而是給這些區域提供容量。然後我們會部署到目標區域內。每當出現伺服器故障時,我們都會更換它,並且服務將被轉移到這些新伺服器上,全過程無需任何人工干預。我們在 uDeploy 中做到了這一點,結合了名為 Mesos 的開源叢集管理系統,以及我們在優步內部構建的,名為 Peloton 的無狀態負載排程程式(後者已開源)。今天,你可以使用 Kubernetes 實現類似的目標。

安全 - 監控指標

我們還決定將安全機制直接構建到已部署的平臺中,以儘可能提升部署的安全性。我們在已部署平臺中內建了我們的監控系統,uMonitor。我們所有的服務都會發出由 uMonitor 攝取的指標。uMonitor 按時間序列持續監控這些指標,並確保指標不會超出某些預定義的閾值。如果我們看到資料庫指標突破了這些預定義的閾值,就會開始回滾到安全狀態,這一操作將在 Micro Deploy 系統中自動執行。Micro Deploy 會捕獲系統之前的狀態,然後在啟動回滾時自動將服務恢復到其舊狀態。

Uber 是如何安全快速地進行全球化部署的

安全 - 白盒整合

此外,對於優步最重要的那些服務,我們還進行了 白盒整合測試。我們使用了內部開發的,稱為 Hailstorm 的系統。當你將第一個例項部署到一個新區域時,它將在生產環境中對這些特定例項執行負載測試,並執行白盒整合和負載測試。這種測試是在釋出程式碼之前執行的大量整合測試之外的一種補充。

Uber 是如何安全快速地進行全球化部署的

這些整合測試會針對已部署服務的 API 端點,並確保 API 仍按我們預期的方式執行。在部署到某個區域的前幾個例項上執行此操作後,我們就可以在生產環境中的問題影響多個主機之前發現它們。如果其中一些測試失敗,我們還可以回滾到服務的先前已知的安全狀態。

安全 - 連續黑盒

最後,我們構建了所謂的 黑盒測試。黑盒測試本質上是在優步產品所在的所有城市不斷髮生的虛擬出行訂單。黑盒測試會執行這些虛擬出行訂單,如果我們發現某個城市訂單無法完成,就會向一位工程師彈出一個頁面。然後,這位工程師必須手動決定是回滾還是繼續部署。他們還必須找出是哪些服務可能導致平臺上的出行訂單突然開始出現問題。所以黑盒測試是我們問題檢測機制的最後一道防線。

Uber 是如何安全快速地進行全球化部署的

Micro Deploy 為我們提供了大規模的安全性保障。即便個別伺服器出現故障,它也能為我們提供服務的可用性。幾年前,我們發現我們花費了越來越多的工程時間來管理服務。工程師仍然必須弄清楚在他們應該在哪些區域放置服務。例如,他們是要在 AWS 還是在我們自己的資料中心上提供服務呢?他們需要多少個例項?等等。兩年前,服務管理仍然是一項需要大量人工干預的任務。

提升規模效率

因此,我們又一次退後一步開始思考,如何構建一個能夠為我們的工程師自動完成所有這些日常任務,並確保平臺能夠自我管理的系統?

我們提出了三個要構建到系統中的原則:

首先,我們希望它是真正的多雲架構,這意味著無論服務執行我們自己的資料中心還是某個公有云的主機或伺服器上,對工程師來說都應該是一樣的,沒有什麼區別。我們應該能夠毫不費力地在任何地方進行部署。

其次,我們還希望它是全託管的,這意味著我們希望工程師只需要作出更改,確保這些更改有效,並將它們推到生產環境中就完事。我們不再指望他們來處理在區域中定位、擴充套件服務之類的人工管理任務。同時,我們仍然希望部署系統行為是可預測的。

最後,我們仍然希望工程師能夠了解和預測他們的服務會發生什麼事情。因此,即使我們決定更改縮放比例或將它們移動到雲區域,我們也想告訴工程師到底發生了什麼事情,以及為什麼會這樣做。

Up 提升基礎設施效率

基於這三個原則,我們開始在優步構建我們使用至今的部署平臺,名為 Up。在 Up 中,工程師在管理他們的服務並部署更改時需要考慮的抽象級別又進了一步。例如,我們並不會要求他們關心具體的區域,而是詢問他們要部署到哪個物理地區。這樣,使用 Up 的工程師只需指明他希望服務部署到哪個地區,然後 Up 就會負責其餘的工作。對於我們今天的工程師來說,這套流程是下面這個樣子。

Uber 是如何安全快速地進行全球化部署的

我們可以看到,這個服務被部署到一個金絲雀中,並被部署到了兩個不同的地區,在本例中它們分別稱為“DCA”和“PHX”。我們不會告訴工程師物理伺服器是在雲區域中執行還是在我們自己的資料中心中執行。我們只是告訴他們有這兩個地區,在這兩個地區中一共有多少例項。

當工程師對生產環境進行部署時,如果系統決定對服務進行更改,他們就會看到這樣的計劃。該計劃列出了已經執行的步驟,因此你可以看到服務到目前為止具體發生了什麼事情。其次,它顯示了當前正在發生的事情。例如我們目前正在為該服務升級哪個區域,以及我們的具體升級進度。最後,在應用當前更改之後,會有一個稍後將應用的更改列表——這意味著工程師完全可以預測在整個部署過程中會發生什麼事情。

讓我們新增一個新區域

我們希望 Up 系統做到的一件事,是讓我們的後端工程師不用再關心基礎設施,特別是底層基礎設施的拓撲。具體來說,我們希望新增或刪除區域的操作不會影響工程師。如果這裡有一個地區,並且我正在向現有地區新增一個新區域,如下圖所示。

Uber 是如何安全快速地進行全球化部署的

基礎架構團隊將設定物理伺服器,設定儲存容量,並將區域物理連線到現有基礎架構。現在的挑戰是讓 4000 個服務所有者和 4000 個服務,或者至少其中的一部分轉移到新區域,以使用我們在該區域所有的新容量。在 Up 面世之前,需要數十名工程師才能完成一個新的區域部署工作,整個過程需要耗費大量人力和時間,所以我們希望 Up 為我們自動化這一步驟。

宣告式配置

假設我們有一個如前所述的新區域;然後,工程師將僅根據地區及其在地球上的物理位置配置他們的容量。他們會告訴我們,他們想要 DCA 地區中部署 250 個例項,在 PHX 地區中部署 250 個例項。此外,他們可以告訴部署系統一些關於它們對其他服務的依賴關係的基本資訊,以及他們是否想為這些服務使用金絲雀。然後,Up 負責檢視這一配置並不斷評估服務的當前位置和當前配置是否適合各個服務。Up 將不斷地將基礎設施的當前拓撲與這些宣告式服務配置進行比較,並找出如何以最佳方式放置此服務。

Uber 是如何安全快速地進行全球化部署的

有了這個配置和持續的評估迴圈,當我們新增一個新區域時,系統會發生什麼事情呢?首先,Up 系統會自動發現更好的部署位置;比如說對於某些特定服務,可能有一個新區域可用,其容量比現有區域大得多。然後在評估這些限制並確定有更好的位置之後,我們還有一個 Balancer,負責啟動從該地區內的一個區域到另一個區域的遷移任務。工程師不再需要花時間手動移動這些服務,因為我們的 Balancer 會自動為他們完成這項工作。

總 結

本文介紹了我們從由工程師來管理各個伺服器的小規模系統,到可以自動管理伺服器的,基於區域抽象的 Micro Deploy 系統的歷程。彼時,整體來說服務管理仍然是我們工程師日常需要維護的一項任務。最後,到我們的新 Up 系統面世後,我們在區域層面實現了完全自動化。你可以安全地每週向生產環境部署 5,000 次更改,並且你可以輕鬆管理像優步後端這麼巨大規模的系統。讓它在實踐中發揮作用的關鍵是自動化。它的抽象級別讓你可以自動執行很多原本需要工程師手動管理的任務。這意味著無論是部署位置、主機提供商選擇以及服務的擴充套件方面,我們都可以將工作完全交給機器。

作者介紹

Mathias Schwarz 在優步擔任基礎設施工程師已超過 5 年。他和他的團隊負責開發被整個優步工程團隊所使用的無狀態服務部署平臺。Mathias 擁有奧胡斯大學程式語言組的計算機科學博士學位。

https://www。infoq。com/articles/uber-deployment-planet-scale/