在此博客文章中,我們將向您介紹在全球200多個邊緣城市中運行的服務的可靠性模型。然后,我們將探討如何部署新的動態(tài)任務調度系統(tǒng)HashiCorp Nomad,幫助我們改善每個數(shù)據(jù)中心的服務可用性,涵蓋了我們如何部署Nomad以及克服的挑戰(zhàn)。最后,我們將向您展示我們目前如何使用Nomad以及將來如何計劃使用Nomad。
每個數(shù)據(jù)中心中運行的服務的可靠性模型
對于此博客文章,我們將區(qū)分每個數(shù)據(jù)中心中運行的兩種不同類別的服務:
面向客戶的服務:客戶使用的所有產(chǎn)品堆棧,例如緩存,WAF,DDoS保護,速率限制,負載平衡等。
管理服務:操作數(shù)據(jù)中心所需的軟件,該軟件不在客戶流量的直接請求路徑中。
面向客戶的服務
我們面向客戶的服務的可靠性模型是在每個數(shù)據(jù)中心的所有計算機上運行它們。這非常有效,因為它允許通過添加更多計算機來動態(tài)擴展每個數(shù)據(jù)中心的容量。
多虧了我們在每臺機器上運行的動態(tài)負載平衡系統(tǒng)Unimog,使擴展變得特別容易。它的作用是根據(jù)當前資源的使用情況不斷地重新平衡流量,并檢查服務的運行狀況。這有助于為單個計算機故障提供彈性,并確保所有計算機上的資源使用率幾乎相同。
例如,這是我們一個數(shù)據(jù)中心一天中的CPU使用情況,其中每個時間序列代表一臺機器,不同的顏色代表不同代的硬件。Unimog使所有計算機處理流量并保持大致相同的CPU利用率。
管理服務
我們的一些大型數(shù)據(jù)中心擁有大量計算機,但有時我們需要在每個位置僅可靠地運行一個或幾個管理服務實例。
當前有兩種方法可以做到這一點,每種方法各有利弊:
將服務部署到數(shù)據(jù)中心中的所有計算機:
專業(yè)版:確保服務的可靠性
缺點:它不必要地使用了本可以用于服務客戶流量且不具有成本效益的資源
將服務部署到每個數(shù)據(jù)中心中的少數(shù)幾臺機器上:
優(yōu)點:更少浪費資源,更具成本效益
缺點:當少數(shù)機器意外故障時,它冒著服務不可用的風險
第三種更可行的選擇是使用動態(tài)任務調度,以便在確??煽啃缘耐瑫r僅使用適當數(shù)量的資源。
需要更動態(tài)的任務調度
對于我們希望在每個數(shù)據(jù)中心中運行的管理服務,必須在兩個次優(yōu)的可靠性模型選項之間進行選擇,這并不理想。
確實,其中一些服務即使不在請求路徑中,也需要繼續(xù)運行數(shù)據(jù)中心。如果運行這些服務的計算機不可用,在某些情況下,我們必須在恢復它們時臨時禁用數(shù)據(jù)中心。這樣做會自動將用戶重新路由到下一個可用的數(shù)據(jù)中心,并且不會造成中斷。實際上,整個Cloudflare網(wǎng)絡都設計為在禁用數(shù)據(jù)中心并自動恢復的狀態(tài)下運行。但是最好將最終用戶路由到他們附近的數(shù)據(jù)中心,以便我們希望最大程度地減少任何數(shù)據(jù)中心級別的停機時間。
這使我們意識到,我們需要一個系統(tǒng)來確保每個數(shù)據(jù)中心中正在運行一定數(shù)量的服務實例,而不管最終由哪個物理機運行該服務。
面向客戶的服務在每個數(shù)據(jù)中心的所有計算機上運行,并且不需要加入該新系統(tǒng)。另一方面,當前在固定子集的機器上運行的服務具有次優(yōu)的可靠性保證,不需要在所有機器上運行的服務都是上線的理想選擇。
我們的選擇:HashiCorp Nomad
根據(jù)我們的一系列要求,我們對候選解決方案進行了一些研究。
雖然Kubernetes是另一個選擇,但出于以下原因,我們決定使用HashiCorp的Nomad:
滿足我們的最初要求,該要求是可靠地運行每個數(shù)據(jù)中心中資源隔離的二進制實例。
幾乎沒有依賴關系,并且可以直接與Consul集成。Consul是我們已經(jīng)在每個數(shù)據(jù)中心中部署的另一套HashiCorp軟件。它提供了分布式鍵值存儲和服務發(fā)現(xiàn)功能。
輕量級(單個Go二進制文件),易于部署和配置新集群,這在部署與我們數(shù)據(jù)中心一樣多的集群時是一個加號。
具有模塊化的任務驅動程序(負責執(zhí)行任務和提供資源隔離的部分)體系結構,不僅支持容器,而且還支持二進制文件和任何自定義任務驅動程序。
是開源的,用Go編寫。我們在團隊中擁有Go語言方面的經(jīng)驗,Nomad在GitHub上擁有一個響應迅速的維護者社區(qū)。
部署架構
游牧民族分為兩個不同的部分:
Nomad Server:組成負責調度的群集的實例,每個數(shù)據(jù)中心五個實例以提供足夠的容錯能力
Nomad Client:執(zhí)行實際任務的實例,在每個數(shù)據(jù)中心的所有計算機上運行
為了保證Nomad Server群集的可靠性,我們在屬于不同故障域的計算機上部署了實例:
在不同的相互連接的物理數(shù)據(jù)中心中形成一個位置
在不同的機架中,連接到不同的交換機
在不同的多節(jié)點機箱中(我們大多數(shù)的邊緣硬件以多節(jié)點機箱的形式出現(xiàn),一個機箱包含四個單獨的服務器)
我們還向配置管理工具中添加了邏輯,以確保無論每天發(fā)生的服務器擴展和停用情況如何,我們始終保持一致數(shù)量的Nomad Server實例。
邏輯很簡單,隨著服務器的擴展和停用,Nomad Server角色將重新分配到新的計算機列表中。然后,我們的配置管理工具可確保Nomad Server在新計算機上運行,然后再關閉舊計算機。
此外,由于服務器的擴展和退役會一次影響一部分機架,而Nomad Server角色分配邏輯可提供機架多樣性保證,因此群集始終保持仲裁狀態(tài),因此可以保持健康。
作業(yè)文件
Nomad作業(yè)文件被模板化并檢入git存儲庫。然后,我們的配置管理工具可確保在每個數(shù)據(jù)中心中安排作業(yè)。從那里開始,Nomad接管并確保作業(yè)始終在每個數(shù)據(jù)中心中運行。
通過向每個Nomad Client公開機架元數(shù)據(jù),我們可以確保特定服務的每個實例在不同的機架中運行并綁定到不同的故障域。這樣,我們可以確保一個服務器機架的故障不會影響服務運行狀況,因為該服務也在另一個機架中運行,不受故障影響。
我們通過以下作業(yè)文件約束來實現(xiàn)這一目標:
constraint { attribute = "${meta.rack}" operator = "distinct_property"}
服務發(fā)現(xiàn)
我們利用Nomad與Consul的集成來將Nomad作業(yè)動態(tài)添加到Consul服務目錄中。這使我們可以通過查詢Consul來發(fā)現(xiàn)每個數(shù)據(jù)中心中當前正在運行特定服務的位置。此外,啟用Consul DNS接口后,我們還可以使用基于DNS的查找來定位Nomad上運行的服務。
可觀察性
為了能夠正常運行與我們數(shù)據(jù)中心一樣多的Nomad群集,對Nomad群集和在這些群集上運行的服務的良好可觀察性至關重要。
我們使用Prometheus來抓取在每個數(shù)據(jù)中心中運行的Nomad Server和Client實例,并使用Alertmanager來提醒關鍵指標。使用Prometheus指標,我們構建了Grafana儀表板以提供每個集群的可見性。
我們通過查詢領事服務目錄并使用以下Prometheus配置定期抓取其指標來設置Prometheus實例以發(fā)現(xiàn)在Nomad上運行的服務:
- consul_sd_configs: - server: localhost:8500 job_name: management_service_via_consul relabel_configs: - action: keep regex: management-service source_labels: - __meta_consul_service
然后,我們使用這些指標來創(chuàng)建Grafana儀表板并為在Nomad上運行的服務設置警報。
為了限制對Nomad API端點的訪問,我們啟用了雙向TLS身份驗證,并正在為與Nomad進行交互的每個實體生成客戶端證書。這樣,只有具有有效客戶端證書的實體才能與Nomad API端點進行交互,以便安排作業(yè)或執(zhí)行任何CLI操作。
挑戰(zhàn)性
部署新組件總是伴隨著一系列挑戰(zhàn)。這是我們在此過程中必須克服的一些障礙的列表。
Initramfs rootfs和ivot_root
當開始使用exec
驅動程序運行在chroot
環(huán)境中隔離的二進制文件時,我們注意到不支持在initramfs上運行的無狀態(tài)根分區(qū),因為該任務無法啟動,并且在日志中顯示了此錯誤消息:
Feb 12 19:49:03 machine nomad-client[258433]: 2020-02-12T19:49:03.332Z [ERROR] client.alloc_runner.task_runner: running driver failed: alloc_id=fa202-63b-33f-924-42cbd5 task=server error="failed to launch command with executor: rpc error: code = Unknown desc = container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:109: jailing process inside rootfs caused \\\"pivot_root invalid argument\\\"\"""
我們提交了一個GitHub問題,并提交了一個變通解決方案拉取請求,該請求被迅速審查并合并到上游。
同時,為了獲得最大的隔離安全性,我們pivot_root
通過修改啟動過程來啟用設置,并且其他團隊成員開發(fā)并提出了內核郵件列表的補丁程序,以使其在將來變得更容易。
資源使用情況遏制
一個非常重要的方面是確保在Nomad上運行的任務的資源使用不會破壞同一臺計算機上共存的其他服務。
磁盤空間是每臺計算機上的共享資源,必須為Nomad設置配額。我們通過將Nomad數(shù)據(jù)目錄隔離到每臺計算機上的專用固定大小的安裝點來實現(xiàn)此目的。但是,Nomad目前不支持限制磁盤帶寬和IOPS。
Nomad作業(yè)文件的資源部分可以限制內存和CPU的使用(內存以MB為單位,cpu以MHz為單位):
resources { memory = 2000 cpu = 500}
這在后臺使用了cgroups,我們的測試表明,雖然可以像預期那樣強制執(zhí)行內存限制,但是只要主機上有可用的CPU,CPU限制就是軟限制并且不強制執(zhí)行。
工作量(不可預測)
如上所述,所有計算機當前都運行相同的面向客戶的工作負載。動態(tài)調度各個作業(yè)與游牧到單機器上運行挑戰(zhàn)假設。
雖然我們的動態(tài)負載平衡系統(tǒng)Unimog會根據(jù)資源使用情況來平衡請求,以確保所有計算機上的請求都幾乎相同,但是具有大量資源使用情況的批處理類型作業(yè)可能會帶來挑戰(zhàn)。
隨著我們提供更多服務,我們將對此予以關注:
嘗試通過上述限制限制游牧民族工作的資源利用率
確保Unimog適應此批處理類型的工作量,并且不會出現(xiàn)正反饋循環(huán)
我們在Nomad上運行的是什么
現(xiàn)在,Nomad已部署在每個數(shù)據(jù)中心,我們能夠通過逐步啟用這些服務來提高對運營必不可少的管理服務的可靠性。我們邁出了第一步,加入了重啟和維護管理服務。
重新啟動和維護管理服務
在每個數(shù)據(jù)中心,我們都運行一項服務,該服務可促進在線無人值守的滾動重啟和機器維護。該服務曾經(jīng)在每個數(shù)據(jù)中心的一臺知名機器上運行。這使其容易受到單機故障的影響,并且在停機時阻止了計算機在重新啟動后自動啟用。因此,有人將onboarded游牧,以提高其可靠性,良好的第一服務。
現(xiàn)在,我們保證此服務始終在每個數(shù)據(jù)中心中運行,而不管單個計算機是否發(fā)生故障。他們現(xiàn)在不再查詢依賴于知名地址的其他機器來定位此服務,而是查詢Consul DNS并動態(tài)確定服務在何處與之交互。